feat: add new UI components including Drawer, InputOTP, Pagination, Sidebar, Sonner, and ToggleGroup
Some checks failed
GBCI / build (push) Failing after 2m18s
Some checks failed
GBCI / build (push) Failing after 2m18s
- Implemented Drawer component for modal-like functionality. - Added InputOTP component for handling one-time password inputs. - Created Pagination component for navigating through paginated content. - Developed Sidebar component with collapsible and mobile-friendly features. - Integrated Sonner for toast notifications with theme support. - Introduced ToggleGroup for grouping toggle buttons with context support. - Added useIsMobile hook to determine mobile view based on screen width.
This commit is contained in:
parent
473fae930a
commit
1d458886dd
2 changed files with 760 additions and 532 deletions
|
@ -1,33 +1,259 @@
|
|||
import React from 'react';
|
||||
import { TeamSwitcher } from './components/TeamSwitcher';
|
||||
import { MainNav } from './components/MainNav';
|
||||
import { Search } from './components/Search';
|
||||
import { UserNav } from './components/UserNav';
|
||||
import { CalendarDateRangePicker } from './components/DateRangePicker';
|
||||
import { Overview } from './components/Overview';
|
||||
import { RecentSales } from './components/RecentSales';
|
||||
"use client";
|
||||
import React, { useState } from 'react';
|
||||
|
||||
export default function DashboardScreen() {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<header className="bg-white border-b">
|
||||
<div className="container flex items-center justify-between h-16 px-4">
|
||||
<TeamSwitcher />
|
||||
<MainNav />
|
||||
<div className="flex items-center space-x-4">
|
||||
<Search />
|
||||
<UserNav />
|
||||
const Dashboard = () => {
|
||||
const [dateRange, setDateRange] = useState({
|
||||
startDate: new Date(),
|
||||
endDate: new Date()
|
||||
});
|
||||
const [selectedTeam, setSelectedTeam] = useState({
|
||||
label: "Alicia Koch",
|
||||
value: "personal"
|
||||
});
|
||||
const [teamSwitcherOpen, setTeamSwitcherOpen] = useState(false);
|
||||
const [showNewTeamDialog, setShowNewTeamDialog] = useState(false);
|
||||
const [userNavOpen, setUserNavOpen] = useState(false);
|
||||
|
||||
const formatDate = (date) => {
|
||||
return date.toLocaleDateString('en-US', {
|
||||
month: 'short',
|
||||
day: '2-digit',
|
||||
year: 'numeric'
|
||||
});
|
||||
};
|
||||
|
||||
const salesData = [
|
||||
{ name: "Olivia Martin", email: "olivia.martin@email.com", amount: "+$1,999.00" },
|
||||
{ name: "Jackson Lee", email: "jackson.lee@email.com", amount: "+$39.00" },
|
||||
{ name: "Isabella Nguyen", email: "isabella.nguyen@email.com", amount: "+$299.00" },
|
||||
{ name: "William Kim", email: "will@email.com", amount: "+$99.00" },
|
||||
{ name: "Sofia Davis", email: "sofia.davis@email.com", amount: "+$39.00" },
|
||||
];
|
||||
|
||||
const groups = [
|
||||
{
|
||||
label: "Personal Account",
|
||||
teams: [
|
||||
{ label: "Alicia Koch", value: "personal" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Teams",
|
||||
teams: [
|
||||
{ label: "Acme Inc.", value: "acme-inc" },
|
||||
{ label: "Monsters Inc.", value: "monsters" },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const CalendarDateRangePicker = () => (
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
className="px-3 py-1 border rounded text-foreground border-border hover:bg-accent hover:text-accent-foreground transition-colors"
|
||||
onClick={() => {
|
||||
const date = new Date(prompt("Enter start date (YYYY-MM-DD)") || dateRange.startDate);
|
||||
setDateRange(prev => ({ ...prev, startDate: date }));
|
||||
}}
|
||||
>
|
||||
Start: {formatDate(dateRange.startDate)}
|
||||
</button>
|
||||
<span className="text-foreground">to</span>
|
||||
<button
|
||||
className="px-3 py-1 border rounded text-foreground border-border hover:bg-accent hover:text-accent-foreground transition-colors"
|
||||
onClick={() => {
|
||||
const date = new Date(prompt("Enter end date (YYYY-MM-DD)") || dateRange.endDate);
|
||||
setDateRange(prev => ({ ...prev, endDate: date }));
|
||||
}}
|
||||
>
|
||||
End: {formatDate(dateRange.endDate)}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
||||
const MainNav = () => (
|
||||
<nav className="flex space-x-4">
|
||||
{['Overview', 'Customers', 'Products', 'Settings'].map((item) => (
|
||||
<button
|
||||
key={item}
|
||||
className="px-3 py-2 text-sm font-medium text-foreground hover:text-primary transition-colors"
|
||||
>
|
||||
{item}
|
||||
</button>
|
||||
))}
|
||||
</nav>
|
||||
);
|
||||
|
||||
const Search = () => (
|
||||
<div className="relative max-w-md">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search..."
|
||||
className="w-full pl-8 pr-4 py-2 rounded-full border focus:outline-none focus:ring-2 bg-input border-border focus:ring-ring text-foreground placeholder:text-muted-foreground"
|
||||
/>
|
||||
<svg
|
||||
className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
|
||||
const TeamSwitcher = () => (
|
||||
<div className="relative">
|
||||
<button
|
||||
onClick={() => setTeamSwitcherOpen(true)}
|
||||
className="flex items-center space-x-2 hover:bg-accent hover:text-accent-foreground p-2 rounded transition-colors"
|
||||
>
|
||||
<div className="w-8 h-8 rounded-full bg-secondary flex items-center justify-center text-secondary-foreground">
|
||||
{selectedTeam.label[0]}
|
||||
</div>
|
||||
<span className="text-foreground">{selectedTeam.label}</span>
|
||||
</button>
|
||||
|
||||
{teamSwitcherOpen && (
|
||||
<div className="absolute z-10 mt-2 w-56 bg-popover rounded-md shadow-lg border border-border">
|
||||
<div className="p-2">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search team..."
|
||||
className="w-full p-2 border rounded bg-input border-border text-foreground placeholder:text-muted-foreground"
|
||||
/>
|
||||
</div>
|
||||
{groups.map((group) => (
|
||||
<div key={group.label} className="py-1">
|
||||
<p className="px-3 py-1 text-sm font-medium text-muted-foreground">{group.label}</p>
|
||||
{group.teams.map((team) => (
|
||||
<button
|
||||
key={team.value}
|
||||
onClick={() => {
|
||||
setSelectedTeam(team);
|
||||
setTeamSwitcherOpen(false);
|
||||
}}
|
||||
className="w-full text-left px-3 py-2 hover:bg-accent hover:text-accent-foreground text-popover-foreground transition-colors"
|
||||
>
|
||||
{team.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
<div className="border-t border-border p-2">
|
||||
<button
|
||||
onClick={() => {
|
||||
setTeamSwitcherOpen(false);
|
||||
setShowNewTeamDialog(true);
|
||||
}}
|
||||
className="w-full p-2 text-left hover:bg-accent hover:text-accent-foreground text-popover-foreground transition-colors"
|
||||
>
|
||||
Create Team
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
)}
|
||||
|
||||
{showNewTeamDialog && (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center">
|
||||
<div className="bg-popover p-4 rounded-md border border-border">
|
||||
<h3 className="text-lg font-medium mb-4 text-popover-foreground">Create team</h3>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Team name"
|
||||
className="w-full p-2 border rounded mb-4 bg-input border-border text-foreground placeholder:text-muted-foreground"
|
||||
/>
|
||||
<div className="flex justify-end space-x-2">
|
||||
<button
|
||||
onClick={() => setShowNewTeamDialog(false)}
|
||||
className="px-4 py-2 border rounded border-border text-foreground hover:bg-accent hover:text-accent-foreground transition-colors"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShowNewTeamDialog(false)}
|
||||
className="px-4 py-2 bg-primary text-primary-foreground rounded hover:opacity-90 transition-opacity"
|
||||
>
|
||||
Create
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
const UserNav = () => (
|
||||
<div className="relative">
|
||||
<button
|
||||
onClick={() => setUserNavOpen(!userNavOpen)}
|
||||
className="w-8 h-8 rounded-full bg-secondary flex items-center justify-center text-secondary-foreground hover:bg-accent hover:text-accent-foreground transition-colors"
|
||||
>
|
||||
U
|
||||
</button>
|
||||
|
||||
{userNavOpen && (
|
||||
<div className="absolute right-0 mt-2 w-48 bg-popover rounded-md shadow-lg z-10 border border-border">
|
||||
<div className="p-2 border-b border-border">
|
||||
<p className="font-medium text-popover-foreground">shadcn</p>
|
||||
<p className="text-sm text-muted-foreground">m@example.com</p>
|
||||
</div>
|
||||
{['Profile', 'Billing', 'Settings', 'New Team', 'Log out'].map((item) => (
|
||||
<button
|
||||
key={item}
|
||||
className="w-full text-left px-3 py-2 hover:bg-accent hover:text-accent-foreground text-popover-foreground transition-colors"
|
||||
>
|
||||
{item}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
const Overview = () => (
|
||||
<div className="p-4 border rounded-lg border-border">
|
||||
<div className="flex justify-between items-end h-40">
|
||||
{[100, 80, 60, 40, 20].map((height, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="w-8 opacity-60"
|
||||
style={{
|
||||
height: `${height}px`,
|
||||
backgroundColor: `hsl(var(--chart-${(index % 5) + 1}))`
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const RecentSales = () => (
|
||||
<div className="space-y-4">
|
||||
{salesData.map((item, index) => (
|
||||
<div key={index} className="flex items-center justify-between p-2 border-b border-border">
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="w-8 h-8 rounded-full bg-secondary flex items-center justify-center text-secondary-foreground">
|
||||
{item.name[0]}
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium text-foreground">{item.name}</p>
|
||||
<p className="text-sm text-muted-foreground">{item.email}</p>
|
||||
</div>
|
||||
</div>
|
||||
<span className="font-medium text-foreground">{item.amount}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background text-foreground">
|
||||
<main className="container p-4 space-y-4">
|
||||
s
|
||||
<div className="flex items-center justify-between">
|
||||
<h1 className="text-2xl font-bold">Dashboard</h1>
|
||||
<h1 className="text-2xl font-bold text-foreground">Dashboard</h1>
|
||||
<div className="flex items-center space-x-2">
|
||||
<CalendarDateRangePicker />
|
||||
<button className="px-4 py-2 bg-blue-500 text-white rounded">
|
||||
<button className="px-4 py-2 bg-primary text-primary-foreground rounded hover:opacity-90 transition-opacity">
|
||||
Download
|
||||
</button>
|
||||
</div>
|
||||
|
@ -40,26 +266,28 @@ s
|
|||
{ title: "Sales", value: "+12,234", subtext: "+19% from last month" },
|
||||
{ title: "Active Now", value: "+573", subtext: "+201 since last hour" },
|
||||
].map((card, index) => (
|
||||
<div key={index} className="p-6 bg-white border rounded-lg">
|
||||
<h3 className="text-sm font-medium text-gray-500">{card.title}</h3>
|
||||
<p className="text-2xl font-bold mt-1">{card.value}</p>
|
||||
<p className="text-xs text-gray-500 mt-1">{card.subtext}</p>
|
||||
<div key={index} className="p-6 bg-card border rounded-lg border-border">
|
||||
<h3 className="text-sm font-medium text-muted-foreground">{card.title}</h3>
|
||||
<p className="text-2xl font-bold mt-1 text-card-foreground">{card.value}</p>
|
||||
<p className="text-xs text-muted-foreground mt-1">{card.subtext}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
<div className="p-6 bg-white border rounded-lg">
|
||||
<h3 className="text-lg font-medium mb-4">Overview</h3>
|
||||
<div className="p-6 bg-card border rounded-lg border-border">
|
||||
<h3 className="text-lg font-medium mb-4 text-card-foreground">Overview</h3>
|
||||
<Overview />
|
||||
</div>
|
||||
<div className="p-6 bg-white border rounded-lg space-y-4">
|
||||
<h3 className="text-lg font-medium">Recent Sales</h3>
|
||||
<p>You made 265 sales this month.</p>
|
||||
<div className="p-6 bg-card border rounded-lg space-y-4 border-border">
|
||||
<h3 className="text-lg font-medium text-card-foreground">Recent Sales</h3>
|
||||
<p className="text-card-foreground">You made 265 sales this month.</p>
|
||||
<RecentSales />
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default Dashboard;
|
1000
app/mail/page.tsx
1000
app/mail/page.tsx
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue