Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added public/profile.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/profile2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
74 changes: 74 additions & 0 deletions src/app/chat/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
"use client";
import { useState } from "react";
import ChatSidebar from "@/components/chat/ChatSidebar";
import ChatWindow from "@/components/chat/ChatWindow";
import { Profile } from "@/lib/types";

const Chat = () => {
const profiles: Profile[] = [
{
id: "1",
name: "Sarah Johnson",
image: "/profile.png",
status: "online",
lastMessage: "Hey! How are you doing today?",
time: "2:30 PM",
unread: 2,
},
{
id: "2",
name: "Mike Chen",
image: "/profile2.png",
status: "online",
lastMessage: "Can we schedule a meeting?",
time: "1:45 PM",
},
{
id: "3",
name: "Emma Wilson",
image: "/profile2.png",
status: "offline",
lastMessage: "Thanks for the help!",
time: "11:20 AM",
},
];
const [activeProfile, setActiveProfile] = useState<string>("");
const [sidebarOpen, setSidebarOpen] = useState(false);

const selectedProfile = profiles.find((p) => p.id === activeProfile);

return (
<div className="h-screen bg-[#070312] flex flex-col md:flex-row">
{/* Sidebar Drawer for mobile */}
<div
className={`fixed inset-0 z-40 bg-black bg-opacity-40 transition-opacity md:hidden ${
sidebarOpen ? "block" : "hidden"
}`}
onClick={() => setSidebarOpen(false)}
/>
<div
className={`fixed inset-y-0 left-0 z-50 w-64 bg-[#0A0517] border-r border-gray-800 transform transition-transform duration-300 md:static md:translate-x-0 md:w-80 md:flex md:flex-col ${
sidebarOpen ? "translate-x-0" : "-translate-x-full"
} flex flex-col`}
>
<ChatSidebar
profiles={profiles}
activeProfile={activeProfile}
onProfileSelect={(id) => {
setActiveProfile(id);
setSidebarOpen(false);
}}
/>
</div>
{/* Main Chat Window */}
<div className="flex-1 flex flex-col min-w-0">
<ChatWindow
activeProfile={selectedProfile}
onSidebarToggle={() => setSidebarOpen(true)}
/>
</div>
</div>
);
};

export default Chat;
23 changes: 23 additions & 0 deletions src/components/chat/ChatSidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Profile } from "@/lib/types";
import SearchBar from "./chatSidebar/SearchBar";
import ProfileList from "./chatSidebar/ProfileList";

const ChatSidebar: React.FC<{
profiles: Profile[];
activeProfile: string;
onProfileSelect: (id: string) => void;
}> = ({ profiles, activeProfile, onProfileSelect }) => (
<div className="h-full flex flex-col border-r border-gray-800 w-full md:w-80 bg-[#0A0517]">
<div className="p-4 border-b border-gray-800 flex items-center justify-center">
<h1 className="text-white text-xl text-center font-semibold">Chats</h1>
</div>
<SearchBar />
<ProfileList
profiles={profiles}
activeProfile={activeProfile}
onProfileSelect={onProfileSelect}
/>
</div>
);

export default ChatSidebar;
17 changes: 17 additions & 0 deletions src/components/chat/ChatWindow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import ChatHeader from "./chatWindow/ChatHeader";
import MessageArea from "./chatWindow/MessageArea";
import MessageInput from "./chatWindow/MessageInput";
import { Profile } from "@/lib/types";

const ChatWindow: React.FC<{
activeProfile?: Profile;
onSidebarToggle?: () => void;
}> = ({ activeProfile, onSidebarToggle }) => (
<div className="flex-1 flex flex-col">
<ChatHeader profile={activeProfile} onSidebarToggle={onSidebarToggle} />
<MessageArea />
<MessageInput />
</div>
);

export default ChatWindow;
42 changes: 42 additions & 0 deletions src/components/chat/chatSidebar/ProfileItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Profile } from "@/lib/types";
import Image from "next/image";

const ProfileItem: React.FC<{
profile: Profile;
isActive: boolean;
onClick: () => void;
}> = ({ profile, isActive, onClick }) => (
<div
onClick={onClick}
className={`flex items-center px-2 sm:px-4 py-2 sm:py-3 cursor-pointer transition-colors ${
isActive ? "bg-[#160A36]" : "hover:bg-[#160A36]"
}`}
>
<div className="relative">
<div className="w-10 h-10 sm:w-12 sm:h-12 rounded-full overflow-hidden flex items-center justify-center text-lg sm:text-xl">
<Image src={profile.image} alt={profile.name} width={48} height={48} />
</div>
{profile.status === "online" && (
<div className="absolute -bottom-1 -right-1 w-3 h-3 sm:w-4 sm:h-4 bg-green-500 rounded-full border-2 border-gray-900"></div>
)}
</div>
<div className="ml-2 sm:ml-3 flex-1 min-w-0">
<div className="flex items-center justify-between">
<h3 className="text-white font-medium truncate text-sm sm:text-base">
{profile.name}
</h3>
<span className="text-gray-400 text-xs sm:text-sm">{profile.time}</span>
</div>
<p className="text-gray-400 text-xs sm:text-sm truncate">
{profile.lastMessage}
</p>
</div>
{profile.unread && (
<div className="ml-2 bg-blue-500 text-white text-xs rounded-full w-5 h-5 flex items-center justify-center">
{profile.unread}
</div>
)}
</div>
);

