-
Notifications
You must be signed in to change notification settings - Fork 275
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
18 changed files
with
2,731 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { cookies } from "next/headers"; | ||
import Image from "next/image"; | ||
import { redirect } from "next/navigation"; | ||
|
||
import { authOptions } from "@/lib/auth"; | ||
import { getCurrentUser } from "@/lib/session"; | ||
import { isValidJSONString } from "@/lib/utils"; | ||
import { CategoriesDashboard } from "@/components/categories/components/categories-dashboard"; | ||
import { accounts, mails } from "@/components/investments/data"; | ||
|
||
export const metadata = { | ||
title: "Transactions", | ||
description: "Transactions description", | ||
}; | ||
|
||
export default async function DashboardPage() { | ||
const user = await getCurrentUser(); | ||
|
||
if (!user) { | ||
redirect(authOptions?.pages?.signIn || "/login"); | ||
} | ||
|
||
const layout = cookies().get("react-resizable-panels:layout"); | ||
const collapsed = cookies().get("react-resizable-panels:collapsed"); | ||
|
||
const defaultLayout = layout ? JSON.parse(layout.value) : undefined; | ||
const defaultCollapsed = collapsed ? JSON.parse(collapsed.value) : undefined; | ||
|
||
return ( | ||
<> | ||
<div className="md:hidden"> | ||
<Image | ||
src="/examples/mail-dark.png" | ||
width={1280} | ||
height={727} | ||
alt="Mail" | ||
className="hidden dark:block" | ||
/> | ||
<Image | ||
src="/examples/mail-light.png" | ||
width={1280} | ||
height={727} | ||
alt="Mail" | ||
className="block dark:hidden" | ||
/> | ||
</div> | ||
<div className="hidden flex-col md:flex"> | ||
<CategoriesDashboard | ||
accounts={accounts} | ||
mails={mails} | ||
defaultLayout={defaultLayout} | ||
defaultCollapsed={defaultCollapsed} | ||
navCollapsedSize={4} | ||
/> | ||
</div> | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
"use client"; | ||
|
||
import * as React from "react"; | ||
|
||
import { cn } from "@/lib/utils"; | ||
import { | ||
Select, | ||
SelectContent, | ||
SelectItem, | ||
SelectTrigger, | ||
SelectValue, | ||
} from "@/components/ui/select"; | ||
|
||
interface AccountSwitcherProps { | ||
isCollapsed: boolean; | ||
accounts: { | ||
label: string; | ||
email: string; | ||
icon: React.ReactNode; | ||
}[]; | ||
} | ||
|
||
export function AccountSwitcher({ | ||
isCollapsed, | ||
accounts, | ||
}: AccountSwitcherProps) { | ||
const [selectedAccount, setSelectedAccount] = React.useState<string>( | ||
accounts[0].email, | ||
); | ||
|
||
return ( | ||
<Select defaultValue={selectedAccount} onValueChange={setSelectedAccount}> | ||
<SelectTrigger | ||
className={cn( | ||
"flex items-center gap-2 [&>span]:line-clamp-1 [&>span]:flex [&>span]:w-full [&>span]:items-center [&>span]:gap-1 [&>span]:truncate [&_svg]:h-4 [&_svg]:w-4 [&_svg]:shrink-0", | ||
isCollapsed && | ||
"flex h-9 w-9 shrink-0 items-center justify-center p-0 [&>span]:w-auto [&>svg]:hidden", | ||
)} | ||
aria-label="Select account" | ||
> | ||
<SelectValue placeholder="Select an account"> | ||
{accounts.find((account) => account.email === selectedAccount)?.icon} | ||
<span className={cn("ml-2", isCollapsed && "hidden")}> | ||
{ | ||
accounts.find((account) => account.email === selectedAccount) | ||
?.label | ||
} | ||
</span> | ||
</SelectValue> | ||
</SelectTrigger> | ||
<SelectContent> | ||
{accounts.map((account) => ( | ||
<SelectItem key={account.email} value={account.email}> | ||
<div className="flex items-center gap-3 [&_svg]:h-4 [&_svg]:w-4 [&_svg]:shrink-0 [&_svg]:text-foreground"> | ||
{account.icon} | ||
{account.email} | ||
</div> | ||
</SelectItem> | ||
))} | ||
</SelectContent> | ||
</Select> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import React, { ComponentProps } from "react"; | ||
import formatDistanceToNow from "date-fns/formatDistanceToNow"; | ||
import { ChevronRight } from "lucide-react"; | ||
|
||
import { formatCurrency } from "@/lib/utils"; | ||
import { Badge } from "@/components/ui/badge"; | ||
import { Card, CardHeader, CardTitle } from "@/components/ui/card"; | ||
import { ScrollArea } from "@/components/ui/scroll-area"; | ||
|
||
import { Mail } from "../data"; | ||
import { useMail } from "../use-mail"; | ||
|
||
interface MailListProps { | ||
items: Mail[]; | ||
} | ||
|
||
export function AccountsList({ items }: MailListProps) { | ||
const [mail, setMail] = useMail(); | ||
|
||
// Group items by category | ||
const groupedItems = items.reduce( | ||
(acc, item) => { | ||
if (!acc[item.category]) { | ||
acc[item.category] = []; | ||
} | ||
acc[item.category].push(item); | ||
return acc; | ||
}, | ||
{} as Record<string, Mail[]>, | ||
); | ||
|
||
return ( | ||
<ScrollArea className="h-screen"> | ||
<div className="flex flex-col gap-4 p-4 pt-0"> | ||
{Object.entries(groupedItems).map(([category, items]) => ( | ||
<React.Fragment key={category}> | ||
<h2 className="text-lg font-semibold">{category}</h2> | ||
{items.map((item) => ( | ||
<Card | ||
key={item.id} | ||
onClick={() => setMail({ ...mail, selected: item.id })} | ||
className="group flex items-center justify-between p-3 hover:bg-gray-100" | ||
> | ||
<div className="flex grow flex-col"> | ||
<CardTitle className="font-bold">{item.name}</CardTitle> | ||
<p className="text-sm text-gray-500"> | ||
{formatDistanceToNow(new Date(item.date), { | ||
addSuffix: true, | ||
})} | ||
</p> | ||
</div> | ||
<div className="flex items-center space-x-2"> | ||
<Badge | ||
variant={item.change >= 0 ? "default" : "destructive"} | ||
className="self-start" | ||
> | ||
{item.change >= 0 | ||
? `▲ ${item.change}%` | ||
: `▼ ${Math.abs(item.change)}%`} | ||
</Badge> | ||
<p className="flex-1 truncate text-sm font-medium"> | ||
{formatCurrency(item.available)} | ||
</p> | ||
<ChevronRight className="h-5 w-5 text-gray-400" /> | ||
</div> | ||
</Card> | ||
))} | ||
</React.Fragment> | ||
))} | ||
</div> | ||
</ScrollArea> | ||
); | ||
} |
Oops, something went wrong.