@reinvented/design 0.3.0 → 1.0.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/package.json +55 -21
- package/src/components/blocks/auth/auth-card/AuthCard.tsx +62 -0
- package/src/components/blocks/auth/auth-card/index.ts +1 -0
- package/src/components/blocks/auth/index.ts +3 -0
- package/src/components/blocks/auth/social-login/SocialLoginGroup.tsx +75 -0
- package/src/components/blocks/auth/social-login/index.ts +1 -0
- package/src/components/blocks/auth/two-factor/TwoFactorForm.tsx +59 -0
- package/src/components/blocks/auth/two-factor/index.ts +1 -0
- package/src/components/blocks/communication/activity-item/ActivityItem.tsx +17 -0
- package/src/components/blocks/communication/activity-item/ActivityItemAvatar.tsx +21 -0
- package/src/components/blocks/communication/activity-item/ActivityItemContent.tsx +16 -0
- package/src/components/blocks/communication/activity-item/index.ts +3 -0
- package/src/components/blocks/communication/chat-bubble/ChatBubble.tsx +21 -0
- package/src/components/blocks/communication/chat-bubble/ChatBubbleAvatar.tsx +19 -0
- package/src/components/blocks/communication/chat-bubble/ChatBubbleMessage.tsx +35 -0
- package/src/components/blocks/communication/chat-bubble/ChatBubbleTimestamp.tsx +15 -0
- package/src/components/blocks/communication/chat-bubble/index.ts +4 -0
- package/src/components/blocks/communication/index.ts +2 -0
- package/src/components/blocks/content/article-header/ArticleHeader.tsx +73 -0
- package/src/components/blocks/content/article-header/index.ts +1 -0
- package/src/components/blocks/content/author-card/AuthorCard.tsx +46 -0
- package/src/components/blocks/content/author-card/index.ts +1 -0
- package/src/components/blocks/content/index.ts +3 -0
- package/src/components/blocks/content/rich-text/RichTextContent.tsx +27 -0
- package/src/components/blocks/content/rich-text/index.ts +1 -0
- package/src/components/blocks/data-management/index.ts +2 -0
- package/src/components/blocks/data-management/kanban/KanbanBoard.tsx +10 -0
- package/src/components/blocks/data-management/kanban/KanbanCard.tsx +102 -0
- package/src/components/blocks/data-management/kanban/KanbanColumn.tsx +41 -0
- package/src/components/blocks/data-management/kanban/index.ts +3 -0
- package/src/components/blocks/data-management/tree-view/TreeItem.tsx +68 -0
- package/src/components/blocks/data-management/tree-view/TreeView.tsx +10 -0
- package/src/components/blocks/data-management/tree-view/index.ts +2 -0
- package/src/components/blocks/data-viz/financial-ticker/FinancialTicker.tsx +35 -0
- package/src/components/blocks/data-viz/financial-ticker/index.ts +1 -0
- package/src/components/blocks/data-viz/index.ts +3 -0
- package/src/components/blocks/data-viz/kpi-card/KpiCard.tsx +61 -0
- package/src/components/blocks/data-viz/kpi-card/index.ts +1 -0
- package/src/components/blocks/data-viz/stat-grid/StatGrid.tsx +28 -0
- package/src/components/blocks/data-viz/stat-grid/index.ts +1 -0
- package/src/components/blocks/ecommerce/index.ts +3 -0
- package/src/components/blocks/ecommerce/pricing-tier/PricingFeatureList.tsx +27 -0
- package/src/components/blocks/ecommerce/pricing-tier/PricingHeader.tsx +25 -0
- package/src/components/blocks/ecommerce/pricing-tier/PricingTier.tsx +31 -0
- package/src/components/blocks/ecommerce/pricing-tier/index.ts +3 -0
- package/src/components/blocks/ecommerce/product-card/ProductCard.tsx +17 -0
- package/src/components/blocks/ecommerce/product-card/ProductCardContent.tsx +15 -0
- package/src/components/blocks/ecommerce/product-card/ProductCardImage.tsx +20 -0
- package/src/components/blocks/ecommerce/product-card/ProductCardPrice.tsx +20 -0
- package/src/components/blocks/ecommerce/product-card/index.ts +4 -0
- package/src/components/blocks/ecommerce/rating-summary/RatingStars.tsx +32 -0
- package/src/components/blocks/ecommerce/rating-summary/index.ts +1 -0
- package/src/components/blocks/feedback/empty-state/EmptyState.tsx +53 -0
- package/src/components/blocks/feedback/empty-state/index.ts +1 -0
- package/src/components/blocks/feedback/error-state/ErrorState.tsx +50 -0
- package/src/components/blocks/feedback/error-state/index.ts +1 -0
- package/src/components/blocks/feedback/index.ts +3 -0
- package/src/components/blocks/feedback/not-found/NotFoundState.tsx +47 -0
- package/src/components/blocks/feedback/not-found/index.ts +1 -0
- package/src/components/blocks/filtering/faceted-sidebar/FacetedGroup.tsx +32 -0
- package/src/components/blocks/filtering/faceted-sidebar/FacetedSidebar.tsx +26 -0
- package/src/components/blocks/filtering/faceted-sidebar/index.ts +2 -0
- package/src/components/blocks/filtering/filter-bar/FilterBar.tsx +85 -0
- package/src/components/blocks/filtering/filter-bar/index.ts +1 -0
- package/src/components/blocks/filtering/index.ts +3 -0
- package/src/components/blocks/filtering/search-result/SearchResultItem.tsx +70 -0
- package/src/components/blocks/filtering/search-result/index.ts +1 -0
- package/src/components/blocks/index.ts +21 -0
- package/src/components/blocks/marketing/cta-block/CtaBlock.tsx +80 -0
- package/src/components/blocks/marketing/cta-block/index.ts +1 -0
- package/src/components/blocks/marketing/feature-grid/FeatureGrid.tsx +47 -0
- package/src/components/blocks/marketing/feature-grid/index.ts +1 -0
- package/src/components/blocks/marketing/hero-section/HeroSection.tsx +89 -0
- package/src/components/blocks/marketing/hero-section/index.ts +1 -0
- package/src/components/blocks/marketing/index.ts +4 -0
- package/src/components/blocks/marketing/testimonial-card/TestimonialCard.tsx +71 -0
- package/src/components/blocks/marketing/testimonial-card/index.ts +1 -0
- package/src/components/blocks/media/call-controls/CallControlButton.tsx +52 -0
- package/src/components/blocks/media/call-controls/CallControls.tsx +10 -0
- package/src/components/blocks/media/call-controls/index.ts +2 -0
- package/src/components/blocks/media/index.ts +2 -0
- package/src/components/blocks/media/media-player/MediaPlayer.tsx +20 -0
- package/src/components/blocks/media/media-player/MediaPlayerControls.tsx +16 -0
- package/src/components/blocks/media/media-player/MediaPlayerScrubber.tsx +29 -0
- package/src/components/blocks/media/media-player/MediaPlayerVideo.tsx +25 -0
- package/src/components/blocks/media/media-player/index.ts +4 -0
- package/src/components/blocks/navigation/app-sidebar/AppSidebar.tsx +32 -0
- package/src/components/blocks/navigation/app-sidebar/NavItem.tsx +43 -0
- package/src/components/blocks/navigation/app-sidebar/index.ts +2 -0
- package/src/components/blocks/navigation/context-switcher/ContextSwitcher.tsx +77 -0
- package/src/components/blocks/navigation/context-switcher/index.ts +1 -0
- package/src/components/blocks/navigation/index.ts +3 -0
- package/src/components/blocks/navigation/top-navbar/TopNavbar.tsx +47 -0
- package/src/components/blocks/navigation/top-navbar/index.ts +1 -0
- package/src/components/blocks/onboarding/index.ts +2 -0
- package/src/components/blocks/onboarding/onboarding-welcome/OnboardingWelcome.tsx +74 -0
- package/src/components/blocks/onboarding/onboarding-welcome/index.ts +1 -0
- package/src/components/blocks/onboarding/step-wizard/StepWizard.tsx +72 -0
- package/src/components/blocks/onboarding/step-wizard/index.ts +1 -0
- package/src/components/blocks/profiles/connection-list/ConnectionItem.tsx +35 -0
- package/src/components/blocks/profiles/connection-list/ConnectionList.tsx +16 -0
- package/src/components/blocks/profiles/connection-list/index.ts +2 -0
- package/src/components/blocks/profiles/index.ts +3 -0
- package/src/components/blocks/profiles/profile-header/ProfileHeader.tsx +88 -0
- package/src/components/blocks/profiles/profile-header/index.ts +1 -0
- package/src/components/blocks/profiles/profile-stats/ProfileStats.tsx +20 -0
- package/src/components/blocks/profiles/profile-stats/index.ts +1 -0
- package/src/components/blocks/scheduling/booking-slot/BookingSlot.tsx +44 -0
- package/src/components/blocks/scheduling/booking-slot/index.ts +1 -0
- package/src/components/blocks/scheduling/event-card/EventCard.tsx +85 -0
- package/src/components/blocks/scheduling/event-card/index.ts +1 -0
- package/src/components/blocks/scheduling/index.ts +3 -0
- package/src/components/blocks/scheduling/timeline-row/TimelineRow.tsx +82 -0
- package/src/components/blocks/scheduling/timeline-row/index.ts +1 -0
- package/src/components/blocks/settings/billing-usage/BillingUsage.tsx +49 -0
- package/src/components/blocks/settings/billing-usage/index.ts +1 -0
- package/src/components/blocks/settings/index.ts +3 -0
- package/src/components/blocks/settings/integration-card/IntegrationCard.tsx +62 -0
- package/src/components/blocks/settings/integration-card/index.ts +1 -0
- package/src/components/blocks/settings/settings-section/SettingsSection.tsx +23 -0
- package/src/components/blocks/settings/settings-section/index.ts +1 -0
- package/src/components/ui/accordion.tsx +56 -0
- package/src/components/ui/alert-dialog.tsx +141 -0
- package/src/components/ui/alert.tsx +59 -0
- package/src/components/ui/aspect-ratio.tsx +5 -0
- package/src/components/ui/avatar.tsx +50 -0
- package/src/components/ui/badge.tsx +36 -0
- package/src/components/ui/breadcrumb.tsx +115 -0
- package/src/components/ui/button.tsx +56 -0
- package/src/components/ui/calendar.tsx +211 -0
- package/src/components/ui/card.tsx +79 -0
- package/src/components/ui/carousel.tsx +262 -0
- package/src/components/ui/chart.tsx +367 -0
- package/src/components/ui/checkbox.tsx +28 -0
- package/src/components/ui/collapsible.tsx +11 -0
- package/src/components/ui/command.tsx +153 -0
- package/src/components/ui/component-placeholder.tsx +38 -0
- package/src/components/ui/context-menu.tsx +198 -0
- package/src/components/ui/dialog.tsx +122 -0
- package/src/components/ui/drawer.tsx +116 -0
- package/src/components/ui/dropdown-menu.tsx +200 -0
- package/src/components/ui/form.tsx +176 -0
- package/src/components/ui/hover-card.tsx +27 -0
- package/src/components/ui/input-otp.tsx +69 -0
- package/src/components/ui/input.tsx +22 -0
- package/src/components/ui/label.tsx +24 -0
- package/src/components/ui/menubar.tsx +256 -0
- package/src/components/ui/navigation-menu.tsx +128 -0
- package/src/components/ui/pagination.tsx +117 -0
- package/src/components/ui/popover.tsx +29 -0
- package/src/components/ui/progress.tsx +28 -0
- package/src/components/ui/radio-group.tsx +42 -0
- package/src/components/ui/resizable.tsx +45 -0
- package/src/components/ui/scroll-area.tsx +46 -0
- package/src/components/ui/select.tsx +160 -0
- package/src/components/ui/separator.tsx +29 -0
- package/src/components/ui/sheet.tsx +140 -0
- package/src/components/ui/skeleton.tsx +15 -0
- package/src/components/ui/slider.tsx +26 -0
- package/src/components/ui/sonner.tsx +45 -0
- package/src/components/ui/switch.tsx +27 -0
- package/src/components/ui/table.tsx +117 -0
- package/src/components/ui/tabs.tsx +53 -0
- package/src/components/ui/textarea.tsx +22 -0
- package/src/components/ui/toast.tsx +127 -0
- package/src/components/ui/toaster.tsx +33 -0
- package/src/components/ui/toggle-group.tsx +61 -0
- package/src/components/ui/toggle.tsx +45 -0
- package/src/components/ui/tooltip.tsx +28 -0
- package/src/hooks/use-toast.ts +194 -0
- package/src/index.ts +53 -56
- package/src/styles/index.css +6 -0
- package/src/styles/tokens.css +30 -18
- package/tailwind.config.js +120 -98
- package/tsconfig.json +2 -2
- package/src/components/ui/accordion/Accordion.vue +0 -13
- package/src/components/ui/accordion/AccordionContent.vue +0 -20
- package/src/components/ui/accordion/AccordionItem.vue +0 -15
- package/src/components/ui/accordion/AccordionTrigger.vue +0 -25
- package/src/components/ui/accordion/index.ts +0 -4
- package/src/components/ui/alert/Alert.vue +0 -38
- package/src/components/ui/alert/AlertDescription.vue +0 -12
- package/src/components/ui/alert/AlertTitle.vue +0 -12
- package/src/components/ui/alert/index.ts +0 -3
- package/src/components/ui/alert-dialog/AlertDialog.vue +0 -13
- package/src/components/ui/alert-dialog/AlertDialogAction.vue +0 -21
- package/src/components/ui/alert-dialog/AlertDialogCancel.vue +0 -21
- package/src/components/ui/alert-dialog/AlertDialogContent.vue +0 -39
- package/src/components/ui/alert-dialog/AlertDialogDescription.vue +0 -15
- package/src/components/ui/alert-dialog/AlertDialogFooter.vue +0 -12
- package/src/components/ui/alert-dialog/AlertDialogHeader.vue +0 -12
- package/src/components/ui/alert-dialog/AlertDialogTitle.vue +0 -15
- package/src/components/ui/alert-dialog/AlertDialogTrigger.vue +0 -11
- package/src/components/ui/alert-dialog/index.ts +0 -9
- package/src/components/ui/avatar/Avatar.vue +0 -14
- package/src/components/ui/avatar/index.ts +0 -1
- package/src/components/ui/badge/Badge.vue +0 -27
- package/src/components/ui/badge/index.ts +0 -1
- package/src/components/ui/breadcrumb/Breadcrumb.vue +0 -6
- package/src/components/ui/breadcrumb/BreadcrumbEllipsis.vue +0 -12
- package/src/components/ui/breadcrumb/BreadcrumbItem.vue +0 -6
- package/src/components/ui/breadcrumb/BreadcrumbLink.vue +0 -20
- package/src/components/ui/breadcrumb/BreadcrumbList.vue +0 -6
- package/src/components/ui/breadcrumb/BreadcrumbPage.vue +0 -6
- package/src/components/ui/breadcrumb/BreadcrumbSeparator.vue +0 -11
- package/src/components/ui/breadcrumb/index.ts +0 -7
- package/src/components/ui/button/Button.vue +0 -65
- package/src/components/ui/button/index.ts +0 -1
- package/src/components/ui/card/Card.vue +0 -13
- package/src/components/ui/card/CardContent.vue +0 -7
- package/src/components/ui/card/CardDescription.vue +0 -7
- package/src/components/ui/card/CardFooter.vue +0 -7
- package/src/components/ui/card/CardHeader.vue +0 -9
- package/src/components/ui/card/CardTitle.vue +0 -7
- package/src/components/ui/card/index.ts +0 -6
- package/src/components/ui/checkbox/Checkbox.vue +0 -25
- package/src/components/ui/checkbox/index.ts +0 -1
- package/src/components/ui/collapsible/Collapsible.vue +0 -13
- package/src/components/ui/collapsible/index.ts +0 -2
- package/src/components/ui/command/Command.vue +0 -16
- package/src/components/ui/command/CommandEmpty.vue +0 -5
- package/src/components/ui/command/CommandGroup.vue +0 -22
- package/src/components/ui/command/CommandInput.vue +0 -21
- package/src/components/ui/command/CommandItem.vue +0 -22
- package/src/components/ui/command/CommandList.vue +0 -17
- package/src/components/ui/command/CommandSeparator.vue +0 -5
- package/src/components/ui/command/index.ts +0 -7
- package/src/components/ui/context-menu/ContextMenuContent.vue +0 -24
- package/src/components/ui/context-menu/ContextMenuItem.vue +0 -16
- package/src/components/ui/context-menu/ContextMenuLabel.vue +0 -9
- package/src/components/ui/context-menu/ContextMenuSeparator.vue +0 -9
- package/src/components/ui/context-menu/ContextMenuSubContent.vue +0 -14
- package/src/components/ui/context-menu/index.ts +0 -9
- package/src/components/ui/dialog/Dialog.vue +0 -14
- package/src/components/ui/dialog/DialogClose.vue +0 -12
- package/src/components/ui/dialog/DialogContent.vue +0 -48
- package/src/components/ui/dialog/DialogDescription.vue +0 -23
- package/src/components/ui/dialog/DialogFooter.vue +0 -12
- package/src/components/ui/dialog/DialogHeader.vue +0 -12
- package/src/components/ui/dialog/DialogScrollContent.vue +0 -47
- package/src/components/ui/dialog/DialogTitle.vue +0 -23
- package/src/components/ui/dialog/DialogTrigger.vue +0 -12
- package/src/components/ui/dialog/index.ts +0 -9
- package/src/components/ui/dropdown-menu/DropdownMenu.vue +0 -13
- package/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +0 -28
- package/src/components/ui/dropdown-menu/DropdownMenuContent.vue +0 -33
- package/src/components/ui/dropdown-menu/DropdownMenuGroup.vue +0 -11
- package/src/components/ui/dropdown-menu/DropdownMenuItem.vue +0 -27
- package/src/components/ui/dropdown-menu/DropdownMenuLabel.vue +0 -23
- package/src/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +0 -13
- package/src/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +0 -27
- package/src/components/ui/dropdown-menu/DropdownMenuSeparator.vue +0 -13
- package/src/components/ui/dropdown-menu/DropdownMenuShortcut.vue +0 -12
- package/src/components/ui/dropdown-menu/DropdownMenuSub.vue +0 -13
- package/src/components/ui/dropdown-menu/DropdownMenuSubContent.vue +0 -27
- package/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +0 -23
- package/src/components/ui/dropdown-menu/DropdownMenuTrigger.vue +0 -11
- package/src/components/ui/dropdown-menu/index.ts +0 -14
- package/src/components/ui/form/FormControl.vue +0 -3
- package/src/components/ui/form/FormDescription.vue +0 -6
- package/src/components/ui/form/FormItem.vue +0 -6
- package/src/components/ui/form/FormLabel.vue +0 -10
- package/src/components/ui/form/FormMessage.vue +0 -10
- package/src/components/ui/form/index.ts +0 -9
- package/src/components/ui/hover-card/HoverCard.vue +0 -13
- package/src/components/ui/hover-card/HoverCardContent.vue +0 -26
- package/src/components/ui/hover-card/HoverCardTrigger.vue +0 -11
- package/src/components/ui/hover-card/index.ts +0 -3
- package/src/components/ui/input/Input.vue +0 -23
- package/src/components/ui/input/index.ts +0 -1
- package/src/components/ui/label/Label.vue +0 -18
- package/src/components/ui/label/index.ts +0 -1
- package/src/components/ui/lib/utils.ts +0 -2
- package/src/components/ui/menubar/MenubarContent.vue +0 -15
- package/src/components/ui/menubar/MenubarItem.vue +0 -13
- package/src/components/ui/menubar/MenubarTrigger.vue +0 -13
- package/src/components/ui/menubar/index.ts +0 -5
- package/src/components/ui/navigation-menu/NavigationMenuContent.vue +0 -14
- package/src/components/ui/navigation-menu/NavigationMenuTrigger.vue +0 -15
- package/src/components/ui/navigation-menu/index.ts +0 -4
- package/src/components/ui/pagination/PaginationContent.vue +0 -13
- package/src/components/ui/pagination/PaginationEllipsis.vue +0 -12
- package/src/components/ui/pagination/PaginationNext.vue +0 -14
- package/src/components/ui/pagination/PaginationPrev.vue +0 -14
- package/src/components/ui/pagination/index.ts +0 -6
- package/src/components/ui/popover/Popover.vue +0 -13
- package/src/components/ui/popover/PopoverContent.vue +0 -27
- package/src/components/ui/popover/PopoverTrigger.vue +0 -11
- package/src/components/ui/popover/index.ts +0 -3
- package/src/components/ui/progress/Progress.vue +0 -21
- package/src/components/ui/progress/index.ts +0 -1
- package/src/components/ui/radio-group/RadioGroup.vue +0 -16
- package/src/components/ui/radio-group/RadioGroupItem.vue +0 -24
- package/src/components/ui/radio-group/index.ts +0 -2
- package/src/components/ui/scroll-area/ScrollArea.vue +0 -13
- package/src/components/ui/scroll-area/index.ts +0 -1
- package/src/components/ui/select/Select.vue +0 -13
- package/src/components/ui/select/SelectContent.vue +0 -40
- package/src/components/ui/select/SelectGroup.vue +0 -15
- package/src/components/ui/select/SelectItem.vue +0 -30
- package/src/components/ui/select/SelectLabel.vue +0 -15
- package/src/components/ui/select/SelectSeparator.vue +0 -13
- package/src/components/ui/select/SelectTrigger.vue +0 -23
- package/src/components/ui/select/SelectValue.vue +0 -11
- package/src/components/ui/select/index.ts +0 -8
- package/src/components/ui/separator/Separator.vue +0 -16
- package/src/components/ui/separator/index.ts +0 -1
- package/src/components/ui/sheet/Sheet.vue +0 -13
- package/src/components/ui/sheet/SheetClose.vue +0 -11
- package/src/components/ui/sheet/SheetContent.vue +0 -65
- package/src/components/ui/sheet/SheetDescription.vue +0 -15
- package/src/components/ui/sheet/SheetFooter.vue +0 -12
- package/src/components/ui/sheet/SheetHeader.vue +0 -12
- package/src/components/ui/sheet/SheetTitle.vue +0 -15
- package/src/components/ui/sheet/SheetTrigger.vue +0 -11
- package/src/components/ui/sheet/index.ts +0 -8
- package/src/components/ui/skeleton/Skeleton.vue +0 -9
- package/src/components/ui/skeleton/index.ts +0 -1
- package/src/components/ui/slider/Slider.vue +0 -26
- package/src/components/ui/slider/index.ts +0 -1
- package/src/components/ui/switch/Switch.vue +0 -24
- package/src/components/ui/switch/index.ts +0 -1
- package/src/components/ui/table/Table.vue +0 -13
- package/src/components/ui/table/TableBody.vue +0 -6
- package/src/components/ui/table/TableCaption.vue +0 -6
- package/src/components/ui/table/TableCell.vue +0 -6
- package/src/components/ui/table/TableFooter.vue +0 -6
- package/src/components/ui/table/TableHead.vue +0 -6
- package/src/components/ui/table/TableHeader.vue +0 -6
- package/src/components/ui/table/TableRow.vue +0 -6
- package/src/components/ui/table/index.ts +0 -8
- package/src/components/ui/tabs/Tabs.vue +0 -13
- package/src/components/ui/tabs/TabsContent.vue +0 -21
- package/src/components/ui/tabs/TabsList.vue +0 -21
- package/src/components/ui/tabs/TabsTrigger.vue +0 -21
- package/src/components/ui/tabs/index.ts +0 -4
- package/src/components/ui/textarea/Textarea.vue +0 -29
- package/src/components/ui/textarea/index.ts +0 -1
- package/src/components/ui/toggle/Toggle.vue +0 -40
- package/src/components/ui/toggle/index.ts +0 -1
- package/src/components/ui/toggle-group/ToggleGroup.vue +0 -16
- package/src/components/ui/toggle-group/ToggleGroupItem.vue +0 -21
- package/src/components/ui/toggle-group/index.ts +0 -2
- package/src/components/ui/tooltip/Tooltip.vue +0 -13
- package/src/components/ui/tooltip/TooltipContent.vue +0 -27
- package/src/components/ui/tooltip/TooltipProvider.vue +0 -12
- package/src/components/ui/tooltip/TooltipTrigger.vue +0 -11
- package/src/components/ui/tooltip/index.ts +0 -4
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { cn } from '@/lib/utils'
|
|
3
|
+
import { Card, CardContent } from '@/components/ui/card'
|
|
4
|
+
import { Badge } from '@/components/ui/badge'
|
|
5
|
+
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
|
|
6
|
+
import { MessageSquare, Paperclip } from 'lucide-react'
|
|
7
|
+
|
|
8
|
+
interface KanbanCardProps {
|
|
9
|
+
title: string
|
|
10
|
+
id?: string
|
|
11
|
+
priority?: 'low' | 'medium' | 'high'
|
|
12
|
+
labels?: string[]
|
|
13
|
+
assignee?: { name: string; avatar?: string }
|
|
14
|
+
comments?: number
|
|
15
|
+
attachments?: number
|
|
16
|
+
className?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function KanbanCard({
|
|
20
|
+
title,
|
|
21
|
+
id,
|
|
22
|
+
priority,
|
|
23
|
+
labels,
|
|
24
|
+
assignee,
|
|
25
|
+
comments,
|
|
26
|
+
attachments,
|
|
27
|
+
className,
|
|
28
|
+
}: KanbanCardProps) {
|
|
29
|
+
return (
|
|
30
|
+
<Card
|
|
31
|
+
className={cn(
|
|
32
|
+
'cursor-grab active:cursor-grabbing hover:ring-1 hover:ring-primary/50 transition-all shadow-sm',
|
|
33
|
+
className,
|
|
34
|
+
)}
|
|
35
|
+
>
|
|
36
|
+
<CardContent className="p-3">
|
|
37
|
+
{(labels?.length || priority) && (
|
|
38
|
+
<div className="flex flex-wrap gap-1.5 mb-2">
|
|
39
|
+
{priority && (
|
|
40
|
+
<Badge
|
|
41
|
+
variant={
|
|
42
|
+
priority === 'high'
|
|
43
|
+
? 'destructive'
|
|
44
|
+
: priority === 'medium'
|
|
45
|
+
? 'default'
|
|
46
|
+
: 'secondary'
|
|
47
|
+
}
|
|
48
|
+
className="text-[10px] h-4 px-1 leading-none uppercase"
|
|
49
|
+
>
|
|
50
|
+
{priority}
|
|
51
|
+
</Badge>
|
|
52
|
+
)}
|
|
53
|
+
{labels?.map((label) => (
|
|
54
|
+
<Badge
|
|
55
|
+
key={label}
|
|
56
|
+
variant="outline"
|
|
57
|
+
className="text-[10px] h-4 px-1 leading-none bg-background"
|
|
58
|
+
>
|
|
59
|
+
{label}
|
|
60
|
+
</Badge>
|
|
61
|
+
))}
|
|
62
|
+
</div>
|
|
63
|
+
)}
|
|
64
|
+
|
|
65
|
+
<p className="text-sm font-medium leading-tight mb-3">
|
|
66
|
+
{id && (
|
|
67
|
+
<span className="text-xs text-muted-foreground font-mono mr-1.5">
|
|
68
|
+
{id}
|
|
69
|
+
</span>
|
|
70
|
+
)}
|
|
71
|
+
{title}
|
|
72
|
+
</p>
|
|
73
|
+
|
|
74
|
+
<div className="flex items-center justify-between mt-auto pt-2 border-t text-muted-foreground text-xs">
|
|
75
|
+
<div className="flex items-center gap-3">
|
|
76
|
+
{!!comments && (
|
|
77
|
+
<div className="flex items-center gap-1">
|
|
78
|
+
<MessageSquare className="h-3.5 w-3.5" />
|
|
79
|
+
<span className="tabular-nums font-medium">{comments}</span>
|
|
80
|
+
</div>
|
|
81
|
+
)}
|
|
82
|
+
{!!attachments && (
|
|
83
|
+
<div className="flex items-center gap-1">
|
|
84
|
+
<Paperclip className="h-3.5 w-3.5" />
|
|
85
|
+
<span className="tabular-nums font-medium">{attachments}</span>
|
|
86
|
+
</div>
|
|
87
|
+
)}
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
{assignee && (
|
|
91
|
+
<Avatar className="h-6 w-6">
|
|
92
|
+
{assignee.avatar && <AvatarImage src={assignee.avatar} />}
|
|
93
|
+
<AvatarFallback className="text-[10px]">
|
|
94
|
+
{assignee.name.substring(0, 2).toUpperCase()}
|
|
95
|
+
</AvatarFallback>
|
|
96
|
+
</Avatar>
|
|
97
|
+
)}
|
|
98
|
+
</div>
|
|
99
|
+
</CardContent>
|
|
100
|
+
</Card>
|
|
101
|
+
)
|
|
102
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { cn } from '@/lib/utils'
|
|
3
|
+
import { Badge } from '@/components/ui/badge'
|
|
4
|
+
import { MoreHorizontal, Plus } from 'lucide-react'
|
|
5
|
+
import { Button } from '@/components/ui/button'
|
|
6
|
+
|
|
7
|
+
interface KanbanColumnProps {
|
|
8
|
+
title: string
|
|
9
|
+
count?: number
|
|
10
|
+
className?: string
|
|
11
|
+
children?: React.ReactNode
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function KanbanColumn({ title, count, className, children }: KanbanColumnProps) {
|
|
15
|
+
return (
|
|
16
|
+
<div className={cn('flex flex-col gap-3 min-w-[300px] w-[300px] max-w-[300px] shrink-0', className)}>
|
|
17
|
+
<div className="flex items-center justify-between mb-2 px-1">
|
|
18
|
+
<div className="flex items-center gap-2">
|
|
19
|
+
<h3 className="font-semibold text-sm">{title}</h3>
|
|
20
|
+
{count !== undefined && (
|
|
21
|
+
<Badge variant="secondary" className="h-5 px-1.5 text-xs font-mono tabular-nums">
|
|
22
|
+
{count}
|
|
23
|
+
</Badge>
|
|
24
|
+
)}
|
|
25
|
+
</div>
|
|
26
|
+
<div className="flex items-center gap-1 text-muted-foreground">
|
|
27
|
+
<Button variant="ghost" size="icon" className="h-6 w-6 rounded-md">
|
|
28
|
+
<Plus className="h-4 w-4" />
|
|
29
|
+
</Button>
|
|
30
|
+
<Button variant="ghost" size="icon" className="h-6 w-6 rounded-md">
|
|
31
|
+
<MoreHorizontal className="h-4 w-4" />
|
|
32
|
+
</Button>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<div className="flex flex-col gap-3 flex-1 bg-muted/50 p-2 rounded-lg border border-transparent hover:border-border transition-colors">
|
|
37
|
+
{children}
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
)
|
|
41
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
import { ChevronRight, Folder, FolderOpen, File } from 'lucide-react'
|
|
3
|
+
import { cn } from '@/lib/utils'
|
|
4
|
+
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'
|
|
5
|
+
|
|
6
|
+
interface TreeItemProps {
|
|
7
|
+
title: string
|
|
8
|
+
isFolder?: boolean
|
|
9
|
+
defaultExpanded?: boolean
|
|
10
|
+
level?: number
|
|
11
|
+
className?: string
|
|
12
|
+
children?: React.ReactNode
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function TreeItem({
|
|
16
|
+
title,
|
|
17
|
+
isFolder,
|
|
18
|
+
defaultExpanded,
|
|
19
|
+
level,
|
|
20
|
+
className,
|
|
21
|
+
children,
|
|
22
|
+
}: TreeItemProps) {
|
|
23
|
+
const [isOpen, setIsOpen] = useState(defaultExpanded ?? false)
|
|
24
|
+
const nestingLevel = level || 0
|
|
25
|
+
|
|
26
|
+
if (isFolder) {
|
|
27
|
+
return (
|
|
28
|
+
<Collapsible open={isOpen} onOpenChange={setIsOpen} className="w-full">
|
|
29
|
+
<CollapsibleTrigger asChild>
|
|
30
|
+
<div
|
|
31
|
+
className={cn(
|
|
32
|
+
'flex items-center gap-2 px-3 py-1.5 hover:bg-muted/50 cursor-pointer select-none text-sm transition-colors',
|
|
33
|
+
className
|
|
34
|
+
)}
|
|
35
|
+
style={{ paddingLeft: `${nestingLevel * 16 + 12}px` }}
|
|
36
|
+
>
|
|
37
|
+
<ChevronRight
|
|
38
|
+
className={cn(
|
|
39
|
+
'h-4 w-4 shrink-0 transition-transform duration-200 text-muted-foreground',
|
|
40
|
+
isOpen && 'rotate-90'
|
|
41
|
+
)}
|
|
42
|
+
/>
|
|
43
|
+
{isOpen ? (
|
|
44
|
+
<FolderOpen className="h-4 w-4 shrink-0 text-blue-500" />
|
|
45
|
+
) : (
|
|
46
|
+
<Folder className="h-4 w-4 shrink-0 text-blue-400" />
|
|
47
|
+
)}
|
|
48
|
+
<span className="truncate">{title}</span>
|
|
49
|
+
</div>
|
|
50
|
+
</CollapsibleTrigger>
|
|
51
|
+
<CollapsibleContent>{children}</CollapsibleContent>
|
|
52
|
+
</Collapsible>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<div
|
|
58
|
+
className={cn(
|
|
59
|
+
'flex items-center gap-2 px-3 py-1.5 hover:bg-muted/50 cursor-pointer select-none text-sm group transition-colors',
|
|
60
|
+
className
|
|
61
|
+
)}
|
|
62
|
+
style={{ paddingLeft: `${nestingLevel * 16 + 36}px` }}
|
|
63
|
+
>
|
|
64
|
+
<File className="h-4 w-4 shrink-0 text-muted-foreground group-hover:text-foreground transition-colors" />
|
|
65
|
+
<span className="truncate">{title}</span>
|
|
66
|
+
</div>
|
|
67
|
+
)
|
|
68
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { cn } from '@/lib/utils'
|
|
3
|
+
|
|
4
|
+
export function TreeView({ class: className, children }: { class?: string; children?: React.ReactNode }) {
|
|
5
|
+
return (
|
|
6
|
+
<div className={cn('flex flex-col border rounded-lg py-2 bg-background', className)}>
|
|
7
|
+
{children}
|
|
8
|
+
</div>
|
|
9
|
+
)
|
|
10
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { cn } from '@/lib/utils'
|
|
3
|
+
|
|
4
|
+
interface FinancialTickerProps {
|
|
5
|
+
symbol: string
|
|
6
|
+
price: string
|
|
7
|
+
change: number
|
|
8
|
+
changePercent: number
|
|
9
|
+
className?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function FinancialTicker({ symbol, price, change, changePercent, className }: FinancialTickerProps) {
|
|
13
|
+
return (
|
|
14
|
+
<div className={cn('flex items-center gap-2 text-sm font-medium tabular-nums', className)}>
|
|
15
|
+
<span className="font-bold text-foreground">{symbol}</span>
|
|
16
|
+
<span className="text-foreground">{price}</span>
|
|
17
|
+
<span
|
|
18
|
+
className={cn(
|
|
19
|
+
'flex gap-1.5 items-center ml-1',
|
|
20
|
+
change >= 0 ? 'text-emerald-500' : 'text-rose-500'
|
|
21
|
+
)}
|
|
22
|
+
>
|
|
23
|
+
<span>{change > 0 ? '+' : ''}{change}</span>
|
|
24
|
+
<span
|
|
25
|
+
className={cn(
|
|
26
|
+
'text-xs px-1.5 py-0.5 rounded-sm',
|
|
27
|
+
change >= 0 ? 'bg-emerald-500/10' : 'bg-rose-500/10'
|
|
28
|
+
)}
|
|
29
|
+
>
|
|
30
|
+
{changePercent > 0 ? '+' : ''}{changePercent}%
|
|
31
|
+
</span>
|
|
32
|
+
</span>
|
|
33
|
+
</div>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './FinancialTicker'
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
|
3
|
+
import { TrendingUp, TrendingDown, Minus } from 'lucide-react'
|
|
4
|
+
import { cn } from '@/lib/utils'
|
|
5
|
+
|
|
6
|
+
interface KpiCardProps {
|
|
7
|
+
title: string
|
|
8
|
+
metric: string
|
|
9
|
+
trend?: number
|
|
10
|
+
trendLabel?: string
|
|
11
|
+
icon?: React.ComponentType<React.SVGProps<SVGSVGElement>>
|
|
12
|
+
className?: string
|
|
13
|
+
children?: React.ReactNode
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function KpiCard({
|
|
17
|
+
title,
|
|
18
|
+
metric,
|
|
19
|
+
trend,
|
|
20
|
+
trendLabel,
|
|
21
|
+
icon: Icon,
|
|
22
|
+
className,
|
|
23
|
+
children,
|
|
24
|
+
}: KpiCardProps) {
|
|
25
|
+
return (
|
|
26
|
+
<Card className={cn('overflow-hidden', className)}>
|
|
27
|
+
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
28
|
+
<CardTitle className="text-sm font-medium">
|
|
29
|
+
{title}
|
|
30
|
+
</CardTitle>
|
|
31
|
+
{Icon && <Icon className="h-4 w-4 text-muted-foreground" />}
|
|
32
|
+
</CardHeader>
|
|
33
|
+
<CardContent>
|
|
34
|
+
<div className="text-2xl font-bold">{metric}</div>
|
|
35
|
+
{trend !== undefined && (
|
|
36
|
+
<p className="text-xs mt-1 flex items-center gap-1">
|
|
37
|
+
<span
|
|
38
|
+
className={cn(
|
|
39
|
+
'flex items-center font-medium',
|
|
40
|
+
trend > 0 && 'text-emerald-500',
|
|
41
|
+
trend < 0 && 'text-rose-500',
|
|
42
|
+
trend === 0 && 'text-muted-foreground',
|
|
43
|
+
)}
|
|
44
|
+
>
|
|
45
|
+
{trend > 0 ? (
|
|
46
|
+
<TrendingUp className="mr-1 h-3 w-3" />
|
|
47
|
+
) : trend < 0 ? (
|
|
48
|
+
<TrendingDown className="mr-1 h-3 w-3" />
|
|
49
|
+
) : (
|
|
50
|
+
<Minus className="mr-1 h-3 w-3" />
|
|
51
|
+
)}
|
|
52
|
+
{Math.abs(trend)}%
|
|
53
|
+
</span>
|
|
54
|
+
<span className="text-muted-foreground ml-1">{trendLabel}</span>
|
|
55
|
+
</p>
|
|
56
|
+
)}
|
|
57
|
+
</CardContent>
|
|
58
|
+
{children}
|
|
59
|
+
</Card>
|
|
60
|
+
)
|
|
61
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './KpiCard'
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { cn } from '@/lib/utils'
|
|
3
|
+
|
|
4
|
+
interface StatGridProps {
|
|
5
|
+
columns?: 2 | 3 | 4
|
|
6
|
+
className?: string
|
|
7
|
+
children?: React.ReactNode
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function StatGrid({ columns, className, children }: StatGridProps) {
|
|
11
|
+
return (
|
|
12
|
+
<div
|
|
13
|
+
className={cn(
|
|
14
|
+
'grid gap-4 md:gap-8',
|
|
15
|
+
columns === 2
|
|
16
|
+
? 'md:grid-cols-2'
|
|
17
|
+
: columns === 3
|
|
18
|
+
? 'md:grid-cols-3'
|
|
19
|
+
: columns === 4
|
|
20
|
+
? 'md:grid-cols-2 lg:grid-cols-4'
|
|
21
|
+
: 'md:grid-cols-3', // Default 3
|
|
22
|
+
className,
|
|
23
|
+
)}
|
|
24
|
+
>
|
|
25
|
+
{children}
|
|
26
|
+
</div>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './StatGrid'
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import type { HTMLAttributes } from 'react'
|
|
3
|
+
import { CardContent } from '@/components/ui/card'
|
|
4
|
+
import { cn } from '@/lib/utils'
|
|
5
|
+
import { Check } from 'lucide-react'
|
|
6
|
+
|
|
7
|
+
interface PricingFeatureListProps {
|
|
8
|
+
features: string[]
|
|
9
|
+
className?: HTMLAttributes<HTMLDivElement>['className']
|
|
10
|
+
children?: React.ReactNode
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function PricingFeatureList({ features, className, children }: PricingFeatureListProps) {
|
|
14
|
+
return (
|
|
15
|
+
<CardContent className={cn('flex-1', className)}>
|
|
16
|
+
<ul className="space-y-3">
|
|
17
|
+
{features.map((feature) => (
|
|
18
|
+
<li key={feature} className="flex items-center gap-3">
|
|
19
|
+
<Check className="h-4 w-4 text-primary shrink-0" />
|
|
20
|
+
<span className="text-sm">{feature}</span>
|
|
21
|
+
</li>
|
|
22
|
+
))}
|
|
23
|
+
</ul>
|
|
24
|
+
{children}
|
|
25
|
+
</CardContent>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import type { HTMLAttributes } from 'react'
|
|
3
|
+
import { CardHeader, CardTitle, CardDescription } from '@/components/ui/card'
|
|
4
|
+
import { cn } from '@/lib/utils'
|
|
5
|
+
|
|
6
|
+
interface PricingHeaderProps {
|
|
7
|
+
title: string
|
|
8
|
+
description?: string
|
|
9
|
+
price: string
|
|
10
|
+
period?: string
|
|
11
|
+
className?: HTMLAttributes<HTMLDivElement>['className']
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function PricingHeader({ title, description, price, period, className }: PricingHeaderProps) {
|
|
15
|
+
return (
|
|
16
|
+
<CardHeader className={cn('text-center pb-6', className)}>
|
|
17
|
+
<CardTitle className="text-2xl">{title}</CardTitle>
|
|
18
|
+
{description && <CardDescription className="mt-2">{description}</CardDescription>}
|
|
19
|
+
<div className="mt-4 flex items-baseline justify-center gap-1">
|
|
20
|
+
<span className="text-4xl font-bold tracking-tight">{price}</span>
|
|
21
|
+
{period && <span className="text-muted-foreground">{period}</span>}
|
|
22
|
+
</div>
|
|
23
|
+
</CardHeader>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import type { HTMLAttributes } from 'react'
|
|
3
|
+
import { Card } from '@/components/ui/card'
|
|
4
|
+
import { cn } from '@/lib/utils'
|
|
5
|
+
|
|
6
|
+
interface PricingTierProps {
|
|
7
|
+
featured?: boolean
|
|
8
|
+
className?: HTMLAttributes<HTMLElement>['className']
|
|
9
|
+
children?: React.ReactNode
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function PricingTier({ featured, className, children }: PricingTierProps) {
|
|
13
|
+
return (
|
|
14
|
+
<Card
|
|
15
|
+
className={cn(
|
|
16
|
+
'flex flex-col relative',
|
|
17
|
+
featured ? 'border-primary shadow-lg scale-105 z-10' : 'border-border',
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
>
|
|
21
|
+
{featured && (
|
|
22
|
+
<div className="absolute -top-3 left-0 right-0 flex justify-center">
|
|
23
|
+
<span className="bg-primary text-primary-foreground text-xs font-medium px-3 py-1 rounded-full uppercase tracking-wider">
|
|
24
|
+
Most Popular
|
|
25
|
+
</span>
|
|
26
|
+
</div>
|
|
27
|
+
)}
|
|
28
|
+
{children}
|
|
29
|
+
</Card>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import type { HTMLAttributes } from 'react'
|
|
3
|
+
import { Card } from '@/components/ui/card'
|
|
4
|
+
import { cn } from '@/lib/utils'
|
|
5
|
+
|
|
6
|
+
interface ProductCardProps {
|
|
7
|
+
className?: HTMLAttributes<HTMLDivElement>['className']
|
|
8
|
+
children?: React.ReactNode
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function ProductCard({ className, children }: ProductCardProps) {
|
|
12
|
+
return (
|
|
13
|
+
<Card className={cn('overflow-hidden flex flex-col group transition-all hover:shadow-md', className)}>
|
|
14
|
+
{children}
|
|
15
|
+
</Card>
|
|
16
|
+
)
|
|
17
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { CardContent } from '@/components/ui/card'
|
|
3
|
+
import { cn } from '@/lib/utils'
|
|
4
|
+
|
|
5
|
+
export function ProductCardContent({
|
|
6
|
+
className,
|
|
7
|
+
children,
|
|
8
|
+
...props
|
|
9
|
+
}: React.HTMLAttributes<HTMLDivElement>) {
|
|
10
|
+
return (
|
|
11
|
+
<CardContent className={cn('p-4 flex-1 flex flex-col', className)} {...props}>
|
|
12
|
+
{children}
|
|
13
|
+
</CardContent>
|
|
14
|
+
)
|
|
15
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { cn } from '@/lib/utils'
|
|
3
|
+
|
|
4
|
+
interface ProductCardImageProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
src: string
|
|
6
|
+
alt?: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function ProductCardImage({ src, alt, className, children, ...props }: ProductCardImageProps) {
|
|
10
|
+
return (
|
|
11
|
+
<div className={cn('relative aspect-[4/3] overflow-hidden bg-muted', className)} {...props}>
|
|
12
|
+
<img
|
|
13
|
+
src={src}
|
|
14
|
+
alt={alt}
|
|
15
|
+
className="object-cover w-full h-full transition-transform duration-300 group-hover:scale-105"
|
|
16
|
+
/>
|
|
17
|
+
{children}
|
|
18
|
+
</div>
|
|
19
|
+
)
|
|
20
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { cn } from '@/lib/utils'
|
|
3
|
+
|
|
4
|
+
interface ProductCardPriceProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
price: string
|
|
6
|
+
originalPrice?: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function ProductCardPrice({ price, originalPrice, className, ...props }: ProductCardPriceProps) {
|
|
10
|
+
return (
|
|
11
|
+
<div className={cn('mt-auto pt-4 flex items-center gap-2', className)} {...props}>
|
|
12
|
+
<span className="text-lg font-semibold">{price}</span>
|
|
13
|
+
{originalPrice && (
|
|
14
|
+
<span className="text-sm text-muted-foreground line-through">
|
|
15
|
+
{originalPrice}
|
|
16
|
+
</span>
|
|
17
|
+
)}
|
|
18
|
+
</div>
|
|
19
|
+
)
|
|
20
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Star, StarHalf } from 'lucide-react'
|
|
3
|
+
import { cn } from '@/lib/utils'
|
|
4
|
+
|
|
5
|
+
interface RatingStarsProps {
|
|
6
|
+
rating: number
|
|
7
|
+
max?: number
|
|
8
|
+
className?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function RatingStars({ rating, max = 5, className }: RatingStarsProps) {
|
|
12
|
+
return (
|
|
13
|
+
<div className={cn('flex items-center gap-0.5', className)}>
|
|
14
|
+
{Array.from({ length: max }, (_, idx) => {
|
|
15
|
+
const i = idx + 1
|
|
16
|
+
if (i <= rating) {
|
|
17
|
+
return (
|
|
18
|
+
<Star key={i} className="w-4 h-4 fill-primary text-primary" />
|
|
19
|
+
)
|
|
20
|
+
} else if (i - 0.5 <= rating) {
|
|
21
|
+
return (
|
|
22
|
+
<StarHalf key={i} className="w-4 h-4 fill-primary text-primary" />
|
|
23
|
+
)
|
|
24
|
+
} else {
|
|
25
|
+
return (
|
|
26
|
+
<Star key={i} className="w-4 h-4 text-muted-foreground" />
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
})}
|
|
30
|
+
</div>
|
|
31
|
+
)
|
|
32
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './RatingStars'
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { cn } from '@/lib/utils'
|
|
3
|
+
import { Button } from '@/components/ui/button'
|
|
4
|
+
import { Plus } from 'lucide-react'
|
|
5
|
+
|
|
6
|
+
interface EmptyStateProps {
|
|
7
|
+
title: string
|
|
8
|
+
description?: string
|
|
9
|
+
icon?: React.ComponentType<{ className?: string }>
|
|
10
|
+
actionLabel?: string
|
|
11
|
+
className?: string
|
|
12
|
+
onAction?: () => void
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function EmptyState({
|
|
16
|
+
title,
|
|
17
|
+
description,
|
|
18
|
+
icon: Icon,
|
|
19
|
+
actionLabel,
|
|
20
|
+
className,
|
|
21
|
+
onAction,
|
|
22
|
+
}: EmptyStateProps) {
|
|
23
|
+
return (
|
|
24
|
+
<div
|
|
25
|
+
className={cn(
|
|
26
|
+
'flex flex-col items-center justify-center py-12 px-4 sm:px-6 text-center border-2 border-dashed rounded-xl bg-muted/20',
|
|
27
|
+
className,
|
|
28
|
+
)}
|
|
29
|
+
>
|
|
30
|
+
<div className="h-12 w-12 rounded-full bg-muted flex items-center justify-center mb-4">
|
|
31
|
+
{Icon ? (
|
|
32
|
+
<Icon className="h-6 w-6 text-muted-foreground" />
|
|
33
|
+
) : (
|
|
34
|
+
<div className="h-6 w-6 rounded border-2 border-muted-foreground/30" />
|
|
35
|
+
)}
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<h3 className="text-lg font-semibold text-foreground mb-1">{title}</h3>
|
|
39
|
+
{description && (
|
|
40
|
+
<p className="text-sm text-muted-foreground max-w-sm mx-auto mb-6">
|
|
41
|
+
{description}
|
|
42
|
+
</p>
|
|
43
|
+
)}
|
|
44
|
+
|
|
45
|
+
{actionLabel && (
|
|
46
|
+
<Button onClick={onAction}>
|
|
47
|
+
<Plus className="mr-2 h-4 w-4" />
|
|
48
|
+
{actionLabel}
|
|
49
|
+
</Button>
|
|
50
|
+
)}
|
|
51
|
+
</div>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './EmptyState'
|