export default ProfileItem;
21 changes: 21 additions & 0 deletions src/components/chat/chatSidebar/ProfileList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Profile } from "@/lib/types";
import ProfileItem from "./ProfileItem";

const ProfileList: React.FC<{
profiles: Profile[];
activeProfile: string;
onProfileSelect: (id: string) => void;
}> = ({ profiles, activeProfile, onProfileSelect }) => (
<div className="flex-1 overflow-y-auto min-h-0">
{profiles.map((profile) => (
<ProfileItem
key={profile.id}
profile={profile}
isActive={activeProfile === profile.id}
onClick={() => onProfileSelect(profile.id)}
/>
))}
</div>
);

export default ProfileList;
20 changes: 20 additions & 0 deletions src/components/chat/chatSidebar/SearchBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from "react";
import { Search } from "lucide-react";

const SearchBar = () => {
return (
<div className="relative px-2 sm:px-4 py-2 sm:py-3">
<div className="relative ">
<input
type="text"
placeholder="Search"
className="w-full pl-10 pr-4 py-2 text-white rounded-lg border border-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-sm sm:text-base"
/>

<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
</div>
</div>
);
};

export default SearchBar;
56 changes: 56 additions & 0 deletions src/components/chat/chatWindow/ChatHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { Phone, Video, Search, Menu } from "lucide-react";
import { Profile } from "@/lib/types";
import Image from "next/image";

const ChatHeader: React.FC<{
profile?: Profile;
onSidebarToggle?: () => void;
}> = ({ profile, onSidebarToggle }) => (
<div className="bg-[#120C22] px-4 sm:px-6 py-4 flex items-center justify-between border-b border-gray-700">
<div className="flex items-center">
{/* Hamburger menu for mobile */}
{onSidebarToggle && (
<button
className="md:hidden mr-3 text-gray-400 hover:text-white transition-colors"
onClick={onSidebarToggle}
aria-label="Open sidebar"
>
<Menu className="w-6 h-6" />
</button>
)}
{profile && (
<>
<div className="w-10 h-10 rounded-full overflow-hidden flex items-center justify-center text-lg">
<Image
src={profile.image}
alt={profile.name}
width={40}
height={40}
/>
</div>
<div className="ml-3">
<h2 className="text-white font-medium text-base sm:text-lg md:text-xl">
{profile.name}
</h2>
<p className="text-gray-400 text-xs sm:text-sm">
{profile.status === "online" ? "Online" : "Last seen recently"}
</p>
</div>
</>
)}
</div>
<div className="flex items-center space-x-4">
<button className="text-gray-400 hover:text-white transition-colors">
<Search className="w-5 h-5" />
</button>
<button className="text-gray-400 hover:text-white transition-colors">
<Phone className="w-5 h-5" />
</button>
<button className="text-gray-400 hover:text-white transition-colors">
<Video className="w-5 h-5" />
</button>
</div>
</div>
);

export default ChatHeader;
15 changes: 15 additions & 0 deletions src/components/chat/chatWindow/MessageArea.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const MessageArea: React.FC = () => (
<div className="flex-1 bg-blue-600 flex items-center justify-center px-2 sm:px-4">
<div className="text-center">
<div className="text-white text-4xl sm:text-6xl mb-2 sm:mb-4">πŸ’¬</div>
<h3 className="text-white text-lg sm:text-xl font-medium mb-1 sm:mb-2">
Start a conversation
</h3>
<p className="text-blue-100 text-xs sm:text-base">
Select a chat to start messaging
</p>
</div>
</div>
);

export default MessageArea;
27 changes: 27 additions & 0 deletions src/components/chat/chatWindow/MessageInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Smile, Paperclip } from "lucide-react";
import SendGradient from "./SendGradient";

const MessageInput: React.FC = () => (
<div className="bg-[#120C22] px-2 sm:px-6 py-3 sm:py-4 border-t border-gray-700">
<div className="flex items-center space-x-2 sm:space-x-4">
<button className="text-gray-400 hover:text-white transition-colors">
<Paperclip className="w-5 h-5" />
</button>
<div className="flex-1 relative">
<input
type="text"
placeholder="Type a message..."
className="w-full px-3 sm:px-4 py-2 bg-[#241745] text-white rounded-lg border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-sm sm:text-base"
/>
<button className="absolute right-2 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-white transition-colors">
<Smile className="w-5 h-5" />
</button>
</div>
<button className="w-5 h-5">
<SendGradient className="w-5 h-5" />
</button>
</div>
</div>
);

export default MessageInput;
35 changes: 35 additions & 0 deletions src/components/chat/chatWindow/SendGradient.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from "react";

const SendGradient = ({ className = "w-5 h-5" }) => (
<svg
className={className}
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<defs>
<linearGradient id="send-gradient" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="#FF2BB1" />
<stop offset="100%" stopColor="#6C45E4" />
</linearGradient>
</defs>
<path
d="M22 2L11 13"
stroke="url(#send-gradient)"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M22 2L15 22L11 13L2 9L22 2Z"
stroke="url(#send-gradient)"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);

export default SendGradient;
3 changes: 3 additions & 0 deletions src/generated/prisma/client.js
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@

/* !!! This is code generated by Prisma. Do not edit directly. !!!
/* eslint-disable */
module.exports = { ...require('.') }
3 changes: 3 additions & 0 deletions src/generated/prisma/default.js
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@

/* !!! This is code generated by Prisma. Do not edit directly. !!!
/* eslint-disable */
module.exports = { ...require('.') }
Loading
Loading