Skip to content

Commit

Permalink
v1 make the navbar and input field sticky/fixed
Browse files Browse the repository at this point in the history
  • Loading branch information
git-create-devben committed Jul 24, 2024
1 parent a3df2d9 commit 1368d9a
Show file tree
Hide file tree
Showing 10 changed files with 205 additions and 108 deletions.
2 changes: 1 addition & 1 deletion app/api/gemini/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ async function getLocalServices(query: string, latitude: string, longitude: stri
);
}

return response.data.results.slice(0, 6).map((place: any) => ({
return response.data.results.slice(0, 10).map((place: any) => ({
name: place.name,
address: place.vicinity,
rating: place.rating,
Expand Down
52 changes: 33 additions & 19 deletions app/chat/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { auth } from "@/lib/firebase";
import local from "@/public/png/logo-black.png";
import Image from "next/image";
import FirstVisitPopup from "@/components/firstvisitpopup";
import { SignOut } from "@/lib/signIn";
import { LogOut } from "lucide-react";
export default function Chat() {
const [user, setUser] = useState(auth.currentUser);
const image = user?.photoURL || local;
Expand All @@ -19,29 +21,41 @@ export default function Chat() {
return () => unsubscribe();
});
return (
<main className=" flex h-screen w-[100%] bg-black">
<main className=" flex bg-black">
<FirstVisitPopup />
<div className=" hidden lg:block">
<Sidebar />
</div>
<div className=" bg-[#1212] h-screen flex-1">
<div className="flex flex-col">
<nav className="flex justify-between p-4">
<span
className="text-[#caccce] font-medium text-3xl cursor-pointer"
onClick={() => (window.location.href = "/")}
>
Loca
</span>
<Image
src={image}
alt="user"
className="rounded-full"
width={50}
height={50}
/>
</nav>
<div className="self-center">
<div className=" bg-[#1212] flex-1 min-h-[100vh] pb-[15vh] relative ">
<div className="flex flex-col max-h-[830px] overflow-y-scroll scroll-m-1">
<div className="sticky top-0 w-full shadow-md">
<nav className="flex justify-between p-4 ">
<span
className="text-[#caccce] font-medium text-3xl cursor-pointer"
onClick={() => (window.location.href = "/")}
>
Loca
</span>
<div className="flex gap-6 items-center">
<div
className="flex gap-1 cursor-pointer text-[#ccc] lg:hidden xl:flex"
onClick={SignOut}
>
<LogOut />
<span className="animate-fadeIn xs:hidden">LogOut</span>
</div>
<Image
src={image}
alt="user"
className="rounded-full"
width={50}
height={50}
/>
</div>
</nav>
</div>

<div className="self-center max-w-[900px] m-auto h-screen px-4">
<Main />
</div>
</div>
Expand Down
3 changes: 3 additions & 0 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
@tailwind components;
@tailwind utilities;

:root{
min-height: 100vh;
}
@layer base {
:root {
--background: 0 0% 100%;
Expand Down
10 changes: 5 additions & 5 deletions components/loading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ export function SkeletonCard() {
return (
<div className="flex flex-col space-y-3">
<div className="space-y-2">
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-full max-w-[900px]" />
<Skeleton className="h-4 w-full max-w-[900px]" />
<Skeleton className="h-4 w-full max-w-[900px]" />
<Skeleton className="h-4 w-full max-w-[900px]" />
</div>
<Skeleton className="h-[125px] w-[350px] rounded-xl" />
<Skeleton className="h-[125px] max-w-[900px] rounded-xl" />
</div>
);
}
165 changes: 113 additions & 52 deletions components/main.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { ChangeEvent, useRef } from "react";
import { auth } from "@/lib/firebase";
import { SignOut } from "@/lib/signIn";
import Image from "next/image";
import Image, { StaticImageData } from "next/image";
import local from "@/public/png/logo-black.png";
import { useEffect, useState } from "react";
import Logo from "@/public/png/logo-no-background.png";
Expand All @@ -14,7 +14,9 @@ import { cn } from "@/lib/utils";
import ViewMore from "./viewmore";
import { LocalServiceCard } from "./LocalServiceCard";
import { CardCarousel } from "./CardCarousel";
import { User } from "firebase/auth";

import { ScrollArea } from "@/components/ui/scroll-area";
type Location = {
latitude: number | null;
longitude: number | null;
Expand Down Expand Up @@ -249,51 +251,112 @@ const Main: React.FC = () => {
setUserMessage(e.target.value);
};
return (
<main className="flex flex-col overflow-auto">
{/* center */}
<header className="w-full max-w-4xl h-full max-h-[51rem] pb-20">
<div className="text-white flex-1 flex-col gap-12 px-6 ">
{conversation.length === 0 ? (
<main className="">
<div className="">
{conversation.length === 0 ? (
<DefaultChatPage user={user?.displayName?.slice(0, 3) || "Dev"} />
) : (
conversation.map((message, index) => (
<>
<div className="text-[#c4c7c556] lg:text-6xl text-4xl font-semibold flex flex-col self-auto">
<h1 className="bg-clip-text text-transparent bg-gradient-to-r from-[#4b90ff] from-1% via-blue-600 via-5% to-15% to-[#ff5546]">
Hello {user?.displayName?.slice(0, 3) || "Dev"}
</h1>
<p>What can I find for you today?</p>
</div>
<div className="mt-20">
<CardCarousel />
</div>
</>
) : (
conversation.map((message, index) => (
<div
<ChatPage
key={index}
className="flex flex-col lg:flex-row lg:items-center gap-4 mb-8"
>
<Image
src={message.sender === "user" ? image : Logo}
alt={message.sender === "user" ? "user" : "Loca AI image"}
width={50}
height={50}
className={cn(
message.sender === "user" ? "" : "",
"self-start rounded-full"
)}
/>
<p className="text-white " onCopy={(e) => !!e}>
{message.text}
</p>
<div ref={conversationEndRef} />
</div>
))
)}
message={message}
index={index}
image={image}
logo={Logo}
isLoading={isLoading}
conversationEndRef={conversationEndRef}
/>

</>
))
)}
{isLoading && <SkeletonCard />}
</div>
<ChatInbox
locationError={locationError}
isProcessing={isProcessing}
handleInput={handleInput}
textareaRef={textareaRef}
userMessage={userMessage}
handleSendMessage={handleSendMessage}
setManualLocation={setManualLocation}
manualLocation={manualLocation}
/>
</main>
);
};

export default Main;

{isLoading && <SkeletonCard />}
const DefaultChatPage = ({ user }: { user: string }) => {
return (
<main>
<div className="text-[#c4c7c556] lg:text-6xl text-4xl font-semibold flex flex-col self-auto">
<h1 className="bg-clip-text text-transparent bg-gradient-to-r from-[#4b90ff] from-1% via-blue-600 via-5% to-15% to-[#ff5546]">
Hello {user}
</h1>
<p>What can I find for you today?</p>
</div>
<div className="mt-20">
<CardCarousel />
</div>
</main>
);
};

const ChatPage: React.FC<ChatPageProps> = ({
message,
index,
image,
logo,
isLoading,

conversationEndRef,
}) => {
return (
<ScrollArea className="">
<main className=" w-full max-w-[900px] m-auto">
<div
key={index}
className="flex flex-col lg:flex-row lg:items-center gap-4 mb-8"
>
<Image
src={message.sender === "user" ? image : logo}
alt={message.sender === "user" ? "user" : "Loca AI image"}
width={50}
height={50}
className={cn(
message.sender === "user" ? "" : "",
"self-start rounded-full"
)}
/>
<p className="text-white " onCopy={(e) => !!e}>
{message.text}
</p>
<div ref={conversationEndRef} />
</div>
{/* footer */}
</header>
<div className=" fixed bg-black/80 bottom-0 w-full self-center max-w-[63rem] p-4">
{/*
{isLoading && <SkeletonCard />} */}
</main>
</ScrollArea>
);
};

const ChatInbox: React.FC<ChatInboxProps> = ({
manualLocation,
setManualLocation,
textareaRef,
userMessage,
handleInput,
isProcessing,
handleSendMessage,
locationError,
}) => {
return (
<main className="fixed bottom-0 py-4 px-4 left-[50%] right-[50%] transform -translate-x-1/2 bg-black shadow-2xl w-full max-w-[900px] m-auto">
{/* footer */}
<div className="">
{locationError && (
<div className="mb-2">
<p className="text-red-500 mb-1">{locationError}</p>
Expand All @@ -306,7 +369,7 @@ const Main: React.FC = () => {
/>
</div>
)}
<div className="relative flex items-center gap-2 w-full rounded-md bg-[#1e1f20] p-3">
<div className="relative flex items-center gap-2 w-full rounded-md bg-[#1e1f20] p-3">
<textarea
ref={textareaRef}
value={userMessage}
Expand All @@ -329,16 +392,14 @@ const Main: React.FC = () => {
onClick={() => !isProcessing && handleSendMessage()}
/>
</div>
<p className="text-[#ccc] text-xs text-center mt-2">
<b>LOCA</b> use your input to fetch service. So long text will make
<b>LOCA</b> response to be inaccurate so let your input be
minimalistic to be able to get accurate services/response{" "}
</p>
</div>
<div
className="flex gap-2 cursor-pointer absolute top-6 right-28 text-[#ccc] lg:hidden"
onClick={SignOut}
>
<LogOut />
<span className="animate-fadeIn xs:hidden">LogOut</span>
</div>


</main>
);
};

export default Main;
2 changes: 1 addition & 1 deletion components/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const Sidebar = () => {
return (
<aside
className={cn(
`bg-[#1e1f20] h-screen ${
`bg-[#1e1f20] min-h-[100vh] inline-flex ${
extend ? "w-72 p-8" : "w-16 p-4 items-center"
} flex flex-col justify-between transition-all duration-500 ease-in-out`
)}
Expand Down
2 changes: 1 addition & 1 deletion components/viewmore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const ViewMore: React.FC<ViewMoreProps> = ({ data }) => {
<main>
<Sheet>
<SheetTrigger>View More</SheetTrigger>
<SheetContent className="bg-[#1e1f20] text-white border-none">
<SheetContent className="bg-[#1e1f20] text-white border-none overflow-auto">
<SheetHeader className="mt-5">
<SheetTitle className="text-white text-md">These are the Rest of Services find near you.</SheetTitle>
<SheetDescription>
Expand Down
2 changes: 1 addition & 1 deletion middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { auth } from './lib/firebase';
export function middleware(req: NextRequest) {
const res = NextResponse.next();
const token = req.cookies.get('token');
console.log('token', token);
// console.log('token', token);

if (!token && req.nextUrl.pathname === '/chat') {
// Redirect unauthorized users trying to access /chat
Expand Down
47 changes: 47 additions & 0 deletions types/types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@


interface ServiceItem {
name: string;
address: string;
rating: number;
user_ratings_total: number;
place_id: string;
}

interface LocalServiceCardProps {
name: string;
address: string;
rating: number;
user_ratings_total: number;
place_id: string;
}

type ConversationItem = {
sender: string;
text: React.ReactNode;
};

interface ViewMoreProps {
data: ServiceItem[];
}

type ChatPageProps = {
message: ConversationItem;
index: number;
image: StaticImageData | string;
logo: StaticImageData;
isLoading: boolean;

conversationEndRef: React.RefObject<HTMLDivElement>;
};

interface ChatInboxProps {
locationError: string | null;
manualLocation: string;
textareaRef: React.RefObject<HTMLTextAreaElement>;
userMessage: string;
handleInput: (e: ChangeEvent<HTMLTextAreaElement>) => void;
isProcessing: boolean;
handleSendMessage: () => Promise<void>;
setManualLocation: React.Dispatch<React.SetStateAction<string>>;
}
Loading

0 comments on commit 1368d9a

Please sign in to comment.