315 lines
11 KiB
TypeScript
315 lines
11 KiB
TypeScript
"use client";
|
|
|
|
import * as React from "react";
|
|
import { ArchiveX, Command, File, Inbox, Send, Trash2 } from "lucide-react";
|
|
|
|
import { NavUser } from "@/features/dashboard/components/nav-user";
|
|
import { Label } from "@/components/ui/label";
|
|
import {
|
|
Sidebar,
|
|
SidebarContent,
|
|
SidebarFooter,
|
|
SidebarGroup,
|
|
SidebarGroupContent,
|
|
SidebarHeader,
|
|
SidebarInput,
|
|
SidebarMenu,
|
|
SidebarMenuButton,
|
|
SidebarMenuItem,
|
|
useSidebar,
|
|
} from "@/components/ui/sidebar";
|
|
import { Switch } from "@/components/ui/switch";
|
|
import { Separator } from "@/components/ui/separator";
|
|
|
|
// This is sample data
|
|
const data = {
|
|
user: {
|
|
name: "shadcn",
|
|
email: "m@example.com",
|
|
avatar: "/avatars/shadcn.jpg",
|
|
},
|
|
navMain: [
|
|
{
|
|
title: "Inbox",
|
|
url: "#",
|
|
icon: Inbox,
|
|
isActive: true,
|
|
},
|
|
{
|
|
title: "Drafts",
|
|
url: "#",
|
|
icon: File,
|
|
isActive: false,
|
|
},
|
|
{
|
|
title: "Sent",
|
|
url: "#",
|
|
icon: Send,
|
|
isActive: false,
|
|
},
|
|
{
|
|
title: "Junk",
|
|
url: "#",
|
|
icon: ArchiveX,
|
|
isActive: false,
|
|
},
|
|
{
|
|
title: "Trash",
|
|
url: "#",
|
|
icon: Trash2,
|
|
isActive: false,
|
|
},
|
|
],
|
|
mails: [
|
|
{
|
|
name: "William Smith",
|
|
email: "williamsmith@example.com",
|
|
subject: "Meeting Tomorrow",
|
|
date: "09:34 AM",
|
|
teaser:
|
|
"Hi team, just a reminder about our meeting tomorrow at 10 AM.\nPlease come prepared with your project updates.",
|
|
},
|
|
{
|
|
name: "Alice Smith",
|
|
email: "alicesmith@example.com",
|
|
subject: "Re: Project Update",
|
|
date: "Yesterday",
|
|
teaser:
|
|
"Thanks for the update. The progress looks great so far.\nLet's schedule a call to discuss the next steps.",
|
|
},
|
|
{
|
|
name: "Bob Johnson",
|
|
email: "bobjohnson@example.com",
|
|
avatar: "https://ui.shadcn.com/avatars/03.png",
|
|
subject: "Weekend Plans",
|
|
date: "2 days ago",
|
|
teaser:
|
|
"Hey everyone! I'm thinking of organizing a team outing this weekend.\nWould you be interested in a hiking trip or a beach day?",
|
|
},
|
|
{
|
|
name: "Emily Davis",
|
|
email: "emilydavis@example.com",
|
|
avatar: "https://ui.shadcn.com/avatars/04.png",
|
|
subject: "Re: Question about Budget",
|
|
date: "2 days ago",
|
|
teaser:
|
|
"I've reviewed the budget numbers you sent over.\nCan we set up a quick call to discuss some potential adjustments?",
|
|
},
|
|
{
|
|
name: "Michael Wilson",
|
|
email: "michaelwilson@example.com",
|
|
avatar: "https://ui.shadcn.com/avatars/05.png",
|
|
subject: "Important Announcement",
|
|
date: "1 week ago",
|
|
teaser:
|
|
"Please join us for an all-hands meeting this Friday at 3 PM.\nWe have some exciting news to share about the company's future.",
|
|
},
|
|
{
|
|
name: "Sarah Brown",
|
|
email: "sarahbrown@example.com",
|
|
avatar: "https://ui.shadcn.com/avatars/06.png",
|
|
subject: "Re: Feedback on Proposal",
|
|
date: "1 week ago",
|
|
teaser:
|
|
"Thank you for sending over the proposal. I've reviewed it and have some thoughts.\nCould we schedule a meeting to discuss my feedback in detail?",
|
|
},
|
|
{
|
|
name: "David Lee",
|
|
email: "davidlee@example.com",
|
|
avatar: "https://ui.shadcn.com/avatars/07.png",
|
|
subject: "New Project Idea",
|
|
date: "1 week ago",
|
|
teaser:
|
|
"I've been brainstorming and came up with an interesting project concept.\nDo you have time this week to discuss its potential impact and feasibility?",
|
|
},
|
|
{
|
|
name: "Olivia Wilson",
|
|
email: "oliviawilson@example.com",
|
|
avatar: "https://ui.shadcn.com/avatars/08.png",
|
|
subject: "Vacation Plans",
|
|
date: "1 week ago",
|
|
teaser:
|
|
"Just a heads up that I'll be taking a two-week vacation next month.\nI'll make sure all my projects are up to date before I leave.",
|
|
},
|
|
{
|
|
name: "James Martin",
|
|
email: "jamesmartin@example.com",
|
|
avatar: "https://ui.shadcn.com/avatars/09.png",
|
|
subject: "Re: Conference Registration",
|
|
date: "1 week ago",
|
|
teaser:
|
|
"I've completed the registration for the upcoming tech conference.\nLet me know if you need any additional information from my end.",
|
|
},
|
|
{
|
|
name: "Sophia White",
|
|
email: "sophiawhite@example.com",
|
|
avatar: "https://ui.shadcn.com/avatars/10.png",
|
|
subject: "Team Dinner",
|
|
date: "1 week ago",
|
|
teaser:
|
|
"To celebrate our recent project success, I'd like to organize a team dinner.\nAre you available next Friday evening? Please let me know your preferences.",
|
|
},
|
|
],
|
|
};
|
|
|
|
export interface Mail {
|
|
name: string;
|
|
email: string;
|
|
avatar?: string;
|
|
subject: string;
|
|
date: string;
|
|
teaser: string;
|
|
}
|
|
|
|
interface AppSidebarProps {
|
|
onSelectMail?: (mail: Mail) => void;
|
|
selectedMailId?: string;
|
|
}
|
|
|
|
export function AppSidebar({ onSelectMail, selectedMailId }: AppSidebarProps) {
|
|
const [mails, setMails] = React.useState(data.mails.slice(0, 5));
|
|
const [activeItem, setActiveItem] = React.useState(data.navMain[0]);
|
|
|
|
// Apply font to the entire component
|
|
React.useEffect(() => {
|
|
document.documentElement.classList.add('font-["Overpass"]');
|
|
}, []);
|
|
|
|
const { setOpen: setSidebarOpen } = useSidebar();
|
|
|
|
return (
|
|
<Sidebar
|
|
collapsible="icon"
|
|
className="overflow-hidden *:data-[sidebar=sidebar]:flex-row"
|
|
>
|
|
{/* This is the first sidebar */}
|
|
{/* We disable collapsible and adjust width to icon. */}
|
|
{/* This will make the sidebar appear as icons. */}
|
|
<Sidebar
|
|
collapsible="none"
|
|
className="w-[calc(var(--sidebar-width-icon)+1px)]! border-r bg-white font-['Overpass']"
|
|
>
|
|
<SidebarHeader>
|
|
<div className="h-2 bg-gradient-to-r from-purple-500 to-pink-400 w-full"></div>
|
|
<SidebarMenu>
|
|
<SidebarMenuItem>
|
|
<SidebarMenuButton size="lg" asChild className="md:h-10 md:p-2">
|
|
<a href="#">
|
|
<div className="bg-gradient-to-r from-purple-500 to-pink-400 text-white flex aspect-square size-8 items-center justify-center rounded-lg shadow-md">
|
|
<Command className="size-4" />
|
|
</div>
|
|
<div className="grid flex-1 text-left text-sm leading-tight">
|
|
<span className="truncate font-medium text-purple-700">
|
|
Serenity Space
|
|
</span>
|
|
<span className="truncate text-xs text-gray-600">
|
|
Dashboard
|
|
</span>
|
|
</div>
|
|
</a>
|
|
</SidebarMenuButton>
|
|
</SidebarMenuItem>
|
|
</SidebarMenu>
|
|
</SidebarHeader>
|
|
<SidebarContent>
|
|
<SidebarGroup>
|
|
<SidebarGroupContent className="px-1.5 md:px-0">
|
|
<SidebarMenu>
|
|
{data.navMain.map((item) => (
|
|
<SidebarMenuItem key={item.title}>
|
|
<SidebarMenuButton
|
|
tooltip={{
|
|
children: item.title,
|
|
hidden: false,
|
|
}}
|
|
onClick={() => {
|
|
setActiveItem(item);
|
|
const mail = data.mails.sort(() => Math.random() - 0.5);
|
|
setMails(
|
|
mail.slice(
|
|
0,
|
|
Math.max(5, Math.floor(Math.random() * 10) + 1)
|
|
)
|
|
);
|
|
setSidebarOpen(true);
|
|
}}
|
|
isActive={activeItem?.title === item.title}
|
|
className={`px-2.5 md:px-2 rounded-md transition-all duration-300 ${activeItem?.title === item.title ? "bg-purple-100 text-purple-700" : "hover:bg-gray-100"}`}
|
|
>
|
|
<item.icon
|
|
className={`${activeItem?.title === item.title ? "text-purple-700" : "text-gray-600"}`}
|
|
/>
|
|
<span className=" font-medium">{item.title}</span>
|
|
</SidebarMenuButton>
|
|
</SidebarMenuItem>
|
|
))}
|
|
</SidebarMenu>
|
|
</SidebarGroupContent>
|
|
</SidebarGroup>
|
|
</SidebarContent>
|
|
<SidebarFooter>
|
|
<NavUser user={data.user} />
|
|
</SidebarFooter>
|
|
</Sidebar>
|
|
|
|
{/* This is the second sidebar */}
|
|
{/* We disable collapsible and let it fill remaining space */}
|
|
<Sidebar
|
|
collapsible="none"
|
|
className="hidden flex-1 md:flex font-['Overpass'] bg-white"
|
|
>
|
|
<SidebarHeader className="gap-3.5 border-b p-4">
|
|
<div className="flex w-full items-center justify-between">
|
|
<div className="text-purple-700 text-base font-medium">
|
|
{activeItem?.title}
|
|
</div>
|
|
<Label className="flex items-center gap-2 text-sm ">
|
|
<span>Unreads</span>
|
|
<Switch className="shadow-none data-[state=checked]:bg-purple-600" />
|
|
</Label>
|
|
</div>
|
|
<SidebarInput
|
|
placeholder="Type to search..."
|
|
className="rounded-md border-gray-300 focus:border-purple-400 focus:ring focus:ring-purple-200 focus:ring-opacity-50 transition-all duration-300"
|
|
/>
|
|
<Separator className="my-2 bg-gray-200" />
|
|
</SidebarHeader>
|
|
<SidebarContent>
|
|
<SidebarGroup className="px-0">
|
|
<SidebarGroupContent>
|
|
{mails.map((mail, index) => (
|
|
<button
|
|
key={index}
|
|
onClick={() => onSelectMail && onSelectMail(mail)}
|
|
className={`hover:bg-purple-50 transition-all duration-300 flex flex-col items-start gap-2 border-b p-4 text-sm leading-tight whitespace-nowrap last:border-b-0 w-full text-left ${mail.email + mail.subject === selectedMailId ? "bg-purple-50" : ""}`}
|
|
>
|
|
<div className="flex w-full items-center gap-2">
|
|
<div className="h-8 w-8 rounded-md bg-gradient-to-r from-purple-500 to-pink-400 text-white mr-2 flex-shrink-0 flex items-center justify-center">
|
|
<span className=" font-medium text-sm">
|
|
{mail.name
|
|
.split(" ")
|
|
.map((n) => n[0])
|
|
.join("")}
|
|
</span>
|
|
</div>
|
|
<span className=" text-gray-700">{mail.name}</span>{" "}
|
|
<span className="ml-auto text-xs text-gray-500">
|
|
{mail.date}
|
|
</span>
|
|
</div>
|
|
<span className=" font-medium text-purple-700">
|
|
{mail.subject}
|
|
</span>
|
|
<span className="line-clamp-2 w-[260px] text-xs text-gray-600 whitespace-break-spaces">
|
|
{mail.teaser}
|
|
</span>
|
|
</button>
|
|
))}
|
|
</SidebarGroupContent>
|
|
</SidebarGroup>
|
|
</SidebarContent>
|
|
</Sidebar>
|
|
</Sidebar>
|
|
);
|
|
}
|