snapshot of https://github.com/shadcn-ui/ui.git (main, 2026-09-15; blobs > 131072B omitted)
This commit is contained in:
132
apps/v4/components/cards/activity-goal.tsx
Normal file
132
apps/v4/components/cards/activity-goal.tsx
Normal file
@@ -0,0 +1,132 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { MinusIcon, PlusIcon } from "lucide-react"
|
||||
import { Bar, BarChart } from "recharts"
|
||||
|
||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/registry/new-york-v4/ui/card"
|
||||
import {
|
||||
ChartContainer,
|
||||
type ChartConfig,
|
||||
} from "@/registry/new-york-v4/ui/chart"
|
||||
|
||||
const data = [
|
||||
{
|
||||
goal: 400,
|
||||
},
|
||||
{
|
||||
goal: 300,
|
||||
},
|
||||
{
|
||||
goal: 200,
|
||||
},
|
||||
{
|
||||
goal: 300,
|
||||
},
|
||||
{
|
||||
goal: 200,
|
||||
},
|
||||
{
|
||||
goal: 278,
|
||||
},
|
||||
{
|
||||
goal: 189,
|
||||
},
|
||||
{
|
||||
goal: 239,
|
||||
},
|
||||
{
|
||||
goal: 300,
|
||||
},
|
||||
{
|
||||
goal: 200,
|
||||
},
|
||||
{
|
||||
goal: 278,
|
||||
},
|
||||
{
|
||||
goal: 189,
|
||||
},
|
||||
{
|
||||
goal: 349,
|
||||
},
|
||||
]
|
||||
|
||||
const chartConfig = {
|
||||
goal: {
|
||||
label: "Goal",
|
||||
color: "var(--primary)",
|
||||
},
|
||||
} satisfies ChartConfig
|
||||
|
||||
export function CardsActivityGoal() {
|
||||
const [goal, setGoal] = React.useState(350)
|
||||
|
||||
function onClick(adjustment: number) {
|
||||
setGoal(Math.max(200, Math.min(400, goal + adjustment)))
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className="h-full gap-5">
|
||||
<CardHeader>
|
||||
<CardTitle>Move Goal</CardTitle>
|
||||
<CardDescription>Set your daily activity goal.</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-1 flex-col">
|
||||
<div className="flex items-center justify-center gap-4">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="size-7 rounded-full"
|
||||
onClick={() => onClick(-10)}
|
||||
disabled={goal <= 200}
|
||||
>
|
||||
<MinusIcon />
|
||||
<span className="sr-only">Decrease</span>
|
||||
</Button>
|
||||
<div className="text-center">
|
||||
<div className="text-4xl font-bold tracking-tighter tabular-nums">
|
||||
{goal}
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground uppercase">
|
||||
Calories/day
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="size-7 rounded-full"
|
||||
onClick={() => onClick(10)}
|
||||
disabled={goal >= 400}
|
||||
>
|
||||
<PlusIcon />
|
||||
<span className="sr-only">Increase</span>
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<ChartContainer
|
||||
config={chartConfig}
|
||||
className="aspect-auto h-full w-full"
|
||||
>
|
||||
<BarChart data={data}>
|
||||
<Bar dataKey="goal" radius={4} fill="var(--color-goal)" />
|
||||
</BarChart>
|
||||
</ChartContainer>
|
||||
</div>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<Button className="w-full" variant="secondary">
|
||||
Set Goal
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
26
apps/v4/components/cards/calendar.tsx
Normal file
26
apps/v4/components/cards/calendar.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
"use client"
|
||||
|
||||
import { addDays } from "date-fns"
|
||||
|
||||
import { Calendar } from "@/registry/new-york-v4/ui/calendar"
|
||||
import { Card, CardContent } from "@/registry/new-york-v4/ui/card"
|
||||
|
||||
const start = new Date(2025, 5, 5)
|
||||
|
||||
export function CardsCalendar() {
|
||||
return (
|
||||
<Card className="hidden max-w-[260px] p-0 sm:flex">
|
||||
<CardContent className="p-0">
|
||||
<Calendar
|
||||
numberOfMonths={1}
|
||||
mode="range"
|
||||
defaultMonth={start}
|
||||
selected={{
|
||||
from: start,
|
||||
to: addDays(start, 8),
|
||||
}}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
273
apps/v4/components/cards/chat.tsx
Normal file
273
apps/v4/components/cards/chat.tsx
Normal file
@@ -0,0 +1,273 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { cn } from "cn"
|
||||
import { ArrowUpIcon, CheckIcon, PlusIcon } from "lucide-react"
|
||||
|
||||
import {
|
||||
Avatar,
|
||||
AvatarFallback,
|
||||
AvatarImage,
|
||||
} from "@/registry/new-york-v4/ui/avatar"
|
||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
} from "@/registry/new-york-v4/ui/card"
|
||||
import {
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
} from "@/registry/new-york-v4/ui/command"
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/registry/new-york-v4/ui/dialog"
|
||||
import {
|
||||
InputGroup,
|
||||
InputGroupAddon,
|
||||
InputGroupButton,
|
||||
InputGroupInput,
|
||||
} from "@/registry/new-york-v4/ui/input-group"
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "@/registry/new-york-v4/ui/tooltip"
|
||||
|
||||
const users = [
|
||||
{
|
||||
name: "Olivia Martin",
|
||||
email: "m@example.com",
|
||||
avatar: "/avatars/01.png",
|
||||
},
|
||||
{
|
||||
name: "Isabella Nguyen",
|
||||
email: "isabella.nguyen@email.com",
|
||||
avatar: "/avatars/03.png",
|
||||
},
|
||||
{
|
||||
name: "Emma Wilson",
|
||||
email: "emma@example.com",
|
||||
avatar: "/avatars/05.png",
|
||||
},
|
||||
{
|
||||
name: "Jackson Lee",
|
||||
email: "lee@example.com",
|
||||
avatar: "/avatars/02.png",
|
||||
},
|
||||
{
|
||||
name: "William Kim",
|
||||
email: "will@email.com",
|
||||
avatar: "/avatars/04.png",
|
||||
},
|
||||
] as const
|
||||
|
||||
type User = (typeof users)[number]
|
||||
|
||||
export function CardsChat() {
|
||||
const [open, setOpen] = React.useState(false)
|
||||
const [selectedUsers, setSelectedUsers] = React.useState<User[]>([])
|
||||
|
||||
const [messages, setMessages] = React.useState([
|
||||
{
|
||||
role: "agent",
|
||||
content: "Hi, how can I help you today?",
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: "Hey, I'm having trouble with my account.",
|
||||
},
|
||||
{
|
||||
role: "agent",
|
||||
content: "What seems to be the problem?",
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: "I can't log in.",
|
||||
},
|
||||
])
|
||||
const [input, setInput] = React.useState("")
|
||||
const inputLength = input.trim().length
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center">
|
||||
<div className="flex items-center gap-4">
|
||||
<Avatar className="border">
|
||||
<AvatarImage src="/avatars/01.png" alt="Image" />
|
||||
<AvatarFallback>S</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<p className="text-sm leading-none font-medium">Sofia Davis</p>
|
||||
<p className="text-xs text-muted-foreground">m@example.com</p>
|
||||
</div>
|
||||
</div>
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
size="icon"
|
||||
variant="secondary"
|
||||
className="ml-auto size-8 rounded-full"
|
||||
onClick={() => setOpen(true)}
|
||||
>
|
||||
<PlusIcon />
|
||||
<span className="sr-only">New message</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent sideOffset={10}>New message</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex flex-col gap-4">
|
||||
{messages.map((message, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={cn(
|
||||
"flex w-max max-w-[75%] flex-col gap-2 rounded-lg px-3 py-2 text-sm",
|
||||
message.role === "user"
|
||||
? "ml-auto bg-primary text-primary-foreground"
|
||||
: "bg-muted"
|
||||
)}
|
||||
>
|
||||
{message.content}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<form
|
||||
onSubmit={(event) => {
|
||||
event.preventDefault()
|
||||
if (inputLength === 0) return
|
||||
setMessages([
|
||||
...messages,
|
||||
{
|
||||
role: "user",
|
||||
content: input,
|
||||
},
|
||||
])
|
||||
setInput("")
|
||||
}}
|
||||
className="relative w-full"
|
||||
>
|
||||
<InputGroup>
|
||||
<InputGroupInput
|
||||
id="message"
|
||||
placeholder="Type your message..."
|
||||
autoComplete="off"
|
||||
value={input}
|
||||
onChange={(event) => setInput(event.target.value)}
|
||||
/>
|
||||
<InputGroupAddon align="inline-end">
|
||||
<InputGroupButton
|
||||
type="submit"
|
||||
size="icon-xs"
|
||||
className="rounded-full"
|
||||
>
|
||||
<ArrowUpIcon />
|
||||
<span className="sr-only">Send</span>
|
||||
</InputGroupButton>
|
||||
</InputGroupAddon>
|
||||
</InputGroup>
|
||||
</form>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogContent className="gap-0 p-0 outline-none">
|
||||
<DialogHeader className="px-4 pt-5 pb-4">
|
||||
<DialogTitle>New message</DialogTitle>
|
||||
<DialogDescription>
|
||||
Invite a user to this thread. This will create a new group
|
||||
message.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<Command className="overflow-hidden rounded-t-none border-t bg-transparent">
|
||||
<CommandInput placeholder="Search user..." />
|
||||
<CommandList>
|
||||
<CommandEmpty>No users found.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{users.map((user) => (
|
||||
<CommandItem
|
||||
key={user.email}
|
||||
data-active={selectedUsers.includes(user)}
|
||||
className="data-[active=true]:opacity-50"
|
||||
onSelect={() => {
|
||||
if (selectedUsers.includes(user)) {
|
||||
return setSelectedUsers(
|
||||
selectedUsers.filter(
|
||||
(selectedUser) => selectedUser !== user
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return setSelectedUsers(
|
||||
[...users].filter((u) =>
|
||||
[...selectedUsers, user].includes(u)
|
||||
)
|
||||
)
|
||||
}}
|
||||
>
|
||||
<Avatar className="border">
|
||||
<AvatarImage src={user.avatar} alt="Image" />
|
||||
<AvatarFallback>{user.name[0]}</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="ml-2">
|
||||
<p className="text-sm leading-none font-medium">
|
||||
{user.name}
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{user.email}
|
||||
</p>
|
||||
</div>
|
||||
{selectedUsers.includes(user) ? (
|
||||
<CheckIcon className="ml-auto flex size-4 text-primary" />
|
||||
) : null}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
<DialogFooter className="flex items-center border-t p-4 sm:justify-between">
|
||||
{selectedUsers.length > 0 ? (
|
||||
<div className="flex -space-x-2 overflow-hidden">
|
||||
{selectedUsers.map((user) => (
|
||||
<Avatar key={user.email} className="inline-block border">
|
||||
<AvatarImage src={user.avatar} />
|
||||
<AvatarFallback>{user.name[0]}</AvatarFallback>
|
||||
</Avatar>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Select users to add to this thread.
|
||||
</p>
|
||||
)}
|
||||
<Button
|
||||
disabled={selectedUsers.length < 2}
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
setOpen(false)
|
||||
}}
|
||||
>
|
||||
Continue
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
43
apps/v4/components/cards/cookie-settings.tsx
Normal file
43
apps/v4/components/cards/cookie-settings.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
"use client"
|
||||
|
||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/registry/new-york-v4/ui/card"
|
||||
import {
|
||||
Field,
|
||||
FieldContent,
|
||||
FieldDescription,
|
||||
FieldLabel,
|
||||
} from "@/registry/new-york-v4/ui/field"
|
||||
import { Switch } from "@/registry/new-york-v4/ui/switch"
|
||||
|
||||
export function CardsCookieSettings() {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Cookie Settings</CardTitle>
|
||||
<CardDescription>Manage your cookie settings here.</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="grid gap-6">
|
||||
<Field orientation="horizontal">
|
||||
<FieldContent>
|
||||
<FieldLabel htmlFor="necessary">Strictly Necessary</FieldLabel>
|
||||
<FieldDescription>
|
||||
These cookies are essential in order to use the website and use
|
||||
its features.
|
||||
</FieldDescription>
|
||||
</FieldContent>
|
||||
<Switch id="necessary" defaultChecked aria-label="Necessary" />
|
||||
</Field>
|
||||
<Field>
|
||||
<Button variant="outline">Save preferences</Button>
|
||||
</Field>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
72
apps/v4/components/cards/create-account.tsx
Normal file
72
apps/v4/components/cards/create-account.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
"use client"
|
||||
|
||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/registry/new-york-v4/ui/card"
|
||||
import {
|
||||
Field,
|
||||
FieldGroup,
|
||||
FieldLabel,
|
||||
FieldSeparator,
|
||||
} from "@/registry/new-york-v4/ui/field"
|
||||
import { Input } from "@/registry/new-york-v4/ui/input"
|
||||
|
||||
export function CardsCreateAccount() {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-2xl">Create an account</CardTitle>
|
||||
<CardDescription>
|
||||
Enter your email below to create your account
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<FieldGroup>
|
||||
<Field className="grid grid-cols-2 gap-6">
|
||||
<Button variant="outline">
|
||||
<svg viewBox="0 0 438.549 438.549">
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M409.132 114.573c-19.608-33.596-46.205-60.194-79.798-79.8-33.598-19.607-70.277-29.408-110.063-29.408-39.781 0-76.472 9.804-110.063 29.408-33.596 19.605-60.192 46.204-79.8 79.8C9.803 148.168 0 184.854 0 224.63c0 47.78 13.94 90.745 41.827 128.906 27.884 38.164 63.906 64.572 108.063 79.227 5.14.954 8.945.283 11.419-1.996 2.475-2.282 3.711-5.14 3.711-8.562 0-.571-.049-5.708-.144-15.417a2549.81 2549.81 0 01-.144-25.406l-6.567 1.136c-4.187.767-9.469 1.092-15.846 1-6.374-.089-12.991-.757-19.842-1.999-6.854-1.231-13.229-4.086-19.13-8.559-5.898-4.473-10.085-10.328-12.56-17.556l-2.855-6.57c-1.903-4.374-4.899-9.233-8.992-14.559-4.093-5.331-8.232-8.945-12.419-10.848l-1.999-1.431c-1.332-.951-2.568-2.098-3.711-3.429-1.142-1.331-1.997-2.663-2.568-3.997-.572-1.335-.098-2.43 1.427-3.289 1.525-.859 4.281-1.276 8.28-1.276l5.708.853c3.807.763 8.516 3.042 14.133 6.851 5.614 3.806 10.229 8.754 13.846 14.842 4.38 7.806 9.657 13.754 15.846 17.847 6.184 4.093 12.419 6.136 18.699 6.136 6.28 0 11.704-.476 16.274-1.423 4.565-.952 8.848-2.383 12.847-4.285 1.713-12.758 6.377-22.559 13.988-29.41-10.848-1.14-20.601-2.857-29.264-5.14-8.658-2.286-17.605-5.996-26.835-11.14-9.235-5.137-16.896-11.516-22.985-19.126-6.09-7.614-11.088-17.61-14.987-29.979-3.901-12.374-5.852-26.648-5.852-42.826 0-23.035 7.52-42.637 22.557-58.817-7.044-17.318-6.379-36.732 1.997-58.24 5.52-1.715 13.706-.428 24.554 3.853 10.85 4.283 18.794 7.952 23.84 10.994 5.046 3.041 9.089 5.618 12.135 7.708 17.705-4.947 35.976-7.421 54.818-7.421s37.117 2.474 54.823 7.421l10.849-6.849c7.419-4.57 16.18-8.758 26.262-12.565 10.088-3.805 17.802-4.853 23.134-3.138 8.562 21.509 9.325 40.922 2.279 58.24 15.036 16.18 22.559 35.787 22.559 58.817 0 16.178-1.958 30.497-5.853 42.966-3.9 12.471-8.941 22.457-15.125 29.979-6.191 7.521-13.901 13.85-23.131 18.986-9.232 5.14-18.182 8.85-26.84 11.136-8.662 2.286-18.415 4.004-29.263 5.146 9.894 8.562 14.842 22.077 14.842 40.539v60.237c0 3.422 1.19 6.279 3.572 8.562 2.379 2.279 6.136 2.95 11.276 1.995 44.163-14.653 80.185-41.062 108.068-79.226 27.88-38.161 41.825-81.126 41.825-128.906-.01-39.771-9.818-76.454-29.414-110.049z"
|
||||
></path>
|
||||
</svg>
|
||||
GitHub
|
||||
</Button>
|
||||
<Button variant="outline">
|
||||
<svg role="img" viewBox="0 0 24 24">
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M12.48 10.92v3.28h7.84c-.24 1.84-.853 3.187-1.787 4.133-1.147 1.147-2.933 2.4-6.053 2.4-4.827 0-8.6-3.893-8.6-8.72s3.773-8.72 8.6-8.72c2.6 0 4.507 1.027 5.907 2.347l2.307-2.307C18.747 1.44 16.133 0 12.48 0 5.867 0 .307 5.387.307 12s5.56 12 12.173 12c3.573 0 6.267-1.173 8.373-3.36 2.16-2.16 2.84-5.213 2.84-7.667 0-.76-.053-1.467-.173-2.053H12.48z"
|
||||
/>
|
||||
</svg>
|
||||
Google
|
||||
</Button>
|
||||
</Field>
|
||||
<FieldSeparator className="*:data-[slot=field-separator-content]:bg-card">
|
||||
Or continue with
|
||||
</FieldSeparator>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="email-create-account">Email</FieldLabel>
|
||||
<Input
|
||||
id="email-create-account"
|
||||
type="email"
|
||||
placeholder="m@example.com"
|
||||
/>
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="password-create-account">Password</FieldLabel>
|
||||
<Input id="password-create-account" type="password" />
|
||||
</Field>
|
||||
<Field>
|
||||
<Button>Create Account</Button>
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
129
apps/v4/components/cards/exercise-minutes.tsx
Normal file
129
apps/v4/components/cards/exercise-minutes.tsx
Normal file
@@ -0,0 +1,129 @@
|
||||
"use client"
|
||||
|
||||
import { CartesianGrid, Line, LineChart, XAxis } from "recharts"
|
||||
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/registry/new-york-v4/ui/card"
|
||||
import {
|
||||
ChartContainer,
|
||||
ChartTooltip,
|
||||
ChartTooltipContent,
|
||||
type ChartConfig,
|
||||
} from "@/registry/new-york-v4/ui/chart"
|
||||
|
||||
const data = [
|
||||
{
|
||||
average: 400,
|
||||
today: 240,
|
||||
day: "Monday",
|
||||
},
|
||||
{
|
||||
average: 300,
|
||||
today: 139,
|
||||
day: "Tuesday",
|
||||
},
|
||||
{
|
||||
average: 200,
|
||||
today: 980,
|
||||
day: "Wednesday",
|
||||
},
|
||||
{
|
||||
average: 278,
|
||||
today: 390,
|
||||
day: "Thursday",
|
||||
},
|
||||
{
|
||||
average: 189,
|
||||
today: 480,
|
||||
day: "Friday",
|
||||
},
|
||||
{
|
||||
average: 239,
|
||||
today: 380,
|
||||
day: "Saturday",
|
||||
},
|
||||
{
|
||||
average: 349,
|
||||
today: 430,
|
||||
day: "Sunday",
|
||||
},
|
||||
]
|
||||
|
||||
const chartConfig = {
|
||||
today: {
|
||||
label: "Today",
|
||||
color: "var(--primary)",
|
||||
},
|
||||
average: {
|
||||
label: "Average",
|
||||
color: "var(--primary)",
|
||||
},
|
||||
} satisfies ChartConfig
|
||||
|
||||
export function CardsExerciseMinutes() {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Exercise Minutes</CardTitle>
|
||||
<CardDescription>
|
||||
Your exercise minutes are ahead of where you normally are.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ChartContainer config={chartConfig} className="w-full md:h-[200px]">
|
||||
<LineChart
|
||||
accessibilityLayer
|
||||
data={data}
|
||||
margin={{
|
||||
top: 5,
|
||||
right: 10,
|
||||
left: 16,
|
||||
bottom: 0,
|
||||
}}
|
||||
>
|
||||
<CartesianGrid vertical={false} />
|
||||
<XAxis
|
||||
dataKey="day"
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
tickMargin={8}
|
||||
tickFormatter={(value) => value.slice(0, 3)}
|
||||
/>
|
||||
<Line
|
||||
type="monotone"
|
||||
dataKey="today"
|
||||
strokeWidth={2}
|
||||
stroke="var(--color-today)"
|
||||
dot={{
|
||||
fill: "var(--color-today)",
|
||||
}}
|
||||
activeDot={{
|
||||
r: 5,
|
||||
}}
|
||||
/>
|
||||
<Line
|
||||
type="monotone"
|
||||
strokeWidth={2}
|
||||
dataKey="average"
|
||||
stroke="var(--color-average)"
|
||||
strokeOpacity={0.5}
|
||||
dot={{
|
||||
fill: "var(--color-average)",
|
||||
opacity: 0.5,
|
||||
}}
|
||||
activeDot={{
|
||||
r: 5,
|
||||
}}
|
||||
/>
|
||||
<ChartTooltip content={<ChartTooltipContent />} />
|
||||
</LineChart>
|
||||
</ChartContainer>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
140
apps/v4/components/cards/forms.tsx
Normal file
140
apps/v4/components/cards/forms.tsx
Normal file
@@ -0,0 +1,140 @@
|
||||
"use client"
|
||||
|
||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/registry/new-york-v4/ui/card"
|
||||
import { Checkbox } from "@/registry/new-york-v4/ui/checkbox"
|
||||
import {
|
||||
Field,
|
||||
FieldContent,
|
||||
FieldDescription,
|
||||
FieldGroup,
|
||||
FieldLabel,
|
||||
FieldLegend,
|
||||
FieldSet,
|
||||
FieldTitle,
|
||||
} from "@/registry/new-york-v4/ui/field"
|
||||
import { Input } from "@/registry/new-york-v4/ui/input"
|
||||
import {
|
||||
RadioGroup,
|
||||
RadioGroupItem,
|
||||
} from "@/registry/new-york-v4/ui/radio-group"
|
||||
import { Textarea } from "@/registry/new-york-v4/ui/textarea"
|
||||
|
||||
const plans = [
|
||||
{
|
||||
id: "starter",
|
||||
name: "Starter Plan",
|
||||
description: "For small businesses.",
|
||||
price: "$10",
|
||||
},
|
||||
{
|
||||
id: "pro",
|
||||
name: "Pro Plan",
|
||||
description: "More features and storage.",
|
||||
price: "$20",
|
||||
},
|
||||
] as const
|
||||
|
||||
export function CardsForms() {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-lg">Upgrade your Subscription</CardTitle>
|
||||
<CardDescription className="text-balance">
|
||||
You are currently on the free plan. Upgrade to the pro plan to get
|
||||
access to all features.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<form>
|
||||
<FieldGroup>
|
||||
<FieldGroup className="grid grid-cols-2">
|
||||
<Field>
|
||||
<FieldLabel htmlFor="name">Name</FieldLabel>
|
||||
<Input id="name" placeholder="Max Leiter" />
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="email">Email</FieldLabel>
|
||||
<Input id="email" placeholder="mail@acme.com" />
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
<FieldGroup className="grid grid-cols-2 gap-3 md:grid-cols-[1fr_80px_60px]">
|
||||
<Field>
|
||||
<FieldLabel htmlFor="card-number">Card Number</FieldLabel>
|
||||
<Input
|
||||
id="card-number"
|
||||
placeholder="1234 1234 1234 1234"
|
||||
className="col-span-2 md:col-span-1"
|
||||
/>
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="card-number-expiry">
|
||||
Expiry Date
|
||||
</FieldLabel>
|
||||
<Input id="card-number-expiry" placeholder="MM/YY" />
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="card-number-cvc">CVC</FieldLabel>
|
||||
<Input id="card-number-cvc" placeholder="CVC" />
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
<FieldSet>
|
||||
<FieldLegend>Plan</FieldLegend>
|
||||
<FieldDescription>
|
||||
Select the plan that best fits your needs.
|
||||
</FieldDescription>
|
||||
<RadioGroup
|
||||
defaultValue="starter"
|
||||
className="grid grid-cols-2 gap-2"
|
||||
>
|
||||
{plans.map((plan) => (
|
||||
<FieldLabel key={plan.id}>
|
||||
<Field orientation="horizontal">
|
||||
<FieldContent>
|
||||
<FieldTitle>{plan.name}</FieldTitle>
|
||||
<FieldDescription className="text-xs">
|
||||
{plan.description}
|
||||
</FieldDescription>
|
||||
</FieldContent>
|
||||
<RadioGroupItem value={plan.id} id={plan.name} />
|
||||
</Field>
|
||||
</FieldLabel>
|
||||
))}
|
||||
</RadioGroup>
|
||||
</FieldSet>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="notes">Notes</FieldLabel>
|
||||
<Textarea id="notes" placeholder="Enter notes" />
|
||||
</Field>
|
||||
<Field>
|
||||
<Field orientation="horizontal">
|
||||
<Checkbox id="terms" />
|
||||
<FieldLabel htmlFor="terms" className="font-normal">
|
||||
I agree to the terms and conditions
|
||||
</FieldLabel>
|
||||
</Field>
|
||||
<Field orientation="horizontal">
|
||||
<Checkbox id="newsletter" defaultChecked />
|
||||
<FieldLabel htmlFor="newsletter" className="font-normal">
|
||||
Allow us to send you emails
|
||||
</FieldLabel>
|
||||
</Field>
|
||||
</Field>
|
||||
<Field orientation="horizontal">
|
||||
<Button variant="outline" size="sm">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button size="sm">Upgrade Plan</Button>
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
59
apps/v4/components/cards/index.tsx
Normal file
59
apps/v4/components/cards/index.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import { CardsActivityGoal } from "@/components/cards/activity-goal"
|
||||
import { CardsCalendar } from "@/components/cards/calendar"
|
||||
import { CardsChat } from "@/components/cards/chat"
|
||||
import { CardsCookieSettings } from "@/components/cards/cookie-settings"
|
||||
import { CardsCreateAccount } from "@/components/cards/create-account"
|
||||
import { CardsExerciseMinutes } from "@/components/cards/exercise-minutes"
|
||||
import { CardsForms } from "@/components/cards/forms"
|
||||
import { CardsReportIssue } from "@/components/cards/report-issue"
|
||||
import { CardsShare } from "@/components/cards/share"
|
||||
import { CardsStats } from "@/components/cards/stats"
|
||||
import { CardsTeamMembers } from "@/components/cards/team-members"
|
||||
|
||||
export function CardsDemo() {
|
||||
return (
|
||||
<div className="md:grids-col-2 grid **:data-[slot=card]:shadow-none md:gap-4 lg:grid-cols-10 xl:grid-cols-11">
|
||||
<div className="grid gap-4 lg:col-span-4 xl:col-span-6">
|
||||
<CardsStats />
|
||||
<div className="grid gap-1 sm:grid-cols-[auto_1fr] md:hidden">
|
||||
<CardsCalendar />
|
||||
<div className="pt-3 sm:pt-0 sm:pl-2 xl:pl-4">
|
||||
<CardsActivityGoal />
|
||||
</div>
|
||||
<div className="pt-3 sm:col-span-2 xl:pt-4">
|
||||
<CardsExerciseMinutes />
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-1 xl:grid-cols-2">
|
||||
<div className="flex flex-col gap-4">
|
||||
<CardsForms />
|
||||
<CardsTeamMembers />
|
||||
<CardsCookieSettings />
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
<CardsCreateAccount />
|
||||
<CardsChat />
|
||||
<div className="hidden xl:block">
|
||||
<CardsReportIssue />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4 lg:col-span-6 xl:col-span-5">
|
||||
<div className="hidden gap-1 sm:grid-cols-[auto_1fr] md:grid">
|
||||
<CardsCalendar />
|
||||
<div className="pt-3 sm:pt-0 sm:pl-2 xl:pl-3">
|
||||
<CardsActivityGoal />
|
||||
</div>
|
||||
<div className="pt-3 sm:col-span-2 xl:pt-3">
|
||||
<CardsExerciseMinutes />
|
||||
</div>
|
||||
</div>
|
||||
<CardsShare />
|
||||
<div className="xl:hidden">
|
||||
<CardsReportIssue />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
134
apps/v4/components/cards/payment-method.tsx
Normal file
134
apps/v4/components/cards/payment-method.tsx
Normal file
@@ -0,0 +1,134 @@
|
||||
"use client"
|
||||
|
||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/registry/new-york-v4/ui/card"
|
||||
import { Input } from "@/registry/new-york-v4/ui/input"
|
||||
import { Label } from "@/registry/new-york-v4/ui/label"
|
||||
import {
|
||||
RadioGroup,
|
||||
RadioGroupItem,
|
||||
} from "@/registry/new-york-v4/ui/radio-group"
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/registry/new-york-v4/ui/select"
|
||||
|
||||
const plans = [
|
||||
{
|
||||
id: "starter",
|
||||
name: "Starter Plan",
|
||||
description: "Perfect for small businesses.",
|
||||
price: "$10",
|
||||
},
|
||||
{
|
||||
id: "pro",
|
||||
name: "Pro Plan",
|
||||
description: "Advanced features with more storage.",
|
||||
price: "$20",
|
||||
},
|
||||
] as const
|
||||
|
||||
export function CardsPaymentMethod() {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Payment Method</CardTitle>
|
||||
<CardDescription>
|
||||
Add a new payment method to your account.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col gap-6">
|
||||
<div className="flex flex-col gap-3">
|
||||
<Label htmlFor="name">Name</Label>
|
||||
<Input id="name" placeholder="First Last" />
|
||||
</div>
|
||||
<fieldset className="flex flex-col gap-3">
|
||||
<legend className="text-sm font-medium">Plan</legend>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Select the plan that best fits your needs.
|
||||
</p>
|
||||
<RadioGroup defaultValue="starter" className="grid gap-3">
|
||||
{plans.map((plan) => (
|
||||
<Label
|
||||
className="flex items-start gap-3 rounded-lg border p-3 has-[[data-state=checked]]:border-ring has-[[data-state=checked]]:bg-primary/5"
|
||||
key={plan.id}
|
||||
>
|
||||
<RadioGroupItem
|
||||
value={plan.id}
|
||||
id={plan.name}
|
||||
className="data-[state=checked]:border-primary"
|
||||
/>
|
||||
<div className="grid gap-1 font-normal">
|
||||
<div className="font-medium">{plan.name}</div>
|
||||
<div className="pr-2 text-xs leading-snug text-balance text-muted-foreground">
|
||||
{plan.description}
|
||||
</div>
|
||||
</div>
|
||||
</Label>
|
||||
))}
|
||||
</RadioGroup>
|
||||
</fieldset>
|
||||
<div className="flex flex-col gap-3">
|
||||
<Label htmlFor="number">Card number</Label>
|
||||
<Input id="number" placeholder="" />
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
<div className="flex flex-col gap-3">
|
||||
<Label htmlFor="month">Expires</Label>
|
||||
<Select>
|
||||
<SelectTrigger id="month" aria-label="Month" className="w-full">
|
||||
<SelectValue placeholder="Month" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="1">January</SelectItem>
|
||||
<SelectItem value="2">February</SelectItem>
|
||||
<SelectItem value="3">March</SelectItem>
|
||||
<SelectItem value="4">April</SelectItem>
|
||||
<SelectItem value="5">May</SelectItem>
|
||||
<SelectItem value="6">June</SelectItem>
|
||||
<SelectItem value="7">July</SelectItem>
|
||||
<SelectItem value="8">August</SelectItem>
|
||||
<SelectItem value="9">September</SelectItem>
|
||||
<SelectItem value="10">October</SelectItem>
|
||||
<SelectItem value="11">November</SelectItem>
|
||||
<SelectItem value="12">December</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="flex flex-col gap-3">
|
||||
<Label htmlFor="year">Year</Label>
|
||||
<Select>
|
||||
<SelectTrigger id="year" aria-label="Year" className="w-full">
|
||||
<SelectValue placeholder="Year" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{Array.from({ length: 10 }, (_, i) => (
|
||||
<SelectItem key={i} value={`${new Date().getFullYear() + i}`}>
|
||||
{new Date().getFullYear() + i}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="flex flex-col gap-3">
|
||||
<Label htmlFor="cvc">CVC</Label>
|
||||
<Input id="cvc" placeholder="CVC" />
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<Button className="w-full">Continue</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
100
apps/v4/components/cards/report-issue.tsx
Normal file
100
apps/v4/components/cards/report-issue.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
|
||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/registry/new-york-v4/ui/card"
|
||||
import { Field, FieldGroup, FieldLabel } from "@/registry/new-york-v4/ui/field"
|
||||
import { Input } from "@/registry/new-york-v4/ui/input"
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/registry/new-york-v4/ui/select"
|
||||
import { Textarea } from "@/registry/new-york-v4/ui/textarea"
|
||||
|
||||
export function CardsReportIssue() {
|
||||
const id = React.useId()
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Report an issue</CardTitle>
|
||||
<CardDescription>
|
||||
What area are you having problems with?
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<FieldGroup>
|
||||
<FieldGroup className="grid gap-4 sm:grid-cols-2">
|
||||
<Field>
|
||||
<FieldLabel htmlFor={`area-${id}`}>Area</FieldLabel>
|
||||
<Select defaultValue="billing">
|
||||
<SelectTrigger
|
||||
id={`area-${id}`}
|
||||
aria-label="Area"
|
||||
className="w-full"
|
||||
>
|
||||
<SelectValue placeholder="Select" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="team">Team</SelectItem>
|
||||
<SelectItem value="billing">Billing</SelectItem>
|
||||
<SelectItem value="account">Account</SelectItem>
|
||||
<SelectItem value="deployments">Deployments</SelectItem>
|
||||
<SelectItem value="support">Support</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel htmlFor={`security-level-${id}`}>
|
||||
Security Level
|
||||
</FieldLabel>
|
||||
<Select defaultValue="2">
|
||||
<SelectTrigger
|
||||
id={`security-level-${id}`}
|
||||
className="w-full [&_span]:block! [&_span]:truncate"
|
||||
aria-label="Security Level"
|
||||
>
|
||||
<SelectValue placeholder="Select level" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="1">Severity 1 (Highest)</SelectItem>
|
||||
<SelectItem value="2">Severity 2</SelectItem>
|
||||
<SelectItem value="3">Severity 3</SelectItem>
|
||||
<SelectItem value="4">Severity 4 (Lowest)</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
<Field>
|
||||
<FieldLabel htmlFor={`subject-${id}`}>Subject</FieldLabel>
|
||||
<Input id={`subject-${id}`} placeholder="I need help with..." />
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel htmlFor={`description-${id}`}>Description</FieldLabel>
|
||||
<Textarea
|
||||
id={`description-${id}`}
|
||||
placeholder="Please include all information relevant to your issue."
|
||||
className="min-h-24"
|
||||
/>
|
||||
</Field>
|
||||
<Field orientation="horizontal" className="justify-end">
|
||||
<Button variant="ghost" size="sm">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button size="sm">Submit</Button>
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
117
apps/v4/components/cards/share.tsx
Normal file
117
apps/v4/components/cards/share.tsx
Normal file
@@ -0,0 +1,117 @@
|
||||
"use client"
|
||||
|
||||
import {
|
||||
Avatar,
|
||||
AvatarFallback,
|
||||
AvatarImage,
|
||||
} from "@/registry/new-york-v4/ui/avatar"
|
||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/registry/new-york-v4/ui/card"
|
||||
import { Input } from "@/registry/new-york-v4/ui/input"
|
||||
import {
|
||||
Item,
|
||||
ItemActions,
|
||||
ItemContent,
|
||||
ItemDescription,
|
||||
ItemGroup,
|
||||
ItemTitle,
|
||||
} from "@/registry/new-york-v4/ui/item"
|
||||
import { Label } from "@/registry/new-york-v4/ui/label"
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/registry/new-york-v4/ui/select"
|
||||
import { Separator } from "@/registry/new-york-v4/ui/separator"
|
||||
|
||||
const people = [
|
||||
{
|
||||
name: "Olivia Martin",
|
||||
email: "m@example.com",
|
||||
avatar: "/avatars/03.png",
|
||||
},
|
||||
{
|
||||
name: "Isabella Nguyen",
|
||||
email: "b@example.com",
|
||||
avatar: "/avatars/04.png",
|
||||
},
|
||||
{
|
||||
name: "Sofia Davis",
|
||||
email: "p@example.com",
|
||||
avatar: "/avatars/05.png",
|
||||
},
|
||||
{
|
||||
name: "Ethan Thompson",
|
||||
email: "e@example.com",
|
||||
avatar: "/avatars/01.png",
|
||||
},
|
||||
]
|
||||
export function CardsShare() {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Share this document</CardTitle>
|
||||
<CardDescription>
|
||||
Anyone with the link can view this document.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex items-center gap-2">
|
||||
<Label htmlFor="link" className="sr-only">
|
||||
Link
|
||||
</Label>
|
||||
<Input
|
||||
id="link"
|
||||
value="http://example.com/link/to/document"
|
||||
className="h-8"
|
||||
readOnly
|
||||
/>
|
||||
<Button size="sm" variant="outline" className="shadow-none">
|
||||
Copy Link
|
||||
</Button>
|
||||
</div>
|
||||
<Separator className="my-4" />
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="text-sm font-medium">People with access</div>
|
||||
<ItemGroup>
|
||||
{people.map((person) => (
|
||||
<Item key={person.email} className="px-0 py-2">
|
||||
<Avatar>
|
||||
<AvatarImage src={person.avatar} alt="Image" />
|
||||
<AvatarFallback>{person.name.charAt(0)}</AvatarFallback>
|
||||
</Avatar>
|
||||
<ItemContent>
|
||||
<ItemTitle>{person.name}</ItemTitle>
|
||||
<ItemDescription>{person.email}</ItemDescription>
|
||||
</ItemContent>
|
||||
<ItemActions>
|
||||
<Select defaultValue="edit">
|
||||
<SelectTrigger
|
||||
className="ml-auto pr-2"
|
||||
aria-label="Edit"
|
||||
size="sm"
|
||||
>
|
||||
<SelectValue placeholder="Select" />
|
||||
</SelectTrigger>
|
||||
<SelectContent align="end">
|
||||
<SelectItem value="edit">Can edit</SelectItem>
|
||||
<SelectItem value="view">Can view</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</ItemActions>
|
||||
</Item>
|
||||
))}
|
||||
</ItemGroup>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
132
apps/v4/components/cards/stats.tsx
Normal file
132
apps/v4/components/cards/stats.tsx
Normal file
@@ -0,0 +1,132 @@
|
||||
"use client"
|
||||
|
||||
import { Area, AreaChart, Line, LineChart } from "recharts"
|
||||
|
||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
||||
import {
|
||||
Card,
|
||||
CardAction,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/registry/new-york-v4/ui/card"
|
||||
import {
|
||||
ChartContainer,
|
||||
type ChartConfig,
|
||||
} from "@/registry/new-york-v4/ui/chart"
|
||||
|
||||
const data = [
|
||||
{
|
||||
revenue: 10400,
|
||||
subscription: 40,
|
||||
},
|
||||
{
|
||||
revenue: 14405,
|
||||
subscription: 90,
|
||||
},
|
||||
{
|
||||
revenue: 9400,
|
||||
subscription: 200,
|
||||
},
|
||||
{
|
||||
revenue: 8200,
|
||||
subscription: 278,
|
||||
},
|
||||
{
|
||||
revenue: 7000,
|
||||
subscription: 89,
|
||||
},
|
||||
{
|
||||
revenue: 9600,
|
||||
subscription: 239,
|
||||
},
|
||||
{
|
||||
revenue: 11244,
|
||||
subscription: 78,
|
||||
},
|
||||
{
|
||||
revenue: 26475,
|
||||
subscription: 89,
|
||||
},
|
||||
]
|
||||
|
||||
const chartConfig = {
|
||||
revenue: {
|
||||
label: "Revenue",
|
||||
color: "var(--primary)",
|
||||
},
|
||||
subscription: {
|
||||
label: "Subscriptions",
|
||||
color: "var(--primary)",
|
||||
},
|
||||
} satisfies ChartConfig
|
||||
|
||||
export function CardsStats() {
|
||||
return (
|
||||
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-1 xl:grid-cols-2">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardDescription>Total Revenue</CardDescription>
|
||||
<CardTitle className="text-3xl">$15,231.89</CardTitle>
|
||||
<CardDescription>+20.1% from last month</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="pb-0">
|
||||
<ChartContainer config={chartConfig} className="h-[80px] w-full">
|
||||
<LineChart
|
||||
data={data}
|
||||
margin={{
|
||||
top: 5,
|
||||
right: 10,
|
||||
left: 10,
|
||||
bottom: 0,
|
||||
}}
|
||||
>
|
||||
<Line
|
||||
type="monotone"
|
||||
strokeWidth={2}
|
||||
dataKey="revenue"
|
||||
stroke="var(--color-revenue)"
|
||||
activeDot={{
|
||||
r: 6,
|
||||
}}
|
||||
/>
|
||||
</LineChart>
|
||||
</ChartContainer>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card className="pb-0 lg:hidden xl:flex">
|
||||
<CardHeader>
|
||||
<CardDescription>Subscriptions</CardDescription>
|
||||
<CardTitle className="text-3xl">+2,350</CardTitle>
|
||||
<CardDescription>+180.1% from last month</CardDescription>
|
||||
<CardAction>
|
||||
<Button variant="ghost" size="sm">
|
||||
View More
|
||||
</Button>
|
||||
</CardAction>
|
||||
</CardHeader>
|
||||
<CardContent className="mt-auto max-h-[124px] flex-1 p-0">
|
||||
<ChartContainer config={chartConfig} className="size-full">
|
||||
<AreaChart
|
||||
data={data}
|
||||
margin={{
|
||||
left: 0,
|
||||
right: 0,
|
||||
}}
|
||||
>
|
||||
<Area
|
||||
dataKey="subscription"
|
||||
fill="var(--color-subscription)"
|
||||
fillOpacity={0.05}
|
||||
stroke="var(--color-subscription)"
|
||||
strokeWidth={2}
|
||||
type="monotone"
|
||||
/>
|
||||
</AreaChart>
|
||||
</ChartContainer>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
142
apps/v4/components/cards/team-members.tsx
Normal file
142
apps/v4/components/cards/team-members.tsx
Normal file
@@ -0,0 +1,142 @@
|
||||
"use client"
|
||||
|
||||
import { ChevronDownIcon } from "lucide-react"
|
||||
|
||||
import {
|
||||
Avatar,
|
||||
AvatarFallback,
|
||||
AvatarImage,
|
||||
} from "@/registry/new-york-v4/ui/avatar"
|
||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/registry/new-york-v4/ui/card"
|
||||
import {
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
} from "@/registry/new-york-v4/ui/command"
|
||||
import {
|
||||
Item,
|
||||
ItemActions,
|
||||
ItemContent,
|
||||
ItemDescription,
|
||||
ItemGroup,
|
||||
ItemTitle,
|
||||
} from "@/registry/new-york-v4/ui/item"
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/registry/new-york-v4/ui/popover"
|
||||
|
||||
const teamMembers = [
|
||||
{
|
||||
name: "Sofia Davis",
|
||||
email: "m@example.com",
|
||||
avatar: "/avatars/01.png",
|
||||
role: "Owner",
|
||||
},
|
||||
{
|
||||
name: "Jackson Lee",
|
||||
email: "p@example.com",
|
||||
avatar: "/avatars/02.png",
|
||||
role: "Developer",
|
||||
},
|
||||
{
|
||||
name: "Isabella Nguyen",
|
||||
email: "i@example.com",
|
||||
avatar: "/avatars/03.png",
|
||||
role: "Billing",
|
||||
},
|
||||
]
|
||||
|
||||
const roles = [
|
||||
{
|
||||
name: "Viewer",
|
||||
description: "Can view and comment.",
|
||||
},
|
||||
{
|
||||
name: "Developer",
|
||||
description: "Can view, comment and edit.",
|
||||
},
|
||||
{
|
||||
name: "Billing",
|
||||
description: "Can view, comment and manage billing.",
|
||||
},
|
||||
{
|
||||
name: "Owner",
|
||||
description: "Admin-level access to all resources.",
|
||||
},
|
||||
]
|
||||
|
||||
export function CardsTeamMembers() {
|
||||
return (
|
||||
<Card className="gap-4">
|
||||
<CardHeader>
|
||||
<CardTitle>Team Members</CardTitle>
|
||||
<CardDescription>
|
||||
Invite your team members to collaborate.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ItemGroup>
|
||||
{teamMembers.map((member) => (
|
||||
<Item key={member.name} className="px-0 py-2">
|
||||
<Avatar className="border">
|
||||
<AvatarImage src={member.avatar} alt="Image" />
|
||||
<AvatarFallback>{member.name.charAt(0)}</AvatarFallback>
|
||||
</Avatar>
|
||||
<ItemContent>
|
||||
<ItemTitle>{member.name}</ItemTitle>
|
||||
<ItemDescription>{member.email}</ItemDescription>
|
||||
</ItemContent>
|
||||
<ItemActions>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="ml-auto shadow-none"
|
||||
>
|
||||
{member.role} <ChevronDownIcon />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="p-0" align="end">
|
||||
<Command>
|
||||
<CommandInput placeholder="Select role..." />
|
||||
<CommandList>
|
||||
<CommandEmpty>No roles found.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{roles.map((role) => (
|
||||
<CommandItem key={role.name}>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-sm font-medium">
|
||||
{role.name}
|
||||
</span>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{role.description}
|
||||
</span>
|
||||
</div>
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</ItemActions>
|
||||
</Item>
|
||||
))}
|
||||
</ItemGroup>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user