added top bar nav

This commit is contained in:
Dhravya 2024-06-02 13:58:40 -05:00
parent 5fe0caabf8
commit 0a282da5b1
9 changed files with 125 additions and 35 deletions

View file

@ -0,0 +1,48 @@
"use server";
import { cookies, headers } from "next/headers";
import { db } from "../helpers/server/db";
import { sessions, users, space } from "../helpers/server/db/schema";
import { eq } from "drizzle-orm";
import { redirect } from "next/navigation";
export async function ensureAuth() {
const token =
cookies().get("next-auth.session-token")?.value ??
cookies().get("__Secure-authjs.session-token")?.value ??
cookies().get("authjs.session-token")?.value ??
headers().get("Authorization")?.replace("Bearer ", "");
if (!token) {
return undefined;
}
const sessionData = await db
.select()
.from(sessions)
.innerJoin(users, eq(users.id, sessions.userId))
.where(eq(sessions.sessionToken, token));
if (!sessionData || sessionData.length < 0) {
return undefined;
}
return {
user: sessionData[0]!.user,
session: sessionData[0]!,
};
}
export async function getSpaces() {
const data = await ensureAuth();
if (!data) {
redirect("/signin");
}
const sp = await db
.select()
.from(space)
.where(eq(space.user, data.user.email));
return sp;
}

View file

