mirror of
https://github.com/StanGirard/quivr.git
synced 2024-09-11 21:17:36 +03:00
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:
parent
e6e5099d6b
commit
1d7bc8a5bc
188
frontend/.eslintrc.js
Normal file
188
frontend/.eslintrc.js
Normal 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,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
4
frontend/.lintstagedrc.js
Normal file
4
frontend/.lintstagedrc.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
module.exports = {
|
||||||
|
"*": "prettier --ignore-unknown --write",
|
||||||
|
"*.{js,ts, tsx}": "pnpm lint-fix",
|
||||||
|
};
|
@ -1,6 +1,8 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
import { useSupabase } from "@/app/supabase-provider";
|
import { useSupabase } from "@/app/supabase-provider";
|
||||||
import { useToast } from "@/lib/hooks/useToast";
|
import { useToast } from "@/lib/hooks/useToast";
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
export const useGoogleLogin = () => {
|
export const useGoogleLogin = () => {
|
||||||
const { supabase } = useSupabase();
|
const { supabase } = useSupabase();
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable */
|
||||||
import Button from "@/lib/components/ui/Button";
|
import Button from "@/lib/components/ui/Button";
|
||||||
|
|
||||||
import { useGoogleLogin } from "./hooks/useGoogleLogin";
|
import { useGoogleLogin } from "./hooks/useGoogleLogin";
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
|
/* eslint-disable */
|
||||||
"use client";
|
"use client";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
import { useSupabase } from "@/app/supabase-provider";
|
import { useSupabase } from "@/app/supabase-provider";
|
||||||
import Button from "@/lib/components/ui/Button";
|
import Button from "@/lib/components/ui/Button";
|
||||||
import { useToast } from "@/lib/hooks/useToast";
|
import { useToast } from "@/lib/hooks/useToast";
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
type MaginLinkLoginProps = {
|
type MaginLinkLoginProps = {
|
||||||
email: string;
|
email: string;
|
||||||
@ -21,6 +23,7 @@ export const MagicLinkLogin = ({ email, setEmail }: MaginLinkLoginProps) => {
|
|||||||
variant: "danger",
|
variant: "danger",
|
||||||
text: "Please enter your email address",
|
text: "Please enter your email address",
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
|
/* eslint-disable */
|
||||||
"use client";
|
"use client";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { redirect } from "next/navigation";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
import { useSupabase } from "@/app/supabase-provider";
|
import { useSupabase } from "@/app/supabase-provider";
|
||||||
import Button from "@/lib/components/ui/Button";
|
import Button from "@/lib/components/ui/Button";
|
||||||
import Card from "@/lib/components/ui/Card";
|
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 Field from "@/lib/components/ui/Field";
|
||||||
import PageHeading from "@/lib/components/ui/PageHeading";
|
import PageHeading from "@/lib/components/ui/PageHeading";
|
||||||
import { useToast } from "@/lib/hooks/useToast";
|
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 { GoogleLoginButton } from "./components/GoogleLogin";
|
||||||
import { MagicLinkLogin } from "./components/MagicLinkLogin";
|
import { MagicLinkLogin } from "./components/MagicLinkLogin";
|
||||||
|
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
|
/* eslint-disable */
|
||||||
"use client";
|
"use client";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
import { useSupabase } from "@/app/supabase-provider";
|
import { useSupabase } from "@/app/supabase-provider";
|
||||||
import Button from "@/lib/components/ui/Button";
|
import Button from "@/lib/components/ui/Button";
|
||||||
import Card from "@/lib/components/ui/Card";
|
import Card from "@/lib/components/ui/Card";
|
||||||
import PageHeading from "@/lib/components/ui/PageHeading";
|
import PageHeading from "@/lib/components/ui/PageHeading";
|
||||||
import { useToast } from "@/lib/hooks/useToast";
|
import { useToast } from "@/lib/hooks/useToast";
|
||||||
import Link from "next/link";
|
|
||||||
import { useRouter } from "next/navigation";
|
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
export default function Logout() {
|
export default function Logout() {
|
||||||
const { supabase } = useSupabase();
|
const { supabase } = useSupabase();
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
|
/* eslint-disable */
|
||||||
"use client";
|
"use client";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
import { useSupabase } from "@/app/supabase-provider";
|
import { useSupabase } from "@/app/supabase-provider";
|
||||||
import Button from "@/lib/components/ui/Button";
|
import Button from "@/lib/components/ui/Button";
|
||||||
import Card from "@/lib/components/ui/Card";
|
import Card from "@/lib/components/ui/Card";
|
||||||
import Field from "@/lib/components/ui/Field";
|
import Field from "@/lib/components/ui/Field";
|
||||||
import PageHeading from "@/lib/components/ui/PageHeading";
|
import PageHeading from "@/lib/components/ui/PageHeading";
|
||||||
import { useToast } from "@/lib/hooks/useToast";
|
import { useToast } from "@/lib/hooks/useToast";
|
||||||
import Link from "next/link";
|
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
export default function SignUp() {
|
export default function SignUp() {
|
||||||
const { supabase } = useSupabase();
|
const { supabase } = useSupabase();
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import Card from "@/lib/components/ui/Card";
|
import { ReactNode } from "react";
|
||||||
import { FC, ReactNode } from "react";
|
|
||||||
import {
|
import {
|
||||||
GiArtificialIntelligence,
|
GiArtificialIntelligence,
|
||||||
GiBrain,
|
GiBrain,
|
||||||
@ -9,7 +8,9 @@ import {
|
|||||||
GiOpenBook,
|
GiOpenBook,
|
||||||
} from "react-icons/gi";
|
} from "react-icons/gi";
|
||||||
|
|
||||||
const Features: FC = () => {
|
import Card from "@/lib/components/ui/Card";
|
||||||
|
|
||||||
|
const Features = (): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<section className="my-20 text-center flex flex-col items-center justify-center gap-10">
|
<section className="my-20 text-center flex flex-col items-center justify-center gap-10">
|
||||||
<div>
|
<div>
|
||||||
@ -58,7 +59,7 @@ interface FeatureProps {
|
|||||||
desc: string;
|
desc: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Feature: FC<FeatureProps> = ({ title, desc, icon }) => {
|
const Feature = ({ title, desc, icon }: FeatureProps): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<Card className="p-10 max-w-xs flex flex-col gap-5 w-full">
|
<Card className="p-10 max-w-xs flex flex-col gap-5 w-full">
|
||||||
{icon}
|
{icon}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import Button from "@/lib/components/ui/Button";
|
|
||||||
import { motion, useScroll, useSpring, useTransform } from "framer-motion";
|
import { motion, useScroll, useSpring, useTransform } from "framer-motion";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { FC, useRef } from "react";
|
import { useRef } from "react";
|
||||||
import { MdNorthEast } from "react-icons/md";
|
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 targetRef = useRef<HTMLDivElement | null>(null);
|
||||||
const { scrollYProgress } = useScroll({
|
const { scrollYProgress } = useScroll({
|
||||||
target: targetRef,
|
target: targetRef,
|
||||||
@ -19,6 +20,7 @@ const Hero: FC = () => {
|
|||||||
if (pos === 1) {
|
if (pos === 1) {
|
||||||
return "relative";
|
return "relative";
|
||||||
}
|
}
|
||||||
|
|
||||||
return "sticky";
|
return "sticky";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import Features from "./Features";
|
|
||||||
import Hero from "./Hero";
|
|
||||||
import { redirect } from "next/navigation";
|
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") {
|
if (process.env.NEXT_PUBLIC_ENV === "local") {
|
||||||
redirect("/upload");
|
redirect("/upload");
|
||||||
}
|
}
|
||||||
@ -13,4 +14,6 @@ export default function HomePage() {
|
|||||||
<Features />
|
<Features />
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export default HomePage;
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
|
/* eslint-disable */
|
||||||
"use client";
|
"use client";
|
||||||
import PageHeading from "@/lib/components/ui/PageHeading";
|
|
||||||
import useChatsContext from "@/lib/context/ChatsProvider/hooks/useChatsContext";
|
|
||||||
import { UUID } from "crypto";
|
import { UUID } from "crypto";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
|
||||||
|
import PageHeading from "@/lib/components/ui/PageHeading";
|
||||||
|
import useChatsContext from "@/lib/context/ChatsProvider/hooks/useChatsContext";
|
||||||
|
|
||||||
import { ChatInput, ChatMessages } from "../components";
|
import { ChatInput, ChatMessages } from "../components";
|
||||||
|
|
||||||
interface ChatPageProps {
|
interface ChatPageProps {
|
||||||
@ -18,7 +21,9 @@ export default function ChatPage({ params }: ChatPageProps) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// if (chatId)
|
// if (chatId)
|
||||||
if (!chatId) resetChat();
|
if (!chatId) {
|
||||||
|
resetChat();
|
||||||
|
}
|
||||||
fetchChat(chatId);
|
fetchChat(chatId);
|
||||||
}, [fetchChat, chatId]);
|
}, [fetchChat, chatId]);
|
||||||
|
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import Button from "@/lib/components/ui/Button";
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { MdSettings } from "react-icons/md";
|
import { MdSettings } from "react-icons/md";
|
||||||
|
|
||||||
export function ConfigButton() {
|
import Button from "@/lib/components/ui/Button";
|
||||||
|
|
||||||
|
export const ConfigButton = (): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<Link href={"/config"}>
|
<Link href={"/config"}>
|
||||||
<Button className="p-2 sm:px-3" variant={"tertiary"}>
|
<Button className="p-2 sm:px-3" variant={"tertiary"}>
|
||||||
@ -11,4 +12,4 @@ export function ConfigButton() {
|
|||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
|
/* eslint-disable */
|
||||||
"use client";
|
"use client";
|
||||||
import Button from "@/lib/components/ui/Button";
|
|
||||||
import { useSpeech } from "@/lib/context/ChatsProvider/hooks/useSpeech";
|
|
||||||
import { MdMic, MdMicOff } from "react-icons/md";
|
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();
|
const { isListening, speechSupported, startListening } = useSpeech();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -21,4 +23,4 @@ export function MicButton() {
|
|||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
@ -1,17 +1,22 @@
|
|||||||
|
/* eslint-disable */
|
||||||
"use client";
|
"use client";
|
||||||
import Button from "@/lib/components/ui/Button";
|
import Button from "@/lib/components/ui/Button";
|
||||||
import useChatsContext from "@/lib/context/ChatsProvider/hooks/useChatsContext";
|
import useChatsContext from "@/lib/context/ChatsProvider/hooks/useChatsContext";
|
||||||
|
|
||||||
import { ConfigButton } from "./ConfigButton";
|
import { ConfigButton } from "./ConfigButton";
|
||||||
import { MicButton } from "./MicButton";
|
import { MicButton } from "./MicButton";
|
||||||
|
|
||||||
export function ChatInput() {
|
export const ChatInput = (): JSX.Element => {
|
||||||
const { isSendingMessage, sendMessage, setMessage, message, chat } =
|
const { isSendingMessage, sendMessage, setMessage, message, chat } =
|
||||||
useChatsContext();
|
useChatsContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
onSubmit={(e) => {
|
onSubmit={(e) => {
|
||||||
e.preventDefault();
|
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"
|
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) => {
|
onKeyDown={(e) => {
|
||||||
if (e.key === "Enter" && !e.shiftKey) {
|
if (e.key === "Enter" && !e.shiftKey) {
|
||||||
e.preventDefault(); // Prevents the newline from being entered in the textarea
|
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"
|
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>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
|
/* eslint-disable */
|
||||||
"use client";
|
"use client";
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import { forwardRef, Ref } from "react";
|
import { forwardRef, Ref } from "react";
|
||||||
import ReactMarkdown from "react-markdown";
|
import ReactMarkdown from "react-markdown";
|
||||||
|
|
||||||
const ChatMessage = forwardRef(
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
export const ChatMessage = forwardRef(
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
speaker,
|
speaker,
|
||||||
@ -46,5 +48,3 @@ const ChatMessage = forwardRef(
|
|||||||
);
|
);
|
||||||
|
|
||||||
ChatMessage.displayName = "ChatMessage";
|
ChatMessage.displayName = "ChatMessage";
|
||||||
|
|
||||||
export default ChatMessage;
|
|
||||||
|
@ -1,26 +1,32 @@
|
|||||||
|
/* eslint-disable */
|
||||||
"use client";
|
"use client";
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
import Card from "@/lib/components/ui/Card";
|
import Card from "@/lib/components/ui/Card";
|
||||||
import useChatsContext from "@/lib/context/ChatsProvider/hooks/useChatsContext";
|
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 lastChatRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
const { chat } = useChatsContext();
|
const { chat } = useChatsContext();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!chat || !lastChatRef.current) return;
|
if (!chat || !lastChatRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// if (chat.history.length > 2) {
|
// if (chat.history.length > 2) {
|
||||||
lastChatRef.current?.scrollIntoView({
|
lastChatRef.current.scrollIntoView({
|
||||||
behavior: "smooth",
|
behavior: "smooth",
|
||||||
block: "end",
|
block: "end",
|
||||||
});
|
});
|
||||||
// }
|
// }
|
||||||
}, [chat, lastChatRef]);
|
}, [chat, lastChatRef]);
|
||||||
|
|
||||||
if (!chat) return null;
|
if (!chat) {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="p-5 max-w-3xl w-full flex flex-col h-full mb-8">
|
<Card className="p-5 max-w-3xl w-full flex flex-col h-full mb-8">
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import { cn } from "@/lib/utils";
|
/* eslint-disable */
|
||||||
import { UUID } from "crypto";
|
import { UUID } from "crypto";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
import { FC } from "react";
|
|
||||||
import { FiTrash2 } from "react-icons/fi";
|
import { FiTrash2 } from "react-icons/fi";
|
||||||
import { MdChatBubbleOutline } from "react-icons/md";
|
import { MdChatBubbleOutline } from "react-icons/md";
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
import { Chat } from "../../../../lib/types/Chat";
|
import { Chat } from "../../../../lib/types/Chat";
|
||||||
|
|
||||||
interface ChatsListItemProps {
|
interface ChatsListItemProps {
|
||||||
@ -12,7 +14,10 @@ interface ChatsListItemProps {
|
|||||||
deleteChat: (id: UUID) => void;
|
deleteChat: (id: UUID) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ChatsListItem: FC<ChatsListItemProps> = ({ chat, deleteChat }) => {
|
const ChatsListItem = ({
|
||||||
|
chat,
|
||||||
|
deleteChat,
|
||||||
|
}: ChatsListItemProps): JSX.Element => {
|
||||||
const pathname = usePathname()?.split("/").at(-1);
|
const pathname = usePathname()?.split("/").at(-1);
|
||||||
const selected = chat.chatId === pathname;
|
const selected = chat.chatId === pathname;
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { BsPlusSquare } from "react-icons/bs";
|
import { BsPlusSquare } from "react-icons/bs";
|
||||||
|
|
||||||
export const NewChatButton = () => (
|
export const NewChatButton = (): JSX.Element => (
|
||||||
<Link
|
<Link
|
||||||
href="/chat"
|
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"
|
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"
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
|
/* eslint-disable */
|
||||||
"use client";
|
"use client";
|
||||||
|
import useChatsContext from "@/lib/context/ChatsProvider/hooks/useChatsContext";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { MotionConfig, motion } from "framer-motion";
|
import { MotionConfig, motion } from "framer-motion";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { MdChevronRight } from "react-icons/md";
|
import { MdChevronRight } from "react-icons/md";
|
||||||
import useChatsContext from "@/lib/context/ChatsProvider/hooks/useChatsContext";
|
|
||||||
import ChatsListItem from "./ChatsListItem";
|
import ChatsListItem from "./ChatsListItem";
|
||||||
import { NewChatButton } from "./NewChatButton";
|
import { NewChatButton } from "./NewChatButton";
|
||||||
export function ChatsList() {
|
|
||||||
|
export const ChatsList = (): JSX.Element => {
|
||||||
const { allChats, deleteChat } = useChatsContext();
|
const { allChats, deleteChat } = useChatsContext();
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
@ -69,4 +72,4 @@ export function ChatsList() {
|
|||||||
</motion.div>
|
</motion.div>
|
||||||
</MotionConfig>
|
</MotionConfig>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
|
export * from "./ChatMessages";
|
||||||
export * from "./ChatMessages/ChatInput";
|
export * from "./ChatMessages/ChatInput";
|
||||||
export * from "./ChatMessages/ChatMessage";
|
export * from "./ChatMessages/ChatMessage";
|
||||||
export * from "./ChatMessages";
|
|
||||||
export * from "./ChatsList";
|
export * from "./ChatsList";
|
||||||
|
@ -1,17 +1,21 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { ChatsProvider } from "@/lib/context/ChatsProvider/chats-provider";
|
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import { FC, ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
import { useSupabase } from "../supabase-provider";
|
|
||||||
|
import { ChatsProvider } from "@/lib/context/ChatsProvider/chats-provider";
|
||||||
|
|
||||||
import { ChatsList } from "./components";
|
import { ChatsList } from "./components";
|
||||||
|
import { useSupabase } from "../supabase-provider";
|
||||||
|
|
||||||
interface LayoutProps {
|
interface LayoutProps {
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Layout: FC<LayoutProps> = ({ children }) => {
|
const Layout = ({ children }: LayoutProps): JSX.Element => {
|
||||||
const { session } = useSupabase();
|
const { session } = useSupabase();
|
||||||
if (!session) redirect("/login");
|
if (!session) {
|
||||||
|
redirect("/login");
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ChatsProvider>
|
<ChatsProvider>
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
|
/* eslint-disable */
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
import Button from "@/lib/components/ui/Button";
|
import Button from "@/lib/components/ui/Button";
|
||||||
import { useAxios } from "@/lib/useAxios";
|
import { useAxios } from "@/lib/useAxios";
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
export const ApiKeyConfig = (): JSX.Element => {
|
export const ApiKeyConfig = (): JSX.Element => {
|
||||||
const [apiKey, setApiKey] = useState("");
|
const [apiKey, setApiKey] = useState("");
|
||||||
@ -40,13 +42,13 @@ export const ApiKeyConfig = (): JSX.Element => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<div className="flex items-center space-x-4">
|
<div className="flex items-center space-x-4">
|
||||||
{!apiKey && (
|
{apiKey === "" && (
|
||||||
<Button variant="secondary" onClick={handleCreateClick}>
|
<Button variant="secondary" onClick={handleCreateClick}>
|
||||||
Create New Key
|
Create New Key
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{apiKey && (
|
{apiKey !== "" && (
|
||||||
<div className="flex items-center space-x-4">
|
<div className="flex items-center space-x-4">
|
||||||
<span className="text-gray-600">{apiKey}</span>
|
<span className="text-gray-600">{apiKey}</span>
|
||||||
<Button variant="secondary" onClick={handleCopyClick}>
|
<Button variant="secondary" onClick={handleCopyClick}>
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
|
/* eslint-disable */
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import { UseFormRegister } from "react-hook-form";
|
||||||
|
|
||||||
import Field from "@/lib/components/ui/Field";
|
import Field from "@/lib/components/ui/Field";
|
||||||
import { BrainConfig } from "@/lib/context/BrainConfigProvider/types";
|
import { BrainConfig } from "@/lib/context/BrainConfigProvider/types";
|
||||||
import { UseFormRegister } from "react-hook-form";
|
|
||||||
|
|
||||||
interface BackendConfigProps {
|
interface BackendConfigProps {
|
||||||
register: UseFormRegister<BrainConfig>;
|
register: UseFormRegister<BrainConfig>;
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
|
/* eslint-disable */
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import Button from "@/lib/components/ui/Button";
|
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
|
||||||
|
import Button from "@/lib/components/ui/Button";
|
||||||
|
|
||||||
import { useConfig } from "../hooks/useConfig";
|
import { useConfig } from "../hooks/useConfig";
|
||||||
import { BackendConfig } from "./BackendConfig";
|
import { BackendConfig } from "./BackendConfig";
|
||||||
import { ModelConfig } from "./ModelConfig";
|
import { ModelConfig } from "./ModelConfig";
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
|
/* eslint-disable */
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import { UseFormRegister } from "react-hook-form";
|
||||||
|
|
||||||
import Field from "@/lib/components/ui/Field";
|
import Field from "@/lib/components/ui/Field";
|
||||||
import {
|
import {
|
||||||
BrainConfig,
|
BrainConfig,
|
||||||
@ -9,7 +12,6 @@ import {
|
|||||||
models,
|
models,
|
||||||
paidModels,
|
paidModels,
|
||||||
} from "@/lib/context/BrainConfigProvider/types";
|
} from "@/lib/context/BrainConfigProvider/types";
|
||||||
import { UseFormRegister } from "react-hook-form";
|
|
||||||
|
|
||||||
interface ModelConfigProps {
|
interface ModelConfigProps {
|
||||||
register: UseFormRegister<BrainConfig>;
|
register: UseFormRegister<BrainConfig>;
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
|
/* eslint-disable */
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
import { useSupabase } from "@/app/supabase-provider";
|
import { useSupabase } from "@/app/supabase-provider";
|
||||||
import Button from "@/lib/components/ui/Button";
|
import Button from "@/lib/components/ui/Button";
|
||||||
import Link from "next/link";
|
|
||||||
|
|
||||||
export const UserAccountSection = (): JSX.Element => {
|
export const UserAccountSection = (): JSX.Element => {
|
||||||
const { session } = useSupabase();
|
const { session } = useSupabase();
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { useToast } from "@/lib/hooks/useToast";
|
/* eslint-disable */
|
||||||
|
import { useEffect } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
|
||||||
import { useBrainConfig } from "@/lib/context/BrainConfigProvider/hooks/useBrainConfig";
|
import { useBrainConfig } from "@/lib/context/BrainConfigProvider/hooks/useBrainConfig";
|
||||||
import { useEffect } from "react";
|
import { useToast } from "@/lib/hooks/useToast";
|
||||||
|
|
||||||
export const useConfig = () => {
|
export const useConfig = () => {
|
||||||
const { config, updateConfig, resetConfig } = useBrainConfig();
|
const { config, updateConfig, resetConfig } = useBrainConfig();
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable */
|
||||||
"use client";
|
"use client";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
@ -6,7 +7,7 @@ import { ConfigForm, ConfigTitle } from "./components";
|
|||||||
import { ApiKeyConfig } from "./components/ApiKeyConfig";
|
import { ApiKeyConfig } from "./components/ApiKeyConfig";
|
||||||
|
|
||||||
// TODO: Use states instead of NEXTJS router to open and close modal
|
// TODO: Use states instead of NEXTJS router to open and close modal
|
||||||
export default function ConfigPage() {
|
const ConfigPage = (): JSX.Element => {
|
||||||
const { session } = useSupabase();
|
const { session } = useSupabase();
|
||||||
|
|
||||||
if (session === null) {
|
if (session === null) {
|
||||||
@ -22,4 +23,6 @@ export default function ConfigPage() {
|
|||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export default ConfigPage;
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import { useAxios } from "@/lib/useAxios";
|
/* eslint-disable */
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
import { useAxios } from "@/lib/useAxios";
|
||||||
|
|
||||||
import { useSupabase } from "../../supabase-provider";
|
import { useSupabase } from "../../supabase-provider";
|
||||||
|
|
||||||
interface DocumentDataProps {
|
interface DocumentDataProps {
|
||||||
|
@ -1,19 +1,22 @@
|
|||||||
|
/* eslint-disable */
|
||||||
"use client";
|
"use client";
|
||||||
|
import {
|
||||||
|
Dispatch,
|
||||||
|
forwardRef,
|
||||||
|
RefObject,
|
||||||
|
SetStateAction,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
|
|
||||||
import { useSupabase } from "@/app/supabase-provider";
|
import { useSupabase } from "@/app/supabase-provider";
|
||||||
import Button from "@/lib/components/ui/Button";
|
import Button from "@/lib/components/ui/Button";
|
||||||
import { AnimatedCard } from "@/lib/components/ui/Card";
|
import { AnimatedCard } from "@/lib/components/ui/Card";
|
||||||
import Ellipsis from "@/lib/components/ui/Ellipsis";
|
import Ellipsis from "@/lib/components/ui/Ellipsis";
|
||||||
import Modal from "@/lib/components/ui/Modal";
|
import Modal from "@/lib/components/ui/Modal";
|
||||||
import { useToast } from "@/lib/hooks/useToast";
|
import { useToast } from "@/lib/hooks/useToast";
|
||||||
|
import { Document } from "@/lib/types/Document";
|
||||||
import { useAxios } from "@/lib/useAxios";
|
import { useAxios } from "@/lib/useAxios";
|
||||||
import {
|
|
||||||
Dispatch,
|
|
||||||
RefObject,
|
|
||||||
SetStateAction,
|
|
||||||
forwardRef,
|
|
||||||
useState,
|
|
||||||
} from "react";
|
|
||||||
import { Document } from "../../../lib/types/Document";
|
|
||||||
import DocumentData from "./DocumentData";
|
import DocumentData from "./DocumentData";
|
||||||
|
|
||||||
interface DocumentProps {
|
interface DocumentProps {
|
||||||
|
@ -1,17 +1,19 @@
|
|||||||
|
/* eslint-disable */
|
||||||
"use client";
|
"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 { AnimatePresence, motion } from "framer-motion";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import { useEffect, useState } from "react";
|
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 { useSupabase } from "../supabase-provider";
|
||||||
import DocumentItem from "./DocumentItem";
|
import DocumentItem from "./DocumentItem";
|
||||||
|
|
||||||
export default function ExplorePage() {
|
const ExplorePage = (): JSX.Element => {
|
||||||
const [documents, setDocuments] = useState<Document[]>([]);
|
const [documents, setDocuments] = useState<Document[]>([]);
|
||||||
const [isPending, setIsPending] = useState(true);
|
const [isPending, setIsPending] = useState(true);
|
||||||
const { session } = useSupabase();
|
const { session } = useSupabase();
|
||||||
@ -79,4 +81,6 @@ export default function ExplorePage() {
|
|||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export default ExplorePage;
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
@import 'tailwindcss/base';
|
@import "tailwindcss/base";
|
||||||
@import 'tailwindcss/components';
|
@import "tailwindcss/components";
|
||||||
@import 'tailwindcss/utilities';
|
@import "tailwindcss/utilities";
|
||||||
|
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@apply max-w-screen-xl mx-auto flex flex-col;
|
@apply max-w-screen-xl mx-auto flex flex-col;
|
||||||
}
|
}
|
||||||
|
|
||||||
header, section {
|
header,
|
||||||
|
section {
|
||||||
@apply px-5 md:px-10;
|
@apply px-5 md:px-10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 { createServerComponentSupabaseClient } from "@supabase/auth-helpers-nextjs";
|
||||||
import { Analytics } from "@vercel/analytics/react";
|
import { Analytics } from "@vercel/analytics/react";
|
||||||
import { Inter } from "next/font/google";
|
import { Inter } from "next/font/google";
|
||||||
import { cookies, headers } from "next/headers";
|
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 "./globals.css";
|
||||||
import SupabaseProvider from "./supabase-provider";
|
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.",
|
"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,
|
||||||
}: {
|
}: {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) {
|
}): Promise<JSX.Element> => {
|
||||||
const supabase = createServerComponentSupabaseClient({
|
const supabase = createServerComponentSupabaseClient({
|
||||||
headers,
|
headers,
|
||||||
cookies,
|
cookies,
|
||||||
@ -49,4 +51,6 @@ export default async function RootLayout({
|
|||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export default RootLayout;
|
||||||
|
@ -1,56 +1,59 @@
|
|||||||
'use client'
|
"use client";
|
||||||
|
|
||||||
import { createContext, useContext, useEffect, useState } from 'react'
|
import type { SupabaseClient } from "@supabase/auth-helpers-nextjs";
|
||||||
import { Session, createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
|
import {
|
||||||
import { useRouter } from 'next/navigation'
|
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 = {
|
type SupabaseContext = {
|
||||||
supabase: SupabaseClient
|
supabase: SupabaseClient;
|
||||||
session: MaybeSession
|
session: MaybeSession;
|
||||||
}
|
};
|
||||||
|
|
||||||
const Context = createContext<SupabaseContext | undefined>(undefined)
|
const Context = createContext<SupabaseContext | undefined>(undefined);
|
||||||
|
|
||||||
export default function SupabaseProvider({
|
const SupabaseProvider = ({
|
||||||
children,
|
children,
|
||||||
session,
|
session,
|
||||||
}: {
|
}: {
|
||||||
children: React.ReactNode
|
children: React.ReactNode;
|
||||||
session: MaybeSession
|
session: MaybeSession;
|
||||||
}) {
|
}): JSX.Element => {
|
||||||
const [supabase] = useState(() => createBrowserSupabaseClient())
|
const [supabase] = useState(() => createBrowserSupabaseClient());
|
||||||
const router = useRouter()
|
const router = useRouter();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const {
|
const {
|
||||||
data: { subscription },
|
data: { subscription },
|
||||||
} = supabase.auth.onAuthStateChange(() => {
|
} = supabase.auth.onAuthStateChange(() => {
|
||||||
router.refresh()
|
router.refresh();
|
||||||
})
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
subscription.unsubscribe()
|
subscription.unsubscribe();
|
||||||
}
|
};
|
||||||
}, [router, supabase])
|
}, [router, supabase]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Context.Provider value={{ supabase, session }}>
|
<Context.Provider value={{ supabase, session }}>
|
||||||
<>{children}</>
|
<>{children}</>
|
||||||
</Context.Provider>
|
</Context.Provider>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export const useSupabase = () => {
|
export const useSupabase = (): SupabaseContext => {
|
||||||
const context = useContext(Context)
|
const context = useContext(Context);
|
||||||
|
|
||||||
if (context === undefined) {
|
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;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
export const isValidUrl = (string: string) => {
|
export const isValidUrl = (string: string): boolean => {
|
||||||
try {
|
try {
|
||||||
new URL(string);
|
new URL(string);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
import { redirect } from "next/navigation";
|
||||||
|
import { useCallback, useRef, useState } from "react";
|
||||||
|
|
||||||
import { useSupabase } from "@/app/supabase-provider";
|
import { useSupabase } from "@/app/supabase-provider";
|
||||||
import { useToast } from "@/lib/hooks/useToast";
|
import { useToast } from "@/lib/hooks/useToast";
|
||||||
import { useAxios } from "@/lib/useAxios";
|
import { useAxios } from "@/lib/useAxios";
|
||||||
import { redirect } from "next/navigation";
|
|
||||||
import { useCallback, useRef, useState } from "react";
|
|
||||||
import { isValidUrl } from "../helpers/isValidUrl";
|
import { isValidUrl } from "../helpers/isValidUrl";
|
||||||
|
|
||||||
export const useCrawler = () => {
|
export const useCrawler = () => {
|
||||||
@ -26,6 +29,7 @@ export const useCrawler = () => {
|
|||||||
variant: "danger",
|
variant: "danger",
|
||||||
text: "Invalid URL",
|
text: "Invalid URL",
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,10 +2,12 @@
|
|||||||
import Button from "@/lib/components/ui/Button";
|
import Button from "@/lib/components/ui/Button";
|
||||||
import Card from "@/lib/components/ui/Card";
|
import Card from "@/lib/components/ui/Card";
|
||||||
import Field from "@/lib/components/ui/Field";
|
import Field from "@/lib/components/ui/Field";
|
||||||
|
|
||||||
import { useCrawler } from "./hooks/useCrawler";
|
import { useCrawler } from "./hooks/useCrawler";
|
||||||
|
|
||||||
export const Crawler = (): JSX.Element => {
|
export const Crawler = (): JSX.Element => {
|
||||||
const { urlInputRef, isCrawling, crawlWebsite } = useCrawler();
|
const { urlInputRef, isCrawling, crawlWebsite } = useCrawler();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<div className="flex justify-center gap-5 px-6">
|
<div className="flex justify-center gap-5 px-6">
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
|
/* eslint-disable */
|
||||||
import { motion } from "framer-motion";
|
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";
|
import { MdClose } from "react-icons/md";
|
||||||
|
|
||||||
interface FileComponentProps {
|
interface FileComponentProps {
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import { useSupabase } from "@/app/supabase-provider";
|
/* eslint-disable */
|
||||||
import { useToast } from "@/lib/hooks/useToast";
|
|
||||||
import { useAxios } from "@/lib/useAxios";
|
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import { FileRejection, useDropzone } from "react-dropzone";
|
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 = () => {
|
export const useFileUploader = () => {
|
||||||
const [isPending, setIsPending] = useState(false);
|
const [isPending, setIsPending] = useState(false);
|
||||||
const { publish } = useToast();
|
const { publish } = useToast();
|
||||||
@ -44,6 +46,7 @@ export const useFileUploader = () => {
|
|||||||
const onDrop = (acceptedFiles: File[], fileRejections: FileRejection[]) => {
|
const onDrop = (acceptedFiles: File[], fileRejections: FileRejection[]) => {
|
||||||
if (fileRejections.length > 0) {
|
if (fileRejections.length > 0) {
|
||||||
publish({ variant: "danger", text: "File too big." });
|
publish({ variant: "danger", text: "File too big." });
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,6 +72,7 @@ export const useFileUploader = () => {
|
|||||||
text: "Please, add files to upload",
|
text: "Please, add files to upload",
|
||||||
variant: "warning",
|
variant: "warning",
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setIsPending(true);
|
setIsPending(true);
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
|
/* eslint-disable */
|
||||||
"use client";
|
"use client";
|
||||||
|
import { AnimatePresence } from "framer-motion";
|
||||||
|
|
||||||
import Button from "@/lib/components/ui/Button";
|
import Button from "@/lib/components/ui/Button";
|
||||||
import Card from "@/lib/components/ui/Card";
|
import Card from "@/lib/components/ui/Card";
|
||||||
import { AnimatePresence } from "framer-motion";
|
|
||||||
import FileComponent from "./components/FileComponent";
|
import FileComponent from "./components/FileComponent";
|
||||||
import { useFileUploader } from "./hooks/useFileUploader";
|
import { useFileUploader } from "./hooks/useFileUploader";
|
||||||
|
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
import Button from "@/lib/components/ui/Button";
|
import Button from "@/lib/components/ui/Button";
|
||||||
import { Divider } from "@/lib/components/ui/Divider";
|
import { Divider } from "@/lib/components/ui/Divider";
|
||||||
import PageHeading from "@/lib/components/ui/PageHeading";
|
import PageHeading from "@/lib/components/ui/PageHeading";
|
||||||
import Link from "next/link";
|
|
||||||
import { Crawler } from "./components/Crawler";
|
import { Crawler } from "./components/Crawler";
|
||||||
import { FileUploader } from "./components/FileUploader";
|
import { FileUploader } from "./components/FileUploader";
|
||||||
|
|
||||||
export default function UploadPage() {
|
const UploadPage = (): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<main className="pt-10">
|
<main className="pt-10">
|
||||||
<PageHeading
|
<PageHeading
|
||||||
@ -25,4 +27,6 @@ export default function UploadPage() {
|
|||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export default UploadPage;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { GiBrain } from "react-icons/gi";
|
import { GiBrain } from "react-icons/gi";
|
||||||
|
|
||||||
import { UserStats } from "../../../lib/types/User";
|
import { UserStats } from "../../../lib/types/User";
|
||||||
|
|
||||||
export const BrainConsumption = (userStats: UserStats): JSX.Element => {
|
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"
|
className="fill-pink-300 stroke-black stoke-1"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center justify-center w-fit">
|
<div className="flex flex-col items-center justify-center w-fit">
|
||||||
<div className="w-24 h-24 relative">
|
<div className="w-24 h-24 relative">
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { HTMLAttributes } from "react";
|
import { HTMLAttributes } from "react";
|
||||||
|
|
||||||
import { UserStats } from "../../../lib/types/User";
|
import { UserStats } from "../../../lib/types/User";
|
||||||
|
|
||||||
interface DateComponentProps extends HTMLAttributes<HTMLSpanElement> {
|
interface DateComponentProps extends HTMLAttributes<HTMLSpanElement> {
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { FC } from "react";
|
|
||||||
import {
|
import {
|
||||||
VictoryContainer,
|
VictoryContainer,
|
||||||
VictoryPie,
|
VictoryPie,
|
||||||
@ -12,12 +11,14 @@ interface BrainSpaceChartProps extends VictoryPieProps {
|
|||||||
max_brain_size: number;
|
max_brain_size: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BrainSpaceChart: FC<BrainSpaceChartProps> = ({
|
const BrainSpaceChart = ({
|
||||||
current_brain_size,
|
current_brain_size,
|
||||||
max_brain_size,
|
max_brain_size,
|
||||||
...props
|
...props
|
||||||
}) => {
|
}: BrainSpaceChartProps): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
{/* @ts-expect-error Server Component */}
|
||||||
<VictoryPie
|
<VictoryPie
|
||||||
data={[
|
data={[
|
||||||
{ x: "Used", y: current_brain_size },
|
{ x: "Used", y: current_brain_size },
|
||||||
@ -32,6 +33,7 @@ const BrainSpaceChart: FC<BrainSpaceChartProps> = ({
|
|||||||
{...props}
|
{...props}
|
||||||
theme={VictoryTheme.material}
|
theme={VictoryTheme.material}
|
||||||
/>
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
|
/* eslint-disable */
|
||||||
"use client";
|
"use client";
|
||||||
import { format, subDays } from "date-fns";
|
import { format, subDays } from "date-fns";
|
||||||
import React from "react";
|
|
||||||
import {
|
import {
|
||||||
VictoryAxis,
|
VictoryAxis,
|
||||||
VictoryChart,
|
VictoryChart,
|
||||||
@ -20,14 +20,15 @@ interface RequestsPerDayChartProps extends VictoryChartProps {
|
|||||||
requests_stats: RequestStat[];
|
requests_stats: RequestStat[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const RequestsPerDayChart: React.FC<RequestsPerDayChartProps> = ({
|
export const RequestsPerDayChart = ({
|
||||||
requests_stats,
|
requests_stats,
|
||||||
...props
|
...props
|
||||||
}) => {
|
}: RequestsPerDayChartProps): JSX.Element => {
|
||||||
const data = Array.from({ length: 7 }, (_, i) => subDays(new Date(), i))
|
const data = Array.from({ length: 7 }, (_, i) => subDays(new Date(), i))
|
||||||
.map((date) => {
|
.map((date) => {
|
||||||
const dateString = format(date, "yyyyMMdd");
|
const dateString = format(date, "yyyyMMdd");
|
||||||
const stat = requests_stats.find((s) => s.date === dateString);
|
const stat = requests_stats.find((s) => s.date === dateString);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
date: format(date, "MM/dd/yyyy"),
|
date: format(date, "MM/dd/yyyy"),
|
||||||
requests_count: stat ? stat.requests_count : 0,
|
requests_count: stat ? stat.requests_count : 0,
|
||||||
@ -50,12 +51,15 @@ export const RequestsPerDayChart: React.FC<RequestsPerDayChartProps> = ({
|
|||||||
}}
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
|
{/* @ts-expect-error Server Component */}
|
||||||
<VictoryAxis
|
<VictoryAxis
|
||||||
tickFormat={(tick) => {
|
tickFormat={(tick) => {
|
||||||
return `${tick.split("/")[0]}/${tick.split("/")[1]}`;
|
return `${tick.split("/")[0]}/${tick.split("/")[1]}`;
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
{/* @ts-expect-error Server Component */}
|
||||||
<VictoryAxis dependentAxis />
|
<VictoryAxis dependentAxis />
|
||||||
|
{/* @ts-expect-error Server Component */}
|
||||||
<VictoryLine data={data} x="date" y="requests_count" />
|
<VictoryLine data={data} x="date" y="requests_count" />
|
||||||
</VictoryChart>
|
</VictoryChart>
|
||||||
);
|
);
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
|
/* eslint-disable */
|
||||||
"use client";
|
"use client";
|
||||||
import Button from "@/lib/components/ui/Button";
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import prettyBytes from "pretty-bytes";
|
import prettyBytes from "pretty-bytes";
|
||||||
import { HTMLAttributes } from "react";
|
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 { BrainConsumption } from "./BrainConsumption";
|
||||||
import { DateComponent } from "./Date";
|
import { DateComponent } from "./Date";
|
||||||
import BrainSpaceChart from "./Graphs/BrainSpaceChart";
|
import BrainSpaceChart from "./Graphs/BrainSpaceChart";
|
||||||
@ -13,6 +16,7 @@ import { RequestsPerDayChart } from "./Graphs/RequestsPerDayChart";
|
|||||||
export const UserStatistics = (userStats: UserStats): JSX.Element => {
|
export const UserStatistics = (userStats: UserStats): JSX.Element => {
|
||||||
const { email, current_brain_size, max_brain_size, date, requests_stats } =
|
const { email, current_brain_size, max_brain_size, date, requests_stats } =
|
||||||
userStats;
|
userStats;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex flex-col sm:flex-row sm:items-center py-10 gap-5">
|
<div className="flex flex-col sm:flex-row sm:items-center py-10 gap-5">
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
|
/* eslint-disable */
|
||||||
"use client";
|
"use client";
|
||||||
import Spinner from "@/lib/components/ui/Spinner";
|
|
||||||
import { useAxios } from "@/lib/useAxios";
|
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import { useEffect, useState } from "react";
|
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 { useSupabase } from "../supabase-provider";
|
||||||
import { UserStatistics } from "./components/UserStatistics";
|
import { UserStatistics } from "./components/UserStatistics";
|
||||||
|
|
||||||
export default function UserPage() {
|
const UserPage = (): JSX.Element => {
|
||||||
const [userStats, setUserStats] = useState<UserStats>();
|
const [userStats, setUserStats] = useState<UserStats>();
|
||||||
const { session } = useSupabase();
|
const { session } = useSupabase();
|
||||||
const { axiosInstance } = useAxios();
|
const { axiosInstance } = useAxios();
|
||||||
@ -57,4 +60,5 @@ export default function UserPage() {
|
|||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
export default UserPage;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
const Footer = () => {
|
const Footer = (): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<footer className="bg-white dark:bg-black border-t dark:border-white/10 mt-auto py-10">
|
<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">
|
<div className="max-w-screen-xl mx-auto flex justify-center items-center gap-4">
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable */
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
export const useHeader = () => {
|
export const useHeader = () => {
|
||||||
@ -16,6 +17,7 @@ export const useHeader = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener("scroll", handleScroll);
|
window.addEventListener("scroll", handleScroll);
|
||||||
|
|
||||||
return () => window.removeEventListener("scroll", handleScroll);
|
return () => window.removeEventListener("scroll", handleScroll);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
|
|
||||||
import { useHeader } from "./hooks/useHeader";
|
import { useHeader } from "./hooks/useHeader";
|
||||||
|
|
||||||
export const Header = ({ children }: { children: React.ReactNode }) => {
|
export const Header = ({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}): JSX.Element => {
|
||||||
const { hidden } = useHeader();
|
const { hidden } = useHeader();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
export const Logo = () => {
|
export const Logo = (): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<Link href={"/"} className="flex items-center gap-4">
|
<Link href={"/"} className="flex items-center gap-4">
|
||||||
<Image
|
<Image
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
import * as Dialog from "@radix-ui/react-dialog";
|
import * as Dialog from "@radix-ui/react-dialog";
|
||||||
import { AnimatePresence, motion } from "framer-motion";
|
import { AnimatePresence, motion } from "framer-motion";
|
||||||
import { FC, useState } from "react";
|
import { useState } from "react";
|
||||||
import { MdClose, MdMenu } from "react-icons/md";
|
import { MdClose, MdMenu } from "react-icons/md";
|
||||||
|
|
||||||
import { NavItems } from "./NavItems";
|
import { NavItems } from "./NavItems";
|
||||||
|
|
||||||
export const MobileMenu: FC = () => {
|
export const MobileMenu = (): JSX.Element => {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog.Root onOpenChange={setOpen} open={open}>
|
<Dialog.Root onOpenChange={setOpen} open={open}>
|
||||||
<Dialog.Trigger asChild>
|
<Dialog.Trigger asChild>
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
import Button from "@/lib/components/ui/Button";
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
|
|
||||||
|
import Button from "@/lib/components/ui/Button";
|
||||||
|
|
||||||
export const AuthButtons = (): JSX.Element => {
|
export const AuthButtons = (): JSX.Element => {
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
|
|
||||||
if (pathname === "/signup")
|
if (pathname === "/signup") {
|
||||||
return (
|
return (
|
||||||
<Link href={"/login"}>
|
<Link href={"/login"}>
|
||||||
<Button variant={"secondary"}>Login</Button>
|
<Button variant={"secondary"}>Login</Button>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link href={"/signup"}>
|
<Link href={"/signup"}>
|
||||||
<Button variant={"secondary"}>Register</Button>
|
<Button variant={"secondary"}>Register</Button>
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
|
/* eslint-disable */
|
||||||
"use client";
|
"use client";
|
||||||
import Button from "@/lib/components/ui/Button";
|
import { useEffect, useLayoutEffect, useState } from "react";
|
||||||
import { FC, useEffect, useLayoutEffect, useState } from "react";
|
|
||||||
import { MdDarkMode, MdLightMode } from "react-icons/md";
|
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);
|
const [dark, setDark] = useState(false);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
|
/* eslint-disable */
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { Dispatch, FC, ReactNode, SetStateAction } from "react";
|
import { Dispatch, ReactNode, SetStateAction } from "react";
|
||||||
|
|
||||||
interface NavLinkProps {
|
interface NavLinkProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
@ -7,7 +8,11 @@ interface NavLinkProps {
|
|||||||
setOpen?: Dispatch<SetStateAction<boolean>>;
|
setOpen?: Dispatch<SetStateAction<boolean>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NavLink: FC<NavLinkProps> = ({ children, to, setOpen }) => {
|
export const NavLink = ({
|
||||||
|
children,
|
||||||
|
to,
|
||||||
|
setOpen,
|
||||||
|
}: NavLinkProps): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<li className="group relative">
|
<li className="group relative">
|
||||||
<Link onClick={() => setOpen && setOpen(false)} href={to}>
|
<Link onClick={() => setOpen && setOpen(false)} href={to}>
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
|
/* eslint-disable */
|
||||||
"use client";
|
"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 { useSupabase } from "@/app/supabase-provider";
|
||||||
import Button from "@/lib/components/ui/Button";
|
import Button from "@/lib/components/ui/Button";
|
||||||
import { cn } from "@/lib/utils";
|
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 { AuthButtons } from "./components/AuthButtons";
|
||||||
import { DarkModeToggle } from "./components/DarkModeToggle";
|
import { DarkModeToggle } from "./components/DarkModeToggle";
|
||||||
import { NavLink } from "./components/NavLink";
|
import { NavLink } from "./components/NavLink";
|
||||||
@ -13,11 +16,11 @@ interface NavItemsProps extends HTMLAttributes<HTMLUListElement> {
|
|||||||
setOpen?: Dispatch<SetStateAction<boolean>>;
|
setOpen?: Dispatch<SetStateAction<boolean>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NavItems: FC<NavItemsProps> = ({
|
export const NavItems = ({
|
||||||
className,
|
className,
|
||||||
setOpen,
|
setOpen,
|
||||||
...props
|
...props
|
||||||
}) => {
|
}: NavItemsProps): JSX.Element => {
|
||||||
const { session } = useSupabase();
|
const { session } = useSupabase();
|
||||||
const isUserLoggedIn = session?.user !== undefined;
|
const isUserLoggedIn = session?.user !== undefined;
|
||||||
|
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { FC } from "react";
|
|
||||||
|
|
||||||
import { Header } from "./components/Header";
|
import { Header } from "./components/Header";
|
||||||
import { Logo } from "./components/Logo";
|
import { Logo } from "./components/Logo";
|
||||||
import { MobileMenu } from "./components/MobileMenu";
|
import { MobileMenu } from "./components/MobileMenu";
|
||||||
import { NavItems } from "./components/NavItems";
|
import { NavItems } from "./components/NavItems";
|
||||||
|
|
||||||
export const NavBar: FC = () => {
|
export const NavBar = (): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<Header>
|
<Header>
|
||||||
<Logo />
|
<Logo />
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import { cn } from "@/lib/utils";
|
/* eslint-disable */
|
||||||
import { cva, type VariantProps } from "class-variance-authority";
|
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 { FaSpinner } from "react-icons/fa";
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
const ButtonVariants = cva(
|
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",
|
"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;
|
isLoading?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Button: FC<ButtonProps> = forwardRef(
|
const Button = forwardRef(
|
||||||
(
|
(
|
||||||
{ className, children, variant, brightness, isLoading, ...props },
|
{
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
variant,
|
||||||
|
brightness,
|
||||||
|
isLoading,
|
||||||
|
...props
|
||||||
|
}: ButtonProps,
|
||||||
forwardedRef
|
forwardedRef
|
||||||
) => {
|
): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className={cn(ButtonVariants({ variant, brightness, className }))}
|
className={cn(ButtonVariants({ variant, brightness, className }))}
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
|
/* eslint-disable */
|
||||||
"use client";
|
"use client";
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import { motion } from "framer-motion";
|
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>;
|
type CardProps = HTMLAttributes<HTMLDivElement>;
|
||||||
|
|
||||||
const Card: FC<CardProps> = forwardRef(
|
const Card = forwardRef(
|
||||||
({ children, className, ...props }, ref) => {
|
({ children, className, ...props }: CardProps, ref): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={ref as LegacyRef<HTMLDivElement>}
|
ref={ref as LegacyRef<HTMLDivElement>}
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
import { forwardRef, HTMLAttributes, LegacyRef } from "react";
|
||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { FC, HTMLAttributes, LegacyRef, forwardRef } from "react";
|
|
||||||
|
|
||||||
type DividerProps = HTMLAttributes<HTMLDivElement> & {
|
type DividerProps = HTMLAttributes<HTMLDivElement> & {
|
||||||
text?: string;
|
text?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Divider: FC<DividerProps> = forwardRef(
|
const Divider = forwardRef(
|
||||||
({ className, text, ...props }, ref) => {
|
({ className, text, ...props }: DividerProps, ref): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={ref as LegacyRef<HTMLDivElement>}
|
ref={ref as LegacyRef<HTMLDivElement>}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
|
/* eslint-disable */
|
||||||
"use client";
|
"use client";
|
||||||
|
import { HTMLAttributes } from "react";
|
||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { FC, HTMLAttributes } from "react";
|
|
||||||
import Tooltip from "./Tooltip";
|
import Tooltip from "./Tooltip";
|
||||||
|
|
||||||
interface EllipsisProps extends HTMLAttributes<HTMLDivElement> {
|
interface EllipsisProps extends HTMLAttributes<HTMLDivElement> {
|
||||||
@ -9,12 +12,12 @@ interface EllipsisProps extends HTMLAttributes<HTMLDivElement> {
|
|||||||
tooltip?: boolean;
|
tooltip?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Ellipsis: FC<EllipsisProps> = ({
|
const Ellipsis = ({
|
||||||
children: originalContent,
|
children: originalContent,
|
||||||
className,
|
className,
|
||||||
maxCharacters,
|
maxCharacters,
|
||||||
tooltip = false,
|
tooltip = false,
|
||||||
}) => {
|
}: EllipsisProps): JSX.Element => {
|
||||||
const renderedContent =
|
const renderedContent =
|
||||||
originalContent.length > maxCharacters
|
originalContent.length > maxCharacters
|
||||||
? `${originalContent.slice(0, maxCharacters)}...`
|
? `${originalContent.slice(0, maxCharacters)}...`
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
import { cn } from "@/lib/utils";
|
/* eslint-disable */
|
||||||
import {
|
import {
|
||||||
DetailedHTMLProps,
|
DetailedHTMLProps,
|
||||||
|
forwardRef,
|
||||||
InputHTMLAttributes,
|
InputHTMLAttributes,
|
||||||
RefObject,
|
RefObject,
|
||||||
forwardRef,
|
|
||||||
} from "react";
|
} from "react";
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
interface FieldProps
|
interface FieldProps
|
||||||
extends DetailedHTMLProps<
|
extends DetailedHTMLProps<
|
||||||
InputHTMLAttributes<HTMLInputElement>,
|
InputHTMLAttributes<HTMLInputElement>,
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import * as Dialog from "@radix-ui/react-dialog";
|
import * as Dialog from "@radix-ui/react-dialog";
|
||||||
import { AnimatePresence, motion } from "framer-motion";
|
import { AnimatePresence, motion } from "framer-motion";
|
||||||
import { FC, ReactNode, useState } from "react";
|
import { ReactNode, useState } from "react";
|
||||||
import { MdClose } from "react-icons/md";
|
import { MdClose } from "react-icons/md";
|
||||||
|
|
||||||
import Button from "./Button";
|
import Button from "./Button";
|
||||||
|
|
||||||
interface ModalProps {
|
interface ModalProps {
|
||||||
@ -13,14 +14,15 @@ interface ModalProps {
|
|||||||
CloseTrigger?: ReactNode;
|
CloseTrigger?: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Modal: FC<ModalProps> = ({
|
const Modal = ({
|
||||||
title,
|
title,
|
||||||
desc,
|
desc,
|
||||||
children,
|
children,
|
||||||
Trigger,
|
Trigger,
|
||||||
CloseTrigger,
|
CloseTrigger,
|
||||||
}) => {
|
}: ModalProps): JSX.Element => {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog.Root onOpenChange={setOpen}>
|
<Dialog.Root onOpenChange={setOpen}>
|
||||||
<Dialog.Trigger asChild>
|
<Dialog.Trigger asChild>
|
||||||
@ -58,7 +60,7 @@ const Modal: FC<ModalProps> = ({
|
|||||||
{children}
|
{children}
|
||||||
|
|
||||||
<Dialog.Close asChild>
|
<Dialog.Close asChild>
|
||||||
{CloseTrigger ? (
|
{CloseTrigger !== undefined ? (
|
||||||
CloseTrigger
|
CloseTrigger
|
||||||
) : (
|
) : (
|
||||||
<Button variant={"secondary"} className="self-end">
|
<Button variant={"secondary"} className="self-end">
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import { FC } from "react";
|
|
||||||
|
|
||||||
interface PageHeadingProps {
|
interface PageHeadingProps {
|
||||||
title: string;
|
title: string;
|
||||||
subtitle?: string;
|
subtitle?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PageHeading: FC<PageHeadingProps> = ({ title, subtitle }) => {
|
const PageHeading = ({ title, subtitle }: PageHeadingProps): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center justify-center px-5">
|
<div className="flex flex-col items-center justify-center px-5">
|
||||||
<h1 className="text-3xl font-bold text-center">{title}</h1>
|
<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>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { FC } from "react";
|
|
||||||
import { FaSpinner } from "react-icons/fa";
|
import { FaSpinner } from "react-icons/fa";
|
||||||
|
|
||||||
const Spinner: FC = () => {
|
const Spinner = (): JSX.Element => {
|
||||||
return <FaSpinner className="animate-spin m-5" />;
|
return <FaSpinner className="animate-spin m-5" />;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
|
/* eslint-disable */
|
||||||
"use client";
|
"use client";
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import * as ToastPrimitive from "@radix-ui/react-toast";
|
import * as ToastPrimitive from "@radix-ui/react-toast";
|
||||||
import { AnimatePresence, motion } from "framer-motion";
|
import { AnimatePresence, motion } from "framer-motion";
|
||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
import Button from "../../Button";
|
import Button from "../../Button";
|
||||||
import { ToastContext } from "../domain/ToastContext";
|
import { ToastContext } from "../domain/ToastContext";
|
||||||
import { ToastVariants } from "../domain/types";
|
import { ToastVariants } from "../domain/types";
|
||||||
@ -11,15 +14,21 @@ import { useToastBuilder } from "../hooks/useToastBuilder";
|
|||||||
export const Toast = ({
|
export const Toast = ({
|
||||||
children,
|
children,
|
||||||
...toastProviderProps
|
...toastProviderProps
|
||||||
}: { children?: ReactNode } & ToastPrimitive.ToastProviderProps) => {
|
}: {
|
||||||
|
children?: ReactNode;
|
||||||
|
} & ToastPrimitive.ToastProviderProps): JSX.Element => {
|
||||||
const { publish, toasts, toggleToast } = useToastBuilder();
|
const { publish, toasts, toggleToast } = useToastBuilder();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ToastPrimitive.Provider {...toastProviderProps}>
|
<ToastPrimitive.Provider {...toastProviderProps}>
|
||||||
<ToastContext.Provider value={{ publish }}>
|
<ToastContext.Provider value={{ publish }}>
|
||||||
{children}
|
{children}
|
||||||
<AnimatePresence mode="popLayout">
|
<AnimatePresence mode="popLayout">
|
||||||
{toasts.map((toast) => {
|
{toasts.map((toast) => {
|
||||||
if (!toast.open) return;
|
if (toast.open !== true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ToastPrimitive.Root
|
<ToastPrimitive.Root
|
||||||
open={toast.open}
|
open={toast.open}
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import * as ToastPrimitive from "@radix-ui/react-toast";
|
import * as ToastPrimitive from "@radix-ui/react-toast";
|
||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
import { Toast } from "./Toast";
|
import { Toast } from "./Toast";
|
||||||
|
|
||||||
export const ToastProvider = ({
|
export const ToastProvider = ({
|
||||||
children,
|
children,
|
||||||
...toastProviderProps
|
...toastProviderProps
|
||||||
}: { children?: ReactNode } & ToastPrimitive.ToastProviderProps) => {
|
}: {
|
||||||
|
children?: ReactNode;
|
||||||
|
} & ToastPrimitive.ToastProviderProps): JSX.Element => {
|
||||||
return <Toast {...toastProviderProps}>{children}</Toast>;
|
return <Toast {...toastProviderProps}>{children}</Toast>;
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { createContext } from "react";
|
import { createContext } from "react";
|
||||||
|
|
||||||
import { ToastPublisher } from "./types";
|
import { ToastPublisher } from "./types";
|
||||||
|
|
||||||
const publish: ToastPublisher = () => void 0;
|
const publish: ToastPublisher = () => void 0;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { VariantProps, cva } from "class-variance-authority";
|
import { cva, VariantProps } from "class-variance-authority";
|
||||||
|
|
||||||
export const ToastVariants = cva(
|
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",
|
"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",
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
export const generateToastUniqueId = () => {
|
export const generateToastUniqueId = (): string => {
|
||||||
const timestamp = Date.now();
|
const timestamp = Date.now();
|
||||||
const random = Math.floor(Math.random() * 10000);
|
const random = Math.floor(Math.random() * 10000);
|
||||||
|
|
||||||
return `${timestamp}-${random}`;
|
return `${timestamp}-${random}`;
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
/* eslint-disable */
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
import { ToastContent, ToastData, ToastPublisher } from "../domain/types";
|
import { ToastContent, ToastData, ToastPublisher } from "../domain/types";
|
||||||
import { generateToastUniqueId } from "../helpers/generateToastUniqueId";
|
import { generateToastUniqueId } from "../helpers/generateToastUniqueId";
|
||||||
|
|
||||||
@ -12,6 +14,7 @@ export const useToastBuilder = () => {
|
|||||||
if (toast.id === toastId) {
|
if (toast.id === toastId) {
|
||||||
toast.open = value;
|
toast.open = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return toast;
|
return toast;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
||||||
import { AnimatePresence, motion } from "framer-motion";
|
import { AnimatePresence, motion } from "framer-motion";
|
||||||
import { FC, ReactNode, useState } from "react";
|
import { ReactNode, useState } from "react";
|
||||||
|
|
||||||
interface TooltipProps {
|
interface TooltipProps {
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
tooltip?: ReactNode;
|
tooltip?: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Tooltip: FC<TooltipProps> = ({ children, tooltip }) => {
|
const Tooltip = ({ children, tooltip }: TooltipProps): JSX.Element => {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TooltipPrimitive.Provider>
|
<TooltipPrimitive.Provider>
|
||||||
<TooltipPrimitive.Root onOpenChange={setOpen} open={open}>
|
<TooltipPrimitive.Root onOpenChange={setOpen} open={open}>
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
|
/* eslint-disable */
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { setEmptyStringsUndefined } from "@/lib/helpers/setEmptyStringsUndefined";
|
|
||||||
import { createContext, useEffect, useState } from "react";
|
import { createContext, useEffect, useState } from "react";
|
||||||
|
|
||||||
|
import { setEmptyStringsUndefined } from "@/lib/helpers/setEmptyStringsUndefined";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getBrainConfigFromLocalStorage,
|
getBrainConfigFromLocalStorage,
|
||||||
saveBrainConfigInLocalStorage,
|
saveBrainConfigInLocalStorage,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable */
|
||||||
import { BrainConfig } from "../types";
|
import { BrainConfig } from "../types";
|
||||||
|
|
||||||
const BRAIN_CONFIG_LOCAL_STORAGE_KEY = "userBrainConfig";
|
const BRAIN_CONFIG_LOCAL_STORAGE_KEY = "userBrainConfig";
|
||||||
@ -12,6 +13,9 @@ export const getBrainConfigFromLocalStorage = (): BrainConfig | undefined => {
|
|||||||
const persistedBrainConfig = localStorage.getItem(
|
const persistedBrainConfig = localStorage.getItem(
|
||||||
BRAIN_CONFIG_LOCAL_STORAGE_KEY
|
BRAIN_CONFIG_LOCAL_STORAGE_KEY
|
||||||
);
|
);
|
||||||
if (persistedBrainConfig === null) return;
|
if (persistedBrainConfig === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return JSON.parse(persistedBrainConfig);
|
return JSON.parse(persistedBrainConfig);
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
/* eslint-disable */
|
||||||
import { useContext } from "react";
|
import { useContext } from "react";
|
||||||
|
|
||||||
import { BrainConfigContext } from "../brain-config-provider";
|
import { BrainConfigContext } from "../brain-config-provider";
|
||||||
|
|
||||||
export const useBrainConfig = () => {
|
export const useBrainConfig = () => {
|
||||||
|
@ -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-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 openAiModels = [
|
||||||
export const openAiPaidModels = ["gpt-3.5-turbo","gpt-3.5-turbo-0613","gpt-3.5-turbo-16k","gpt-4","gpt-4-0613"] as const;
|
"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 = [
|
export const anthropicModels = [
|
||||||
// "claude-v1",
|
// "claude-v1",
|
||||||
@ -39,7 +49,7 @@ export const models = [
|
|||||||
...googleModels,
|
...googleModels,
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export const paidModels = [...openAiPaidModels, ...googleModels] as const;
|
export const paidModels = [...openAiPaidModels] as const;
|
||||||
|
|
||||||
export type PaidModels = (typeof paidModels)[number];
|
export type PaidModels = (typeof paidModels)[number];
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
/* eslint-disable */
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { createContext } from "react";
|
import { createContext } from "react";
|
||||||
|
|
||||||
import useChats from "./hooks/useChats";
|
import useChats from "./hooks/useChats";
|
||||||
import { ChatsState } from "./types";
|
import { ChatsState } from "./types";
|
||||||
|
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
import { useBrainConfig } from "@/lib/context/BrainConfigProvider/hooks/useBrainConfig";
|
/* eslint-disable */
|
||||||
import { useToast } from "@/lib/hooks/useToast";
|
|
||||||
import { useAxios } from "@/lib/useAxios";
|
|
||||||
import { UUID } from "crypto";
|
import { UUID } from "crypto";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
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";
|
import { Chat, ChatMessage } from "../../../types/Chat";
|
||||||
|
|
||||||
export default function useChats() {
|
export default function useChats() {
|
||||||
@ -38,7 +41,9 @@ export default function useChats() {
|
|||||||
|
|
||||||
const fetchChat = useCallback(
|
const fetchChat = useCallback(
|
||||||
async (chatId?: UUID) => {
|
async (chatId?: UUID) => {
|
||||||
if (!chatId) return;
|
if (!chatId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
console.log(`Fetching chat ${chatId}`);
|
console.log(`Fetching chat ${chatId}`);
|
||||||
const response = await axiosInstance.get<Chat>(`/chat/${chatId}`);
|
const response = await axiosInstance.get<Chat>(`/chat/${chatId}`);
|
||||||
@ -64,6 +69,7 @@ export default function useChats() {
|
|||||||
options: Record<string, string | unknown>;
|
options: Record<string, string | unknown>;
|
||||||
}) => {
|
}) => {
|
||||||
fetchAllChats();
|
fetchAllChats();
|
||||||
|
|
||||||
return axiosInstance.post<ChatResponse>(`/chat`, options);
|
return axiosInstance.post<ChatResponse>(`/chat`, options);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -78,7 +84,9 @@ export default function useChats() {
|
|||||||
const sendMessage = async (chatId?: UUID, msg?: ChatMessage) => {
|
const sendMessage = async (chatId?: UUID, msg?: ChatMessage) => {
|
||||||
setIsSendingMessage(true);
|
setIsSendingMessage(true);
|
||||||
|
|
||||||
if (msg) setMessage(msg);
|
if (msg) {
|
||||||
|
setMessage(msg);
|
||||||
|
}
|
||||||
const options: Record<string, unknown> = {
|
const options: Record<string, unknown> = {
|
||||||
chat_id: chatId,
|
chat_id: chatId,
|
||||||
model,
|
model,
|
||||||
@ -101,6 +109,7 @@ export default function useChats() {
|
|||||||
});
|
});
|
||||||
setMessage(["", ""]);
|
setMessage(["", ""]);
|
||||||
setIsSendingMessage(false);
|
setIsSendingMessage(false);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,6 +123,7 @@ export default function useChats() {
|
|||||||
console.log("---- Creating a new chat ----");
|
console.log("---- Creating a new chat ----");
|
||||||
setAllChats((chats) => {
|
setAllChats((chats) => {
|
||||||
console.log({ chats });
|
console.log({ chats });
|
||||||
|
|
||||||
return [...chats, newChat];
|
return [...chats, newChat];
|
||||||
});
|
});
|
||||||
setChat(newChat);
|
setChat(newChat);
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
/* eslint-disable */
|
||||||
import { useContext } from "react";
|
import { useContext } from "react";
|
||||||
|
|
||||||
import { ChatsContext } from "../chats-provider";
|
import { ChatsContext } from "../chats-provider";
|
||||||
|
|
||||||
const useChatsContext = () => {
|
const useChatsContext = () => {
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import { isSpeechRecognitionSupported } from "@/lib/helpers/isSpeechRecognitionSupported";
|
/* eslint-disable */
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
import { isSpeechRecognitionSupported } from "@/lib/helpers/isSpeechRecognitionSupported";
|
||||||
|
|
||||||
import useChatsContext from "./useChatsContext";
|
import useChatsContext from "./useChatsContext";
|
||||||
|
|
||||||
export const useSpeech = () => {
|
export const useSpeech = () => {
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
export function isSpeechRecognitionSupported() {
|
export const isSpeechRecognitionSupported = (): boolean => {
|
||||||
if (
|
if (
|
||||||
typeof window !== "undefined" &&
|
typeof window !== "undefined" &&
|
||||||
("SpeechRecognition" in window || "webkitSpeechRecognition" in window)
|
("SpeechRecognition" in window || "webkitSpeechRecognition" in window)
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
};
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { ToastContext } from "@/lib/components/ui/Toast/domain/ToastContext";
|
/* eslint-disable */
|
||||||
import { useContext } from "react";
|
import { useContext } from "react";
|
||||||
|
|
||||||
|
import { ToastContext } from "@/lib/components/ui/Toast/domain/ToastContext";
|
||||||
|
|
||||||
export const useToast = () => {
|
export const useToast = () => {
|
||||||
const { publish } = useContext(ToastContext);
|
const { publish } = useContext(ToastContext);
|
||||||
|
|
||||||
|
@ -1,20 +1,23 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
import axios, { AxiosInstance } from "axios";
|
||||||
|
|
||||||
import { useSupabase } from "@/app/supabase-provider";
|
import { useSupabase } from "@/app/supabase-provider";
|
||||||
import axios from "axios";
|
|
||||||
import { useBrainConfig } from "./context/BrainConfigProvider/hooks/useBrainConfig";
|
import { useBrainConfig } from "./context/BrainConfigProvider/hooks/useBrainConfig";
|
||||||
|
|
||||||
const axiosInstance = axios.create({
|
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 { session } = useSupabase();
|
||||||
const {
|
const {
|
||||||
config: { backendUrl, openAiKey },
|
config: { backendUrl, openAiKey },
|
||||||
} = useBrainConfig();
|
} = useBrainConfig();
|
||||||
axiosInstance.interceptors.request.clear();
|
axiosInstance.interceptors.request.clear();
|
||||||
axiosInstance.interceptors.request.use(
|
axiosInstance.interceptors.request.use(
|
||||||
async (config) => {
|
(config) => {
|
||||||
config.headers["Authorization"] = "Bearer " + session?.access_token;
|
config.headers["Authorization"] = `Bearer ${session?.access_token}`;
|
||||||
config.headers["Openai-Api-Key"] = openAiKey;
|
config.headers["Openai-Api-Key"] = openAiKey;
|
||||||
config.baseURL = backendUrl ?? config.baseURL;
|
config.baseURL = backendUrl ?? config.baseURL;
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import clsx, { ClassValue } from "clsx";
|
import clsx, { ClassValue } from "clsx";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
export function cn(...inputs: ClassValue[]) {
|
export const cn = (...inputs: ClassValue[]): string => {
|
||||||
return twMerge(clsx(inputs));
|
return twMerge(clsx(inputs));
|
||||||
}
|
};
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { createMiddlewareSupabaseClient } from '@supabase/auth-helpers-nextjs'
|
import { createMiddlewareSupabaseClient } from "@supabase/auth-helpers-nextjs";
|
||||||
import { NextResponse } from 'next/server'
|
import type { NextRequest } from "next/server";
|
||||||
|
import { NextResponse } from "next/server";
|
||||||
import type { NextRequest } from 'next/server'
|
|
||||||
// import type { Database } from '@/lib/database.types'
|
// import type { Database } from '@/lib/database.types'
|
||||||
|
|
||||||
export async function middleware(req: NextRequest) {
|
export const middleware = async (req: NextRequest): Promise<NextResponse> => {
|
||||||
const res = NextResponse.next()
|
const res = NextResponse.next();
|
||||||
const supabase = createMiddlewareSupabaseClient({ req, res })
|
const supabase = createMiddlewareSupabaseClient({ req, res });
|
||||||
await supabase.auth.getSession()
|
await supabase.auth.getSession();
|
||||||
return res
|
|
||||||
}
|
return res;
|
||||||
|
};
|
||||||
|
@ -7,9 +7,12 @@
|
|||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
|
"lint-fix": "eslint --fix .",
|
||||||
"test-type": "tsc --noEmit --emitDeclarationOnly false",
|
"test-type": "tsc --noEmit --emitDeclarationOnly false",
|
||||||
"test": "yarn test-type",
|
"test": "yarn test-type",
|
||||||
"precommit": "yarn lint && yarn test"
|
"precommit": "yarn lint && yarn test",
|
||||||
|
"format-check": "prettier --check .",
|
||||||
|
"format-fix": "prettier --write ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.11.0",
|
"@emotion/react": "^11.11.0",
|
||||||
@ -35,9 +38,11 @@
|
|||||||
"encoding": "^0.1.13",
|
"encoding": "^0.1.13",
|
||||||
"eslint": "^8.41.0",
|
"eslint": "^8.41.0",
|
||||||
"eslint-config-next": "13.4.2",
|
"eslint-config-next": "13.4.2",
|
||||||
|
"eslint-plugin-prefer-arrow": "^1.2.3",
|
||||||
"framer-motion": "^10.12.12",
|
"framer-motion": "^10.12.12",
|
||||||
"next": "13.4.2",
|
"next": "13.4.2",
|
||||||
"postcss": "8.4.23",
|
"postcss": "8.4.23",
|
||||||
|
"prettier": "^2.8.8",
|
||||||
"pretty-bytes": "^6.1.0",
|
"pretty-bytes": "^6.1.0",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
export default function About() {
|
const About = (): JSX.Element => {
|
||||||
return <div>About</div>;
|
return <div>About</div>;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export default About;
|
||||||
|
@ -3,4 +3,4 @@ module.exports = {
|
|||||||
tailwindcss: {},
|
tailwindcss: {},
|
||||||
autoprefixer: {},
|
autoprefixer: {},
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
|
@ -2,25 +2,23 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
darkMode: "class",
|
darkMode: "class",
|
||||||
content: [
|
content: [
|
||||||
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
|
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
'./components/**/*.{js,ts,jsx,tsx,mdx}',
|
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
'./app/**/*.{js,ts,jsx,tsx,mdx}',
|
"./app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
'./lib/**/*.{js,ts,jsx,tsx,mdx}',
|
"./lib/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
],
|
],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
backgroundImage: {
|
backgroundImage: {
|
||||||
'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
|
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
|
||||||
'gradient-conic':
|
"gradient-conic":
|
||||||
'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
|
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
|
||||||
},
|
},
|
||||||
colors: {
|
colors: {
|
||||||
black: "#00121F",
|
black: "#00121F",
|
||||||
primary: "#4F46E5",
|
primary: "#4F46E5",
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [
|
},
|
||||||
require('@tailwindcss/typography'),
|
plugins: [require("@tailwindcss/typography")],
|
||||||
],
|
};
|
||||||
}
|
|
||||||
|
42
frontend/tsconfig.eslint.json
Normal file
42
frontend/tsconfig.eslint.json
Normal 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"]
|
||||||
|
}
|
@ -1,9 +1,10 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"typeRoots": ["node_modules/@types"],
|
"lib": [
|
||||||
"target": "es5",
|
"dom",
|
||||||
"lib": ["dom", "dom.iterable", "esnext"],
|
"dom.iterable",
|
||||||
|
"esnext"
|
||||||
|
],
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
@ -21,10 +22,18 @@
|
|||||||
"name": "next"
|
"name": "next"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"strictNullChecks": true,
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./*"]
|
"@/*": ["./*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
"include": [
|
||||||
"exclude": ["node_modules"]
|
"next-env.d.ts",
|
||||||
|
".next/types/**/*.ts",
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.tsx"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
2088
frontend/yarn.lock
2088
frontend/yarn.lock
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user