@viliha/vui-ui 1.1.8 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENT.md +79 -9
- package/README.md +60 -0
- package/bin/vui.mjs +211 -0
- package/package.json +10 -2
- package/src/record-view.tsx +59 -17
- package/template/.env.example +20 -0
- package/template/app/(app)/branches/branches-table.tsx +69 -0
- package/template/app/(app)/branches/page.tsx +13 -0
- package/template/app/(app)/businesses/businesses-table.tsx +39 -0
- package/template/app/(app)/businesses/page.tsx +13 -0
- package/template/app/(app)/calendar/page.tsx +725 -0
- package/template/app/(app)/charts/charts-content.tsx +190 -0
- package/template/app/(app)/charts/layout.tsx +8 -0
- package/template/app/(app)/charts/page.tsx +44 -0
- package/template/app/(app)/chat/layout.tsx +14 -0
- package/template/app/(app)/chat/page.tsx +311 -0
- package/template/app/(app)/components/layout.tsx +8 -0
- package/template/app/(app)/components/page.tsx +464 -0
- package/template/app/(app)/crm/companies/companies-table.tsx +56 -0
- package/template/app/(app)/crm/companies/page.tsx +13 -0
- package/template/app/(app)/crm/opportunities/opportunities-board.tsx +359 -0
- package/template/app/(app)/crm/opportunities/page.tsx +13 -0
- package/template/app/(app)/crm/people/page.tsx +13 -0
- package/template/app/(app)/crm/people/people-table.tsx +50 -0
- package/template/app/(app)/dashboard/layout.tsx +8 -0
- package/template/app/(app)/dashboard/page.tsx +239 -0
- package/template/app/(app)/departments/departments-table.tsx +55 -0
- package/template/app/(app)/departments/page.tsx +13 -0
- package/template/app/(app)/employees/employees-table.tsx +64 -0
- package/template/app/(app)/employees/page.tsx +13 -0
- package/template/app/(app)/forms/layout.tsx +8 -0
- package/template/app/(app)/forms/page.tsx +388 -0
- package/template/app/(app)/layout.tsx +74 -0
- package/template/app/(app)/markets/markets-table.tsx +76 -0
- package/template/app/(app)/markets/page.tsx +13 -0
- package/template/app/(app)/organizations/edit/page.tsx +72 -0
- package/template/app/(app)/organizations/new/page.tsx +46 -0
- package/template/app/(app)/organizations/organizations-config.tsx +97 -0
- package/template/app/(app)/organizations/organizations-table.tsx +42 -0
- package/template/app/(app)/organizations/page.tsx +13 -0
- package/template/app/(app)/settings/layout.tsx +8 -0
- package/template/app/(app)/settings/page.tsx +255 -0
- package/template/app/(app)/steps/page.tsx +263 -0
- package/template/app/(app)/support/layout.tsx +14 -0
- package/template/app/(app)/support/page.tsx +345 -0
- package/template/app/(app)/system/cities/cities-table.tsx +34 -0
- package/template/app/(app)/system/cities/page.tsx +13 -0
- package/template/app/(app)/system/countries/countries-table.tsx +34 -0
- package/template/app/(app)/system/countries/page.tsx +13 -0
- package/template/app/(app)/system/currencies/currencies-table.tsx +34 -0
- package/template/app/(app)/system/currencies/page.tsx +13 -0
- package/template/app/(app)/system/languages/languages-table.tsx +32 -0
- package/template/app/(app)/system/languages/page.tsx +13 -0
- package/template/app/(app)/system/regions/page.tsx +13 -0
- package/template/app/(app)/system/regions/regions-table.tsx +32 -0
- package/template/app/(app)/users/page.tsx +13 -0
- package/template/app/(app)/users/users-table.tsx +97 -0
- package/template/app/_components/app-sidebar.tsx +565 -0
- package/template/app/_components/auth.tsx +184 -0
- package/template/app/_components/breadcrumbs.tsx +26 -0
- package/template/app/_components/global-search.tsx +209 -0
- package/template/app/_components/logo.tsx +49 -0
- package/template/app/_components/nav-config.ts +138 -0
- package/template/app/_components/open-tabs.tsx +530 -0
- package/template/app/_components/quick-actions.tsx +140 -0
- package/template/app/_components/route-meta.ts +137 -0
- package/template/app/_components/set-page-title.tsx +15 -0
- package/template/app/_components/showcase.tsx +29 -0
- package/template/app/_components/stat-card.tsx +56 -0
- package/template/app/_components/theme-toggle.tsx +46 -0
- package/template/app/_components/top-bar.tsx +109 -0
- package/template/app/_components/user-menu.tsx +109 -0
- package/template/app/_components/wordmark.tsx +45 -0
- package/template/app/apple-icon.png +0 -0
- package/template/app/auth/forgot-password/page.tsx +102 -0
- package/template/app/auth/layout.tsx +43 -0
- package/template/app/auth/page.tsx +13 -0
- package/template/app/auth/reset-password/page.tsx +108 -0
- package/template/app/auth/signin/page.tsx +238 -0
- package/template/app/auth/signup/page.tsx +159 -0
- package/template/app/auth/verify/page.tsx +86 -0
- package/template/app/error.tsx +56 -0
- package/template/app/globals.css +6 -0
- package/template/app/icon.png +0 -0
- package/template/app/icon.svg +4 -0
- package/template/app/layout.tsx +103 -0
- package/template/app/not-found.tsx +29 -0
- package/template/app/onboarding/layout.tsx +31 -0
- package/template/app/onboarding/page.tsx +399 -0
- package/template/app/page.tsx +13 -0
- package/template/app/register-business/layout.tsx +30 -0
- package/template/app/register-business/page.tsx +234 -0
- package/template/components/ui/accordion.tsx +66 -0
- package/template/components/ui/alert-dialog.tsx +196 -0
- package/template/components/ui/alert.tsx +66 -0
- package/template/components/ui/aspect-ratio.tsx +11 -0
- package/template/components/ui/avatar.tsx +109 -0
- package/template/components/ui/badge.tsx +48 -0
- package/template/components/ui/breadcrumb.tsx +109 -0
- package/template/components/ui/button.tsx +64 -0
- package/template/components/ui/calendar.tsx +220 -0
- package/template/components/ui/card.tsx +92 -0
- package/template/components/ui/checkbox.tsx +32 -0
- package/template/components/ui/collapsible.tsx +33 -0
- package/template/components/ui/command.tsx +184 -0
- package/template/components/ui/dialog.tsx +158 -0
- package/template/components/ui/dropdown-menu.tsx +257 -0
- package/template/components/ui/form.tsx +167 -0
- package/template/components/ui/hover-card.tsx +44 -0
- package/template/components/ui/input-otp.tsx +77 -0
- package/template/components/ui/input.tsx +21 -0
- package/template/components/ui/label.tsx +24 -0
- package/template/components/ui/popover.tsx +89 -0
- package/template/components/ui/progress.tsx +31 -0
- package/template/components/ui/radio-group.tsx +45 -0
- package/template/components/ui/scroll-area.tsx +58 -0
- package/template/components/ui/select.tsx +190 -0
- package/template/components/ui/separator.tsx +28 -0
- package/template/components/ui/sheet.tsx +143 -0
- package/template/components/ui/skeleton.tsx +13 -0
- package/template/components/ui/slider.tsx +63 -0
- package/template/components/ui/sonner.tsx +40 -0
- package/template/components/ui/switch.tsx +35 -0
- package/template/components/ui/table.tsx +116 -0
- package/template/components/ui/tabs.tsx +91 -0
- package/template/components/ui/textarea.tsx +18 -0
- package/template/components/ui/toggle-group.tsx +83 -0
- package/template/components/ui/toggle.tsx +47 -0
- package/template/components/ui/tooltip.tsx +57 -0
- package/template/lib/auth-demo.ts +94 -0
- package/template/lib/crm-data.ts +80 -0
- package/template/lib/demo-data.ts +267 -0
- package/template/lib/mock-data.ts +169 -0
- package/template/lib/org-store.ts +50 -0
- package/template/lib/seo.ts +188 -0
- package/template/lib/utils.ts +3 -0
- package/template/next.config.mjs +13 -0
- package/template/postcss.config.mjs +7 -0
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { MixIcon } from "@radix-ui/react-icons";
|
|
5
|
+
import { toast } from "sonner";
|
|
6
|
+
|
|
7
|
+
import { Breadcrumbs } from "@/app/_components/breadcrumbs";
|
|
8
|
+
import { SetPageTitle } from "@/app/_components/set-page-title";
|
|
9
|
+
import { Demo } from "@/app/_components/showcase";
|
|
10
|
+
|
|
11
|
+
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion";
|
|
12
|
+
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
|
13
|
+
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog";
|
|
14
|
+
import { AspectRatio } from "@/components/ui/aspect-ratio";
|
|
15
|
+
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
|
16
|
+
import { Badge } from "@/components/ui/badge";
|
|
17
|
+
import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from "@/components/ui/breadcrumb";
|
|
18
|
+
import { Button } from "@/components/ui/button";
|
|
19
|
+
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
|
|
20
|
+
import { Checkbox } from "@/components/ui/checkbox";
|
|
21
|
+
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
|
|
22
|
+
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
|
|
23
|
+
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
|
|
24
|
+
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
|
|
25
|
+
import { HoverCard, HoverCardContent, HoverCardTrigger } from "@/components/ui/hover-card";
|
|
26
|
+
import { Input } from "@/components/ui/input";
|
|
27
|
+
import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from "@/components/ui/input-otp";
|
|
28
|
+
import { Label } from "@/components/ui/label";
|
|
29
|
+
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
|
30
|
+
import { Progress } from "@/components/ui/progress";
|
|
31
|
+
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
|
32
|
+
import { ScrollArea } from "@/components/ui/scroll-area";
|
|
33
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
|
34
|
+
import { Separator } from "@/components/ui/separator";
|
|
35
|
+
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger } from "@/components/ui/sheet";
|
|
36
|
+
import { Skeleton } from "@/components/ui/skeleton";
|
|
37
|
+
import { Slider } from "@/components/ui/slider";
|
|
38
|
+
import { Toaster } from "@/components/ui/sonner";
|
|
39
|
+
import { Switch } from "@/components/ui/switch";
|
|
40
|
+
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
|
41
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
|
42
|
+
import { Textarea } from "@/components/ui/textarea";
|
|
43
|
+
import { Toggle } from "@/components/ui/toggle";
|
|
44
|
+
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
|
|
45
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
|
46
|
+
|
|
47
|
+
export default function ComponentsPage() {
|
|
48
|
+
const [progress, setProgress] = React.useState(13);
|
|
49
|
+
React.useEffect(() => {
|
|
50
|
+
const t = setTimeout(() => setProgress(66), 500);
|
|
51
|
+
return () => clearTimeout(t);
|
|
52
|
+
}, []);
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<TooltipProvider>
|
|
56
|
+
<div className="flex h-full flex-col">
|
|
57
|
+
<SetPageTitle title="Components" icon={MixIcon} />
|
|
58
|
+
<Toaster />
|
|
59
|
+
|
|
60
|
+
{/* Action header */}
|
|
61
|
+
<div className="flex h-12 shrink-0 items-center justify-between gap-3 border-b border-border px-4">
|
|
62
|
+
<Breadcrumbs />
|
|
63
|
+
<span className="hidden truncate text-muted-foreground md:block">
|
|
64
|
+
Unmodified shadcn/ui components — auto-themed by theme.css.
|
|
65
|
+
</span>
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
{/* Content */}
|
|
69
|
+
<div className="min-h-0 flex-1 overflow-y-auto">
|
|
70
|
+
<div className="flex flex-col gap-4 p-4">
|
|
71
|
+
<div className="rounded-lg border border-border bg-muted/40 px-4 py-3 text-sm leading-relaxed text-muted-foreground">
|
|
72
|
+
Every component below was added straight from the shadcn CLI
|
|
73
|
+
(<code className="font-mono text-foreground">npx shadcn@latest add …</code>){" "}
|
|
74
|
+
and rendered <strong className="text-foreground">unedited</strong>.
|
|
75
|
+
They inherit Vui's colors, radius, fonts, borders and dark
|
|
76
|
+
mode from <code className="font-mono text-foreground">theme.css</code>{" "}
|
|
77
|
+
because they read the same design tokens. Add your own{" "}
|
|
78
|
+
<code className="font-mono text-foreground">className</code> to
|
|
79
|
+
override anything.
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
{/* ---- Overlays (Dialog first — the requested proof) ---- */}
|
|
83
|
+
<Demo
|
|
84
|
+
title="Dialog"
|
|
85
|
+
desc="The proof: an unmodified shadcn dialog, themed by our tokens."
|
|
86
|
+
code={`import { Dialog, DialogTrigger, DialogContent, DialogHeader,
|
|
87
|
+
DialogTitle, DialogDescription, DialogFooter, DialogClose } from "@/components/ui/dialog";
|
|
88
|
+
|
|
89
|
+
<Dialog>
|
|
90
|
+
<DialogTrigger asChild><Button>Open dialog</Button></DialogTrigger>
|
|
91
|
+
<DialogContent>
|
|
92
|
+
<DialogHeader>
|
|
93
|
+
<DialogTitle>Invite teammate</DialogTitle>
|
|
94
|
+
<DialogDescription>Send an invite by email.</DialogDescription>
|
|
95
|
+
</DialogHeader>
|
|
96
|
+
<Input placeholder="you@example.com" />
|
|
97
|
+
<DialogFooter>
|
|
98
|
+
<DialogClose asChild><Button variant="outline">Cancel</Button></DialogClose>
|
|
99
|
+
<Button>Send invite</Button>
|
|
100
|
+
</DialogFooter>
|
|
101
|
+
</DialogContent>
|
|
102
|
+
</Dialog>`}
|
|
103
|
+
>
|
|
104
|
+
<Dialog>
|
|
105
|
+
<DialogTrigger asChild>
|
|
106
|
+
<Button>Open dialog</Button>
|
|
107
|
+
</DialogTrigger>
|
|
108
|
+
<DialogContent>
|
|
109
|
+
<DialogHeader>
|
|
110
|
+
<DialogTitle>Invite teammate</DialogTitle>
|
|
111
|
+
<DialogDescription>Send an invite by email.</DialogDescription>
|
|
112
|
+
</DialogHeader>
|
|
113
|
+
<Input placeholder="you@example.com" />
|
|
114
|
+
<DialogFooter>
|
|
115
|
+
<DialogClose asChild>
|
|
116
|
+
<Button variant="outline">Cancel</Button>
|
|
117
|
+
</DialogClose>
|
|
118
|
+
<Button>Send invite</Button>
|
|
119
|
+
</DialogFooter>
|
|
120
|
+
</DialogContent>
|
|
121
|
+
</Dialog>
|
|
122
|
+
</Demo>
|
|
123
|
+
|
|
124
|
+
<Demo title="Alert dialog" desc="Confirm destructive actions.">
|
|
125
|
+
<AlertDialog>
|
|
126
|
+
<AlertDialogTrigger asChild>
|
|
127
|
+
<Button variant="destructive">Delete</Button>
|
|
128
|
+
</AlertDialogTrigger>
|
|
129
|
+
<AlertDialogContent>
|
|
130
|
+
<AlertDialogHeader>
|
|
131
|
+
<AlertDialogTitle>Are you sure?</AlertDialogTitle>
|
|
132
|
+
<AlertDialogDescription>
|
|
133
|
+
This permanently deletes the record.
|
|
134
|
+
</AlertDialogDescription>
|
|
135
|
+
</AlertDialogHeader>
|
|
136
|
+
<AlertDialogFooter>
|
|
137
|
+
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
|
138
|
+
<AlertDialogAction>Delete</AlertDialogAction>
|
|
139
|
+
</AlertDialogFooter>
|
|
140
|
+
</AlertDialogContent>
|
|
141
|
+
</AlertDialog>
|
|
142
|
+
</Demo>
|
|
143
|
+
|
|
144
|
+
<Demo title="Sheet" desc="Side panel / drawer.">
|
|
145
|
+
<Sheet>
|
|
146
|
+
<SheetTrigger asChild>
|
|
147
|
+
<Button variant="outline">Open sheet</Button>
|
|
148
|
+
</SheetTrigger>
|
|
149
|
+
<SheetContent>
|
|
150
|
+
<SheetHeader>
|
|
151
|
+
<SheetTitle>Filters</SheetTitle>
|
|
152
|
+
<SheetDescription>Refine the current view.</SheetDescription>
|
|
153
|
+
</SheetHeader>
|
|
154
|
+
</SheetContent>
|
|
155
|
+
</Sheet>
|
|
156
|
+
</Demo>
|
|
157
|
+
|
|
158
|
+
<Demo title="Popover / Hover card / Tooltip">
|
|
159
|
+
<Popover>
|
|
160
|
+
<PopoverTrigger asChild>
|
|
161
|
+
<Button variant="outline">Popover</Button>
|
|
162
|
+
</PopoverTrigger>
|
|
163
|
+
<PopoverContent>Anchored popover content.</PopoverContent>
|
|
164
|
+
</Popover>
|
|
165
|
+
<HoverCard>
|
|
166
|
+
<HoverCardTrigger asChild>
|
|
167
|
+
<Button variant="link">Hover me</Button>
|
|
168
|
+
</HoverCardTrigger>
|
|
169
|
+
<HoverCardContent>Rich preview on hover.</HoverCardContent>
|
|
170
|
+
</HoverCard>
|
|
171
|
+
<Tooltip>
|
|
172
|
+
<TooltipTrigger asChild>
|
|
173
|
+
<Button variant="secondary">Tooltip</Button>
|
|
174
|
+
</TooltipTrigger>
|
|
175
|
+
<TooltipContent>A short hint.</TooltipContent>
|
|
176
|
+
</Tooltip>
|
|
177
|
+
<DropdownMenu>
|
|
178
|
+
<DropdownMenuTrigger asChild>
|
|
179
|
+
<Button variant="outline">Menu</Button>
|
|
180
|
+
</DropdownMenuTrigger>
|
|
181
|
+
<DropdownMenuContent>
|
|
182
|
+
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
|
183
|
+
<DropdownMenuItem>Edit</DropdownMenuItem>
|
|
184
|
+
<DropdownMenuItem>Duplicate</DropdownMenuItem>
|
|
185
|
+
<DropdownMenuSeparator />
|
|
186
|
+
<DropdownMenuItem variant="destructive">Delete</DropdownMenuItem>
|
|
187
|
+
</DropdownMenuContent>
|
|
188
|
+
</DropdownMenu>
|
|
189
|
+
</Demo>
|
|
190
|
+
|
|
191
|
+
{/* ---- Buttons & toggles ---- */}
|
|
192
|
+
<Demo
|
|
193
|
+
title="Button"
|
|
194
|
+
code={`<Button>Default</Button>
|
|
195
|
+
<Button variant="secondary" /><Button variant="destructive" />
|
|
196
|
+
<Button variant="outline" /><Button variant="ghost" /><Button variant="link" />`}
|
|
197
|
+
>
|
|
198
|
+
<Button>Default</Button>
|
|
199
|
+
<Button variant="secondary">Secondary</Button>
|
|
200
|
+
<Button variant="destructive">Destructive</Button>
|
|
201
|
+
<Button variant="outline">Outline</Button>
|
|
202
|
+
<Button variant="ghost">Ghost</Button>
|
|
203
|
+
<Button variant="link">Link</Button>
|
|
204
|
+
</Demo>
|
|
205
|
+
|
|
206
|
+
<Demo title="Toggle / Toggle group">
|
|
207
|
+
<Toggle aria-label="Bold">B</Toggle>
|
|
208
|
+
<ToggleGroup type="single" defaultValue="left">
|
|
209
|
+
<ToggleGroupItem value="left">Left</ToggleGroupItem>
|
|
210
|
+
<ToggleGroupItem value="center">Center</ToggleGroupItem>
|
|
211
|
+
<ToggleGroupItem value="right">Right</ToggleGroupItem>
|
|
212
|
+
</ToggleGroup>
|
|
213
|
+
</Demo>
|
|
214
|
+
|
|
215
|
+
<Demo title="Toast (Sonner)">
|
|
216
|
+
<Button
|
|
217
|
+
variant="outline"
|
|
218
|
+
onClick={() =>
|
|
219
|
+
toast("Saved", { description: "Your changes were saved." })
|
|
220
|
+
}
|
|
221
|
+
>
|
|
222
|
+
Show toast
|
|
223
|
+
</Button>
|
|
224
|
+
</Demo>
|
|
225
|
+
|
|
226
|
+
{/* ---- Display ---- */}
|
|
227
|
+
<Demo title="Badge">
|
|
228
|
+
<Badge>Default</Badge>
|
|
229
|
+
<Badge variant="secondary">Secondary</Badge>
|
|
230
|
+
<Badge variant="destructive">Destructive</Badge>
|
|
231
|
+
<Badge variant="outline">Outline</Badge>
|
|
232
|
+
</Demo>
|
|
233
|
+
|
|
234
|
+
<Demo title="Alert">
|
|
235
|
+
<div className="w-full space-y-3">
|
|
236
|
+
<Alert>
|
|
237
|
+
<AlertTitle>Heads up</AlertTitle>
|
|
238
|
+
<AlertDescription>This is an informational alert.</AlertDescription>
|
|
239
|
+
</Alert>
|
|
240
|
+
<Alert variant="destructive">
|
|
241
|
+
<AlertTitle>Error</AlertTitle>
|
|
242
|
+
<AlertDescription>Something went wrong.</AlertDescription>
|
|
243
|
+
</Alert>
|
|
244
|
+
</div>
|
|
245
|
+
</Demo>
|
|
246
|
+
|
|
247
|
+
<Demo title="Card">
|
|
248
|
+
<Card className="w-72">
|
|
249
|
+
<CardHeader>
|
|
250
|
+
<CardTitle>Team plan</CardTitle>
|
|
251
|
+
<CardDescription>$29 / month</CardDescription>
|
|
252
|
+
</CardHeader>
|
|
253
|
+
<CardContent className="text-sm text-muted-foreground">
|
|
254
|
+
Everything in Pro, plus shared workspaces.
|
|
255
|
+
</CardContent>
|
|
256
|
+
<CardFooter>
|
|
257
|
+
<Button className="w-full">Upgrade</Button>
|
|
258
|
+
</CardFooter>
|
|
259
|
+
</Card>
|
|
260
|
+
</Demo>
|
|
261
|
+
|
|
262
|
+
<Demo title="Avatar / Separator / Skeleton / Progress">
|
|
263
|
+
<Avatar>
|
|
264
|
+
<AvatarFallback>SB</AvatarFallback>
|
|
265
|
+
</Avatar>
|
|
266
|
+
<Separator orientation="vertical" className="h-10" />
|
|
267
|
+
<div className="space-y-2">
|
|
268
|
+
<Skeleton className="h-4 w-40" />
|
|
269
|
+
<Skeleton className="h-4 w-28" />
|
|
270
|
+
</div>
|
|
271
|
+
<Progress value={progress} className="w-40" />
|
|
272
|
+
</Demo>
|
|
273
|
+
|
|
274
|
+
<Demo title="Aspect ratio">
|
|
275
|
+
<div className="w-64">
|
|
276
|
+
<AspectRatio
|
|
277
|
+
ratio={16 / 9}
|
|
278
|
+
className="grid place-items-center rounded-md bg-muted text-sm text-muted-foreground"
|
|
279
|
+
>
|
|
280
|
+
16 / 9
|
|
281
|
+
</AspectRatio>
|
|
282
|
+
</div>
|
|
283
|
+
</Demo>
|
|
284
|
+
|
|
285
|
+
<Demo title="Breadcrumb">
|
|
286
|
+
<Breadcrumb>
|
|
287
|
+
<BreadcrumbList>
|
|
288
|
+
<BreadcrumbItem>
|
|
289
|
+
<BreadcrumbLink href="#">Home</BreadcrumbLink>
|
|
290
|
+
</BreadcrumbItem>
|
|
291
|
+
<BreadcrumbSeparator />
|
|
292
|
+
<BreadcrumbItem>
|
|
293
|
+
<BreadcrumbLink href="#">Settings</BreadcrumbLink>
|
|
294
|
+
</BreadcrumbItem>
|
|
295
|
+
<BreadcrumbSeparator />
|
|
296
|
+
<BreadcrumbItem>
|
|
297
|
+
<BreadcrumbPage>Profile</BreadcrumbPage>
|
|
298
|
+
</BreadcrumbItem>
|
|
299
|
+
</BreadcrumbList>
|
|
300
|
+
</Breadcrumb>
|
|
301
|
+
</Demo>
|
|
302
|
+
|
|
303
|
+
{/* ---- Disclosure & navigation ---- */}
|
|
304
|
+
<Demo title="Tabs">
|
|
305
|
+
<Tabs defaultValue="account" className="w-80">
|
|
306
|
+
<TabsList>
|
|
307
|
+
<TabsTrigger value="account">Account</TabsTrigger>
|
|
308
|
+
<TabsTrigger value="password">Password</TabsTrigger>
|
|
309
|
+
</TabsList>
|
|
310
|
+
<TabsContent value="account" className="text-sm text-muted-foreground">
|
|
311
|
+
Manage your account details.
|
|
312
|
+
</TabsContent>
|
|
313
|
+
<TabsContent value="password" className="text-sm text-muted-foreground">
|
|
314
|
+
Change your password.
|
|
315
|
+
</TabsContent>
|
|
316
|
+
</Tabs>
|
|
317
|
+
</Demo>
|
|
318
|
+
|
|
319
|
+
<Demo title="Accordion">
|
|
320
|
+
<Accordion type="single" collapsible className="w-80">
|
|
321
|
+
<AccordionItem value="a">
|
|
322
|
+
<AccordionTrigger>What is Vui Starter?</AccordionTrigger>
|
|
323
|
+
<AccordionContent>A token-driven admin theme.</AccordionContent>
|
|
324
|
+
</AccordionItem>
|
|
325
|
+
<AccordionItem value="b">
|
|
326
|
+
<AccordionTrigger>Is it free?</AccordionTrigger>
|
|
327
|
+
<AccordionContent>Yes — MIT licensed.</AccordionContent>
|
|
328
|
+
</AccordionItem>
|
|
329
|
+
</Accordion>
|
|
330
|
+
</Demo>
|
|
331
|
+
|
|
332
|
+
<Demo title="Collapsible">
|
|
333
|
+
<Collapsible className="w-80 space-y-2">
|
|
334
|
+
<CollapsibleTrigger asChild>
|
|
335
|
+
<Button variant="outline">Toggle details</Button>
|
|
336
|
+
</CollapsibleTrigger>
|
|
337
|
+
<CollapsibleContent className="rounded-md border border-border p-3 text-sm text-muted-foreground">
|
|
338
|
+
Hidden content revealed on toggle.
|
|
339
|
+
</CollapsibleContent>
|
|
340
|
+
</Collapsible>
|
|
341
|
+
</Demo>
|
|
342
|
+
|
|
343
|
+
<Demo title="Scroll area">
|
|
344
|
+
<ScrollArea className="h-28 w-64 rounded-md border border-border p-3 text-sm">
|
|
345
|
+
{Array.from({ length: 12 }, (_, i) => (
|
|
346
|
+
<p key={i} className="py-1">
|
|
347
|
+
Row {i + 1}
|
|
348
|
+
</p>
|
|
349
|
+
))}
|
|
350
|
+
</ScrollArea>
|
|
351
|
+
</Demo>
|
|
352
|
+
|
|
353
|
+
<Demo title="Command">
|
|
354
|
+
<Command className="w-72 rounded-lg border border-border">
|
|
355
|
+
<CommandInput placeholder="Type a command…" />
|
|
356
|
+
<CommandList>
|
|
357
|
+
<CommandEmpty>No results.</CommandEmpty>
|
|
358
|
+
<CommandGroup heading="Suggestions">
|
|
359
|
+
<CommandItem>Calendar</CommandItem>
|
|
360
|
+
<CommandItem>Search</CommandItem>
|
|
361
|
+
<CommandItem>Settings</CommandItem>
|
|
362
|
+
</CommandGroup>
|
|
363
|
+
</CommandList>
|
|
364
|
+
</Command>
|
|
365
|
+
</Demo>
|
|
366
|
+
|
|
367
|
+
{/* ---- Form inputs (primitives) ---- */}
|
|
368
|
+
<Demo title="Input / Textarea / Label">
|
|
369
|
+
<div className="w-full max-w-sm space-y-3">
|
|
370
|
+
<div className="space-y-1.5">
|
|
371
|
+
<Label htmlFor="c-email">Email</Label>
|
|
372
|
+
<Input id="c-email" placeholder="you@example.com" />
|
|
373
|
+
</div>
|
|
374
|
+
<div className="space-y-1.5">
|
|
375
|
+
<Label htmlFor="c-note">Note</Label>
|
|
376
|
+
<Textarea id="c-note" placeholder="Write something…" />
|
|
377
|
+
</div>
|
|
378
|
+
</div>
|
|
379
|
+
</Demo>
|
|
380
|
+
|
|
381
|
+
<Demo title="Checkbox / Switch / Radio group">
|
|
382
|
+
<label htmlFor="c-terms" className="flex items-center gap-2 text-sm">
|
|
383
|
+
<Checkbox id="c-terms" defaultChecked /> Accept terms
|
|
384
|
+
</label>
|
|
385
|
+
<label htmlFor="c-notify" className="flex items-center gap-2 text-sm">
|
|
386
|
+
<Switch id="c-notify" defaultChecked /> Notifications
|
|
387
|
+
</label>
|
|
388
|
+
<RadioGroup defaultValue="a" className="flex gap-4">
|
|
389
|
+
<label htmlFor="c-opt-a" className="flex items-center gap-2 text-sm">
|
|
390
|
+
<RadioGroupItem id="c-opt-a" value="a" /> Option A
|
|
391
|
+
</label>
|
|
392
|
+
<label htmlFor="c-opt-b" className="flex items-center gap-2 text-sm">
|
|
393
|
+
<RadioGroupItem id="c-opt-b" value="b" /> Option B
|
|
394
|
+
</label>
|
|
395
|
+
</RadioGroup>
|
|
396
|
+
</Demo>
|
|
397
|
+
|
|
398
|
+
<Demo title="Select">
|
|
399
|
+
<Select>
|
|
400
|
+
<SelectTrigger className="w-48">
|
|
401
|
+
<SelectValue placeholder="Pick a role" />
|
|
402
|
+
</SelectTrigger>
|
|
403
|
+
<SelectContent>
|
|
404
|
+
<SelectItem value="admin">Administrator</SelectItem>
|
|
405
|
+
<SelectItem value="manager">Manager</SelectItem>
|
|
406
|
+
<SelectItem value="member">Member</SelectItem>
|
|
407
|
+
</SelectContent>
|
|
408
|
+
</Select>
|
|
409
|
+
</Demo>
|
|
410
|
+
|
|
411
|
+
<Demo title="Slider">
|
|
412
|
+
<Slider defaultValue={[40]} max={100} step={1} className="w-64" />
|
|
413
|
+
</Demo>
|
|
414
|
+
|
|
415
|
+
<Demo title="Input OTP">
|
|
416
|
+
<InputOTP maxLength={6}>
|
|
417
|
+
<InputOTPGroup>
|
|
418
|
+
<InputOTPSlot index={0} />
|
|
419
|
+
<InputOTPSlot index={1} />
|
|
420
|
+
<InputOTPSlot index={2} />
|
|
421
|
+
</InputOTPGroup>
|
|
422
|
+
<InputOTPSeparator />
|
|
423
|
+
<InputOTPGroup>
|
|
424
|
+
<InputOTPSlot index={3} />
|
|
425
|
+
<InputOTPSlot index={4} />
|
|
426
|
+
<InputOTPSlot index={5} />
|
|
427
|
+
</InputOTPGroup>
|
|
428
|
+
</InputOTP>
|
|
429
|
+
</Demo>
|
|
430
|
+
|
|
431
|
+
{/* ---- Data ---- */}
|
|
432
|
+
<Demo title="Table">
|
|
433
|
+
<Table>
|
|
434
|
+
<TableHeader>
|
|
435
|
+
<TableRow>
|
|
436
|
+
<TableHead>Name</TableHead>
|
|
437
|
+
<TableHead>Role</TableHead>
|
|
438
|
+
<TableHead className="text-right">Status</TableHead>
|
|
439
|
+
</TableRow>
|
|
440
|
+
</TableHeader>
|
|
441
|
+
<TableBody>
|
|
442
|
+
<TableRow>
|
|
443
|
+
<TableCell>Ada Lovelace</TableCell>
|
|
444
|
+
<TableCell>Admin</TableCell>
|
|
445
|
+
<TableCell className="text-right">
|
|
446
|
+
<Badge>Active</Badge>
|
|
447
|
+
</TableCell>
|
|
448
|
+
</TableRow>
|
|
449
|
+
<TableRow>
|
|
450
|
+
<TableCell>Alan Turing</TableCell>
|
|
451
|
+
<TableCell>Member</TableCell>
|
|
452
|
+
<TableCell className="text-right">
|
|
453
|
+
<Badge variant="secondary">Invited</Badge>
|
|
454
|
+
</TableCell>
|
|
455
|
+
</TableRow>
|
|
456
|
+
</TableBody>
|
|
457
|
+
</Table>
|
|
458
|
+
</Demo>
|
|
459
|
+
</div>
|
|
460
|
+
</div>
|
|
461
|
+
</div>
|
|
462
|
+
</TooltipProvider>
|
|
463
|
+
);
|
|
464
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
ArchiveIcon as Factory,
|
|
5
|
+
CubeIcon as Building,
|
|
6
|
+
GlobeIcon as Globe,
|
|
7
|
+
PersonIcon as Users,
|
|
8
|
+
SewingPinFilledIcon as MapPin,
|
|
9
|
+
} from "@radix-ui/react-icons";
|
|
10
|
+
|
|
11
|
+
import { RecordView, type RecordField } from "@viliha/vui-ui/record-view";
|
|
12
|
+
import { companies, type Company } from "@/lib/crm-data";
|
|
13
|
+
|
|
14
|
+
const fields: RecordField<Company>[] = [
|
|
15
|
+
{ key: "name", label: "Name", editable: true, required: true, group: "General", hideInTable: true },
|
|
16
|
+
{ key: "domain", label: "Domain", icon: Globe, editable: true, copyable: true, width: 210, group: "General" },
|
|
17
|
+
{ key: "industry", label: "Industry", icon: Factory, editable: true, group: "General" },
|
|
18
|
+
{ key: "city", label: "City", icon: MapPin, editable: true, group: "General" },
|
|
19
|
+
{ key: "country", label: "Country", icon: MapPin, editable: true, group: "General" },
|
|
20
|
+
{
|
|
21
|
+
key: "employees",
|
|
22
|
+
label: "Employees",
|
|
23
|
+
icon: Users,
|
|
24
|
+
// No explicit align — auto-aligns center (numeric count).
|
|
25
|
+
group: "System",
|
|
26
|
+
render: (row) => (
|
|
27
|
+
<span className="tabular-nums">{row.employees.toLocaleString()}</span>
|
|
28
|
+
),
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
export function CompaniesTable() {
|
|
33
|
+
return (
|
|
34
|
+
<RecordView
|
|
35
|
+
title="Companies"
|
|
36
|
+
singular="Company"
|
|
37
|
+
icon={Building}
|
|
38
|
+
fields={fields}
|
|
39
|
+
initialData={companies}
|
|
40
|
+
getPrimary={(row) => ({
|
|
41
|
+
title: row.name,
|
|
42
|
+
subtitle: row.domain,
|
|
43
|
+
initials: row.name.slice(0, 2).toUpperCase(),
|
|
44
|
+
})}
|
|
45
|
+
makeEmptyRow={() => ({
|
|
46
|
+
id: Date.now(),
|
|
47
|
+
name: "",
|
|
48
|
+
domain: "",
|
|
49
|
+
industry: "",
|
|
50
|
+
employees: 0,
|
|
51
|
+
city: "",
|
|
52
|
+
country: "",
|
|
53
|
+
})}
|
|
54
|
+
/>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { pageMeta } from "@/lib/seo";
|
|
2
|
+
|
|
3
|
+
import { CompaniesTable } from "./companies-table";
|
|
4
|
+
|
|
5
|
+
export const metadata = pageMeta("/crm/companies");
|
|
6
|
+
|
|
7
|
+
export default function CompaniesPage() {
|
|
8
|
+
return (
|
|
9
|
+
<main className="h-full">
|
|
10
|
+
<CompaniesTable />
|
|
11
|
+
</main>
|
|
12
|
+
);
|
|
13
|
+
}
|