@ -7,7 +7,7 @@ import { cn } from "@repo/ui/lib/utils";
import { motion } from "framer-motion";
import { useRouter } from "next/navigation";
function ChatWindow({ q, spaces }: { q: string; spaces: number[] }) {
function ChatWindow({ q }: { q: string }) {
const [layout, setLayout] = useState<"chat" | "initial">("initial");
const router = useRouter();
@ -31,11 +31,7 @@ function ChatWindow({ q, spaces }: { q: string; spaces: number[] }) {
className="max-w-3xl flex mx-auto w-full flex-col"
>
<div className="w-full h-96">
<QueryInput
initialQuery={q}
initialSpaces={spaces ?? []}
disabled
/>
<QueryInput initialQuery={q} initialSpaces={[]} disabled />
</div>
</motion.div>
) : (

View file

@ -1,5 +1,5 @@
import { chatSearchParamsCache } from "../../helpers/lib/searchParams";
import ChatWindow from "./chatWindow";
import { chatSearchParamsCache } from "../../helpers/lib/searchParams";
function Page({
searchParams,
@ -8,7 +8,9 @@ function Page({
}) {
const { firstTime, q, spaces } = chatSearchParamsCache.parse(searchParams);
return <ChatWindow q={q} spaces={spaces ?? []} />;
console.log(spaces);
return <ChatWindow q={q} />;
}
export default Page;

View file

@ -3,6 +3,7 @@ import Image from "next/image";
import Link from "next/link";
import Logo from "../../public/logo.svg";
import { AddIcon, ChatIcon } from "@repo/ui/icons";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@repo/ui/shadcn/tabs";
function Header() {
return (
@ -16,11 +17,32 @@ function Header() {
/>
</Link>
<div className="absolute flex justify-center w-full -z-10">
<button className="bg-secondary all-center h-11 rounded-full p-2 min-w-14">
<Image src={AddIcon} alt="Add icon" />
</button>
</div>
<Tabs
className="absolute flex flex-col justify-center items-center w-full -z-10 group top-0 transition-transform duration-1000 ease-out"
defaultValue="account"
>
<div className="bg-secondary all-center h-11 rounded-full p-2 min-w-14">
<button className="p-2 group-hover:hidden transition duration-500 ease-in-out">
<Image src={AddIcon} alt="Add icon" />
</button>
<div className="hidden group-hover:flex inset-0 transition-opacity duration-500 ease-in-out">
<TabsList className="p-2">
<TabsTrigger value="account">Account</TabsTrigger>
<TabsTrigger value="password">Password</TabsTrigger>
</TabsList>
</div>
</div>
<div className="bg-secondary all-center rounded-full p-2 mt-4 min-w-14 hidden group-hover:block">
<TabsContent value="account">
Make changes to your account here.
</TabsContent>
<TabsContent value="password">
Change your password here.
</TabsContent>
</div>
</Tabs>
<button className="flex shrink-0 duration-200 items-center gap-2 px-2 py-1.5 rounded-xl hover:bg-secondary">
<Image src={ChatIcon} alt="Chat icon" />

View file

@ -3,8 +3,9 @@ import Menu from "../menu";
import Header from "../header";
import QueryInput from "./queryinput";
import { homeSearchParamsCache } from "@/app/helpers/lib/searchParams";
import { getSpaces } from "../actions";
function Page({
async function Page({
searchParams,
}: {
searchParams: Record<string, string | string[] | undefined>;
@ -12,13 +13,15 @@ function Page({
// TODO: use this to show a welcome page/modal
const { firstTime } = homeSearchParamsCache.parse(searchParams);
const spaces = await getSpaces();
return (
<div className="max-w-3xl flex mx-auto w-full flex-col">
{/* all content goes here */}
{/* <div className="">hi {firstTime ? 'first time' : ''}</div> */}
<div className="w-full h-96">
<QueryInput />
<QueryInput initialSpaces={spaces} />
</div>
</div>
);

View file

@ -7,43 +7,45 @@ import Divider from "@repo/ui/shadcn/divider";
import { MultipleSelector, Option } from "@repo/ui/shadcn/combobox";
import { useRouter } from "next/navigation";
const OPTIONS: Option[] = [
{ label: "nextjs", value: "0" },
{ label: "React", value: "1" },
{ label: "Remix", value: "2" },
{ label: "Vite", value: "3" },
{ label: "Nuxt", value: "4" },
{ label: "Vue", value: "5" },
{ label: "Svelte", value: "6" },
{ label: "Angular", value: "7" },
{ label: "Ember", value: "8" },
{ label: "Gatsby", value: "9" },
];
function QueryInput({
initialQuery = "",
initialSpaces = [],
disabled = false,
}: {
initialQuery?: string;
initialSpaces?: number[];
initialSpaces?: { user: string | null; id: number; name: string }[];
disabled?: boolean;
}) {
const [q, setQ] = useState(initialQuery);
const [selectedSpaces, setSelectedSpaces] = useState<number[]>(initialSpaces);
const [selectedSpaces, setSelectedSpaces] = useState<number[]>([]);
const { push } = useRouter();
const parseQ = () => {
// preparedSpaces is list of spaces selected by user, with id and name
const preparedSpaces = initialSpaces
.filter((x) => selectedSpaces.includes(x.id))
.map((x) => {
return {
id: x.id,
name: x.name,
};
});
const newQ =
"/chat?q=" +
encodeURI(q) +
(selectedSpaces ? "&spaces=" + selectedSpaces.join(",") : "");
(selectedSpaces ? "&spaces=" + JSON.stringify(preparedSpaces) : "");
return newQ;
};
const options = initialSpaces.map((x) => ({
label: x.name,
value: x.id.toString(),
}));
return (
<div>
<div className="bg-secondary rounded-t-[24px] w-full mt-40">
@ -81,7 +83,7 @@ function QueryInput({
<div className="flex items-center gap-6 p-2 h-auto bg-secondary rounded-b-[24px]">
<MultipleSelector
disabled={disabled}
defaultOptions={OPTIONS}
defaultOptions={options}
onChange={(e) => setSelectedSpaces(e.map((x) => parseInt(x.value)))}
placeholder="Focus on specific spaces..."
emptyIndicator={

View file

@ -1,8 +1,15 @@
import React from "react";
import Header from "./header";
import Menu from "./menu";
import { ensureAuth } from "./actions";
import { redirect } from "next/navigation";
async function Layout({ children }: { children: React.ReactNode }) {
const info = await ensureAuth();
if (!info) {
return redirect("/signin");
}
function Layout({ children }: { children: React.ReactNode }) {
return (
<main className="h-screen flex flex-col p-4 relative">
<Header />

View file

@ -4,7 +4,9 @@ import {
parseAsString,
parseAsBoolean,
parseAsArrayOf,
parseAsJson,
} from "nuqs/server";
import { z } from "zod";
export const homeSearchParamsCache = createSearchParamsCache({
firstTime: parseAsBoolean.withDefault(false),
@ -13,5 +15,12 @@ export const homeSearchParamsCache = createSearchParamsCache({
export const chatSearchParamsCache = createSearchParamsCache({
firstTime: parseAsBoolean.withDefault(false),
q: parseAsString.withDefault(""),
spaces: parseAsArrayOf(parseAsInteger, ","),
spaces: parseAsArrayOf(
parseAsJson(() =>
z.object({
id: z.string(),
name: z.string(),
}),
),
).withDefault([]),
});

View file

@ -57,6 +57,7 @@
"@radix-ui/react-progress": "^1.0.3",
"@radix-ui/react-scroll-area": "^1.0.5",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-toast": "^1.1.5",
"@types/readline-sync": "^1.4.8",
"ai": "^3.1.14",