@otfdashkit/ui 0.1.1 → 0.1.2

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.
Files changed (115) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +28 -41
  3. package/dist/index.js +206 -125
  4. package/dist/index.mjs +194 -124
  5. package/package.json +5 -4
  6. package/src/advanced/BulkActions/BulkActions.tsx +2 -2
  7. package/src/advanced/CommandBar/CommandBar.tsx +2 -2
  8. package/src/advanced/Filters/Filters.tsx +6 -6
  9. package/src/advanced/Kanban/Kanban.tsx +1 -1
  10. package/src/advanced/ToggleButton/ToggleButton.tsx +1 -1
  11. package/src/advanced/bulk-actions.tsx +1 -46
  12. package/src/advanced/command-bar.tsx +1 -115
  13. package/src/advanced/filters.tsx +1 -103
  14. package/src/advanced/kanban.tsx +1 -95
  15. package/src/advanced/toggle-button.tsx +1 -78
  16. package/src/blocks/ChatDetail/ChatDetail.tsx +4 -4
  17. package/src/blocks/FeedbackModal/FeedbackModal.tsx +3 -3
  18. package/src/blocks/FileCards/FileCards.tsx +2 -2
  19. package/src/blocks/FilesList/FilesList.tsx +3 -3
  20. package/src/blocks/IntegrationCard/IntegrationCard.tsx +3 -3
  21. package/src/blocks/InviteModal/InviteModal.tsx +7 -7
  22. package/src/blocks/ManageTagsModal/ManageTagsModal.tsx +4 -4
  23. package/src/blocks/MessagesCard/MessagesCard.tsx +3 -3
  24. package/src/blocks/MetricCard/MetricCard.tsx +3 -3
  25. package/src/blocks/NotificationSettings/NotificationSettings.tsx +4 -4
  26. package/src/blocks/OrganizationMenu/OrganizationMenu.tsx +30 -11
  27. package/src/blocks/RolesMenu/RolesMenu.tsx +4 -4
  28. package/src/blocks/SelectUsersModal/SelectUsersModal.tsx +5 -5
  29. package/src/blocks/SidebarLayoutDashboard/SidebarLayoutDashboard.tsx +5 -5
  30. package/src/blocks/SidebarLayoutGroups/SidebarLayoutGroups.tsx +2 -2
  31. package/src/blocks/SidebarLayoutMinimal/SidebarLayoutMinimal.tsx +3 -3
  32. package/src/blocks/SidebarLayoutSearch/SidebarLayoutSearch.tsx +3 -3
  33. package/src/blocks/SidebarLayoutUser/SidebarLayoutUser.tsx +4 -4
  34. package/src/blocks/SortableTaskList/SortableTaskList.tsx +3 -3
  35. package/src/blocks/StackedLayoutBranded/StackedLayoutBranded.tsx +4 -4
  36. package/src/blocks/StackedLayoutTabs/StackedLayoutTabs.tsx +4 -4
  37. package/src/blocks/TaskCard/TaskCard.tsx +4 -4
  38. package/src/blocks/UserMenu/UserMenu.tsx +3 -3
  39. package/src/blocks/WorkspaceMembers/WorkspaceMembers.tsx +5 -5
  40. package/src/blocks/chat-detail.tsx +1 -73
  41. package/src/blocks/feedback-modal.tsx +1 -68
  42. package/src/blocks/file-cards.tsx +1 -56
  43. package/src/blocks/files-list.tsx +1 -72
  44. package/src/blocks/integration-card.tsx +1 -50
  45. package/src/blocks/invite-modal.tsx +1 -69
  46. package/src/blocks/manage-tags-modal.tsx +1 -63
  47. package/src/blocks/messages-card.tsx +1 -59
  48. package/src/blocks/metric-card.tsx +1 -79
  49. package/src/blocks/notification-settings.tsx +1 -61
  50. package/src/blocks/organization-menu.tsx +1 -85
  51. package/src/blocks/roles-menu.tsx +1 -61
  52. package/src/blocks/select-users-modal.tsx +1 -64
  53. package/src/blocks/sidebar-layout-dashboard.tsx +1 -97
  54. package/src/blocks/sidebar-layout-groups.tsx +1 -79
  55. package/src/blocks/sidebar-layout-minimal.tsx +1 -73
  56. package/src/blocks/sidebar-layout-search.tsx +1 -79
  57. package/src/blocks/sidebar-layout-user.tsx +1 -82
  58. package/src/blocks/sortable-task-list.tsx +1 -70
  59. package/src/blocks/stacked-layout-branded.tsx +1 -64
  60. package/src/blocks/stacked-layout-tabs.tsx +1 -80
  61. package/src/blocks/task-card.tsx +1 -88
  62. package/src/blocks/user-menu.tsx +1 -62
  63. package/src/blocks/workspace-members.tsx +1 -61
  64. package/src/charts/sparkline.tsx +1 -1
  65. package/src/components/DataGrid/DataGrid.tsx +1 -1
  66. package/src/components/DataTable/DataTable.tsx +4 -4
  67. package/src/components/EmptyState/EmptyState.tsx +6 -22
  68. package/src/components/Persona/Persona.tsx +1 -1
  69. package/src/components/Stat/Stat.tsx +1 -1
  70. package/src/components/Stepper/Stepper.tsx +13 -2
  71. package/src/components/banner.tsx +1 -203
  72. package/src/components/breadcrumb.tsx +1 -58
  73. package/src/components/command-item.tsx +1 -36
  74. package/src/components/data-grid.tsx +1 -125
  75. package/src/components/data-table.tsx +1 -139
  76. package/src/components/empty-state.tsx +1 -31
  77. package/src/components/hotkeys.tsx +1 -31
  78. package/src/components/icon-badge.tsx +1 -35
  79. package/src/components/loading-overlay.tsx +1 -38
  80. package/src/components/persona.tsx +1 -38
  81. package/src/components/property-list.tsx +1 -29
  82. package/src/components/stat.tsx +1 -51
  83. package/src/components/stepper.tsx +1 -69
  84. package/src/components/structured-list.tsx +1 -51
  85. package/src/components/timeline.tsx +1 -69
  86. package/src/components/toaster.tsx +1 -42
  87. package/src/data/DataList.tsx +1 -1
  88. package/src/engagement/Tour/Tour.tsx +1 -1
  89. package/src/engagement/beacon.tsx +1 -50
  90. package/src/engagement/tour.tsx +1 -106
  91. package/src/forms/ArrayField/ArrayField.tsx +2 -2
  92. package/src/forms/AutoForm/AutoForm.tsx +9 -8
  93. package/src/forms/DatePicker/DatePicker.tsx +3 -3
  94. package/src/forms/FloatingLabelInput/FloatingLabelInput.tsx +104 -0
  95. package/src/forms/FloatingLabelInput/index.ts +1 -0
  96. package/src/forms/PasswordInput/PasswordInput.tsx +2 -2
  97. package/src/forms/SearchInput/SearchInput.tsx +2 -2
  98. package/src/forms/StepForm/StepForm.tsx +2 -2
  99. package/src/forms/array-field.tsx +1 -61
  100. package/src/forms/auto-form.tsx +1 -133
  101. package/src/forms/date-picker.tsx +1 -109
  102. package/src/forms/field.tsx +1 -62
  103. package/src/forms/file-upload.tsx +1 -101
  104. package/src/forms/floating-label-input.tsx +1 -0
  105. package/src/forms/form.tsx +1 -57
  106. package/src/forms/object-field.tsx +1 -53
  107. package/src/forms/password-input.tsx +1 -54
  108. package/src/forms/search-input.tsx +1 -64
  109. package/src/forms/step-form.tsx +1 -113
  110. package/src/index.ts +6 -3
  111. package/src/primitives/Button/Button.tsx +18 -0
  112. package/src/primitives/code-block.tsx +1 -1
  113. package/src/primitives/comparison.tsx +1 -1
  114. package/src/primitives/qr-code.tsx +5 -1
  115. package/src/themes/base.css +18 -0
@@ -1,6 +1,6 @@
1
1
  import React, { useState } from 'react'
2
2
  import { ChevronDown, Check } from 'lucide-react'
3
- import { cn } from '../utils/cn'
3
+ import { cn } from '../../utils/cn'
4
4
  import {
5
5
  DropdownMenu,
6
6
  DropdownMenuContent,
@@ -9,8 +9,8 @@ import {
9
9
  DropdownMenuLabel,
10
10
  DropdownMenuSeparator,
11
11
  DropdownMenuTrigger,
12
- } from '../primitives/dropdown-menu'
13
- import { Button } from '../primitives/button'
12
+ } from '../../primitives/dropdown-menu'
13
+ import { Button } from '../../primitives/button'
14
14
 
15
15
  const ROLES = [
16
16
  { value: 'owner', label: 'Owner', description: 'Full control over the workspace' },
@@ -27,7 +27,7 @@ export interface RolesMenuProps {
27
27
 
28
28
  export function RolesMenu({ value = 'member', onChange, className }: RolesMenuProps) {
29
29
  const [role, setRole] = useState(value)
30
- const current = ROLES.find(r => r.value === role) ?? ROLES[2]
30
+ const current = ROLES.find(r => r.value === role) ?? ROLES[0]!
31
31
 
32
32
  const handleChange = (v: string) => {
33
33
  setRole(v)
@@ -1,10 +1,10 @@
1
1
  import React, { useState } from 'react'
2
2
  import { Search } from 'lucide-react'
3
- import { cn } from '../utils/cn'
4
- import { Input } from '../primitives/input'
5
- import { Button } from '../primitives/button'
6
- import { Checkbox } from '../primitives/checkbox'
7
- import { Avatar, AvatarFallback } from '../primitives/avatar'
3
+ import { cn } from '../../utils/cn'
4
+ import { Input } from '../../primitives/input'
5
+ import { Button } from '../../primitives/button'
6
+ import { Checkbox } from '../../primitives/checkbox'
7
+ import { Avatar, AvatarFallback } from '../../primitives/avatar'
8
8
 
9
9
  const USERS = [
10
10
  { id: '1', name: 'Jane Doe', email: 'jane@acme.com', initials: 'JD' },
@@ -1,11 +1,11 @@
1
1
  // @ts-nocheck
2
2
  import React from 'react'
3
3
  import { LayoutDashboard, FolderOpen, Users, Settings, Bell, TrendingUp } from 'lucide-react'
4
- import { cn } from '../utils/cn'
5
- import { Avatar, AvatarFallback } from '../primitives/avatar'
6
- import { Badge } from '../primitives/badge'
7
- import { Separator } from '../primitives/separator'
8
- import { Card, CardContent } from '../primitives/card'
4
+ import { cn } from '../../utils/cn'
5
+ import { Avatar, AvatarFallback } from '../../primitives/avatar'
6
+ import { Badge } from '../../primitives/badge'
7
+ import { Separator } from '../../primitives/separator'
8
+ import { Card, CardContent } from '../../primitives/card'
9
9
 
10
10
  const NAV_ITEMS = [
11
11
  { icon: LayoutDashboard, label: 'Dashboard', active: true },
@@ -1,8 +1,8 @@
1
1
  // @ts-nocheck
2
2
  import React from 'react'
3
3
  import { LayoutDashboard, BarChart2, FolderOpen, Users, CreditCard, Settings, HelpCircle } from 'lucide-react'
4
- import { cn } from '../utils/cn'
5
- import { Separator } from '../primitives/separator'
4
+ import { cn } from '../../utils/cn'
5
+ import { Separator } from '../../primitives/separator'
6
6
 
7
7
  const NAV_GROUPS = [
8
8
  {
@@ -1,9 +1,9 @@
1
1
  // @ts-nocheck
2
2
  import React from 'react'
3
3
  import { LayoutDashboard, FolderOpen, Users, Settings } from 'lucide-react'
4
- import { cn } from '../utils/cn'
5
- import { Avatar, AvatarFallback } from '../primitives/avatar'
6
- import { Separator } from '../primitives/separator'
4
+ import { cn } from '../../utils/cn'
5
+ import { Avatar, AvatarFallback } from '../../primitives/avatar'
6
+ import { Separator } from '../../primitives/separator'
7
7
 
8
8
  const NAV_ITEMS = [
9
9
  { icon: LayoutDashboard, label: 'Dashboard', active: true },
@@ -1,9 +1,9 @@
1
1
  // @ts-nocheck
2
2
  import React, { useState } from 'react'
3
3
  import { LayoutDashboard, FolderOpen, Users, Settings, BarChart2, Bell } from 'lucide-react'
4
- import { cn } from '../utils/cn'
5
- import { Input } from '../primitives/input'
6
- import { Separator } from '../primitives/separator'
4
+ import { cn } from '../../utils/cn'
5
+ import { Input } from '../../primitives/input'
6
+ import { Separator } from '../../primitives/separator'
7
7
 
8
8
  const ALL_ITEMS = [
9
9
  { icon: LayoutDashboard, label: 'Dashboard', active: true },
@@ -1,10 +1,10 @@
1
1
  // @ts-nocheck
2
2
  import React from 'react'
3
3
  import { LayoutDashboard, FolderOpen, Users, Settings, Bell } from 'lucide-react'
4
- import { cn } from '../utils/cn'
5
- import { Avatar, AvatarFallback } from '../primitives/avatar'
6
- import { Badge } from '../primitives/badge'
7
- import { Separator } from '../primitives/separator'
4
+ import { cn } from '../../utils/cn'
5
+ import { Avatar, AvatarFallback } from '../../primitives/avatar'
6
+ import { Badge } from '../../primitives/badge'
7
+ import { Separator } from '../../primitives/separator'
8
8
 
9
9
  const NAV_ITEMS = [
10
10
  { icon: LayoutDashboard, label: 'Dashboard', active: true },
@@ -1,7 +1,7 @@
1
1
  import React, { useState } from 'react'
2
- import { cn } from '../utils/cn'
3
- import { Checkbox } from '../primitives/checkbox'
4
- import { Avatar, AvatarFallback } from '../primitives/avatar'
2
+ import { cn } from '../../utils/cn'
3
+ import { Checkbox } from '../../primitives/checkbox'
4
+ import { Avatar, AvatarFallback } from '../../primitives/avatar'
5
5
 
6
6
  type Priority = 'High' | 'Medium' | 'Low'
7
7
  type Status = 'In Progress' | 'Todo' | 'Done'
@@ -1,9 +1,9 @@
1
1
  import React from 'react'
2
2
  import { Bell, ChevronDown, ArrowRight } from 'lucide-react'
3
- import { cn } from '../utils/cn'
4
- import { Avatar, AvatarFallback } from '../primitives/avatar'
5
- import { Button } from '../primitives/button'
6
- import { Card, CardContent, CardHeader, CardTitle } from '../primitives/card'
3
+ import { cn } from '../../utils/cn'
4
+ import { Avatar, AvatarFallback } from '../../primitives/avatar'
5
+ import { Button } from '../../primitives/button'
6
+ import { Card, CardContent, CardHeader, CardTitle } from '../../primitives/card'
7
7
 
8
8
  const NAV_LINKS = ['Features', 'Pricing', 'Docs', 'Blog']
9
9
 
@@ -1,9 +1,9 @@
1
1
  import React, { useState } from 'react'
2
2
  import { Bell, ChevronDown } from 'lucide-react'
3
- import { cn } from '../utils/cn'
4
- import { Avatar, AvatarFallback } from '../primitives/avatar'
5
- import { Button } from '../primitives/button'
6
- import { Separator } from '../primitives/separator'
3
+ import { cn } from '../../utils/cn'
4
+ import { Avatar, AvatarFallback } from '../../primitives/avatar'
5
+ import { Button } from '../../primitives/button'
6
+ import { Separator } from '../../primitives/separator'
7
7
 
8
8
  const NAV_LINKS = ['Home', 'Projects', 'Analytics', 'Team']
9
9
  const TABS = ['Overview', 'Activity', 'Settings', 'Billing']
@@ -1,9 +1,9 @@
1
1
  import React from 'react'
2
2
  import { Calendar } from 'lucide-react'
3
- import { cn } from '../utils/cn'
4
- import { Card, CardContent } from '../primitives/card'
5
- import { Avatar, AvatarFallback } from '../primitives/avatar'
6
- import { Badge } from '../primitives/badge'
3
+ import { cn } from '../../utils/cn'
4
+ import { Card, CardContent } from '../../primitives/card'
5
+ import { Avatar, AvatarFallback } from '../../primitives/avatar'
6
+ import { Badge } from '../../primitives/badge'
7
7
 
8
8
  const PRIORITY_COLOR: Record<string, string> = {
9
9
  High: 'bg-red-500/10 text-red-600 border-red-200',
@@ -1,7 +1,7 @@
1
1
  import React from 'react'
2
2
  import { ChevronDown, User, Settings, CreditCard, LogOut } from 'lucide-react'
3
- import { cn } from '../utils/cn'
4
- import { Avatar, AvatarFallback } from '../primitives/avatar'
3
+ import { cn } from '../../utils/cn'
4
+ import { Avatar, AvatarFallback } from '../../primitives/avatar'
5
5
  import {
6
6
  DropdownMenu,
7
7
  DropdownMenuContent,
@@ -9,7 +9,7 @@ import {
9
9
  DropdownMenuLabel,
10
10
  DropdownMenuSeparator,
11
11
  DropdownMenuTrigger,
12
- } from '../primitives/dropdown-menu'
12
+ } from '../../primitives/dropdown-menu'
13
13
 
14
14
  export interface UserMenuProps {
15
15
  name?: string
@@ -1,10 +1,10 @@
1
1
  import React, { useState } from 'react'
2
2
  import { Search, X } from 'lucide-react'
3
- import { cn } from '../utils/cn'
4
- import { Input } from '../primitives/input'
5
- import { Avatar, AvatarFallback } from '../primitives/avatar'
6
- import { Badge } from '../primitives/badge'
7
- import { Button } from '../primitives/button'
3
+ import { cn } from '../../utils/cn'
4
+ import { Input } from '../../primitives/input'
5
+ import { Avatar, AvatarFallback } from '../../primitives/avatar'
6
+ import { Badge } from '../../primitives/badge'
7
+ import { Button } from '../../primitives/button'
8
8
 
9
9
  const MEMBERS = [
10
10
  { id: '1', name: 'Jane Doe', email: 'jane@acme.com', role: 'Owner', initials: 'JD' },
@@ -1,73 +1 @@
1
- import React, { useState } from 'react'
2
- import { Send } from 'lucide-react'
3
- import { cn } from '../utils/cn'
4
- import { Avatar, AvatarFallback } from '../primitives/avatar'
5
- import { Input } from '../primitives/input'
6
- import { Button } from '../primitives/button'
7
-
8
- interface Message { id: string; author: string; initials: string; text: string; time: string; self?: boolean }
9
-
10
- const INITIAL_MESSAGES: Message[] = [
11
- { id: '1', author: 'Alice Chen', initials: 'AC', text: 'Hey team, just pushed the new design files to Figma.', time: '9:41 AM' },
12
- { id: '2', author: 'Bob Smith', initials: 'BS', text: "Awesome! I'll take a look and share feedback by EOD.", time: '9:43 AM' },
13
- { id: '3', author: 'You', initials: 'JD', text: 'Looks great, the new color palette really pops!', time: '9:45 AM', self: true },
14
- { id: '4', author: 'Alice Chen', initials: 'AC', text: "Thanks! Let's sync tomorrow to go through the comments.", time: '9:47 AM' },
15
- ]
16
-
17
- function ChatBubble({ msg }: { msg: Message }) {
18
- return (
19
- <div className={cn('flex gap-2.5', msg.self && 'flex-row-reverse')}>
20
- <Avatar className="h-7 w-7 shrink-0 mt-0.5">
21
- <AvatarFallback className="text-xs">{msg.initials}</AvatarFallback>
22
- </Avatar>
23
- <div className={cn('max-w-[70%]', msg.self && 'items-end flex flex-col')}>
24
- <div className="flex items-baseline gap-2 mb-0.5">
25
- <span className="text-xs font-medium">{msg.author}</span>
26
- <span className="text-[10px] text-[hsl(var(--muted-foreground))]">{msg.time}</span>
27
- </div>
28
- <div className={cn(
29
- 'rounded-md px-3 py-2 text-sm',
30
- msg.self
31
- ? 'bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))]'
32
- : 'bg-[hsl(var(--muted))]'
33
- )}>
34
- {msg.text}
35
- </div>
36
- </div>
37
- </div>
38
- )
39
- }
40
-
41
- export interface ChatDetailProps { className?: string }
42
-
43
- export function ChatDetail({ className }: ChatDetailProps) {
44
- const [messages, setMessages] = useState(INITIAL_MESSAGES)
45
- const [input, setInput] = useState('')
46
-
47
- const send = () => {
48
- if (!input.trim()) return
49
- const now = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
50
- setMessages(m => [...m, { id: `${Date.now()}`, author: 'You', initials: 'JD', text: input.trim(), time: now, self: true }])
51
- setInput('')
52
- }
53
-
54
- return (
55
- <div className={cn('flex h-[480px] flex-col overflow-hidden rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--card))]', className)}>
56
- <header className="flex items-center gap-3 border-b border-[hsl(var(--border))] px-4 py-3">
57
- <Avatar className="h-8 w-8"><AvatarFallback className="text-xs">AC</AvatarFallback></Avatar>
58
- <div>
59
- <p className="text-sm font-semibold">Design Team</p>
60
- <p className="text-xs text-[hsl(var(--muted-foreground))]">4 members</p>
61
- </div>
62
- </header>
63
- <div className="flex-1 overflow-auto p-4 space-y-4">
64
- {messages.map(m => <ChatBubble key={m.id} msg={m} />)}
65
- </div>
66
- <div className="flex items-center gap-2 border-t border-[hsl(var(--border))] p-3">
67
- <Input className="flex-1 h-9 text-sm" placeholder="Type a message..." value={input}
68
- onChange={e => setInput(e.target.value)} onKeyDown={e => e.key === 'Enter' && send()} />
69
- <Button size="icon" className="h-9 w-9 shrink-0" onClick={send}><Send className="h-4 w-4" /></Button>
70
- </div>
71
- </div>
72
- )
73
- }
1
+ export * from './ChatDetail'
@@ -1,68 +1 @@
1
- import React, { useState } from 'react'
2
- import { cn } from '../utils/cn'
3
- import { Button } from '../primitives/button'
4
- import { Textarea } from '../primitives/textarea'
5
-
6
- const REACTIONS = ['😊', '😐', '😕', '😡', '🤩']
7
-
8
- export interface FeedbackModalProps {
9
- onSubmit?: (data: { reaction: string; text: string }) => void
10
- className?: string
11
- }
12
-
13
- export function FeedbackModal({ onSubmit, className }: FeedbackModalProps) {
14
- const [reaction, setReaction] = useState('')
15
- const [text, setText] = useState('')
16
- const [submitted, setSubmitted] = useState(false)
17
-
18
- const handleSubmit = () => {
19
- onSubmit?.({ reaction, text })
20
- setSubmitted(true)
21
- }
22
-
23
- if (submitted) {
24
- return (
25
- <div className={cn('flex flex-col items-center gap-3 py-8 px-6 text-center', className)}>
26
- <span className="text-4xl">🎉</span>
27
- <p className="font-semibold">Thanks for your feedback!</p>
28
- <p className="text-sm text-[hsl(var(--muted-foreground))]">We'll use it to improve your experience.</p>
29
- </div>
30
- )
31
- }
32
-
33
- return (
34
- <div className={cn('flex flex-col gap-4 p-6', className)}>
35
- <div>
36
- <h2 className="text-base font-semibold">Send Feedback</h2>
37
- <p className="text-sm text-[hsl(var(--muted-foreground))] mt-0.5">How are you feeling about the product?</p>
38
- </div>
39
- <div className="flex gap-2">
40
- {REACTIONS.map(r => (
41
- <button
42
- key={r}
43
- onClick={() => setReaction(r)}
44
- className={cn(
45
- 'flex h-10 w-10 items-center justify-center rounded-md border text-xl transition-colors',
46
- reaction === r
47
- ? 'border-[hsl(var(--primary))] bg-[hsl(var(--primary)/0.1)]'
48
- : 'border-[hsl(var(--border))] hover:bg-[hsl(var(--accent))]'
49
- )}
50
- >
51
- {r}
52
- </button>
53
- ))}
54
- </div>
55
- <Textarea
56
- placeholder="Tell us more (optional)..."
57
- value={text}
58
- onChange={e => setText(e.target.value)}
59
- className="resize-none"
60
- rows={4}
61
- />
62
- <div className="flex justify-end gap-2">
63
- <Button variant="outline" size="sm">Cancel</Button>
64
- <Button size="sm" onClick={handleSubmit} disabled={!reaction}>Submit</Button>
65
- </div>
66
- </div>
67
- )
68
- }
1
+ export * from './FeedbackModal'
@@ -1,56 +1 @@
1
- // @ts-nocheck
2
- import React from 'react'
3
- import { FileText, Image, FileCode, Film, Archive, File } from 'lucide-react'
4
- import { cn } from '../utils/cn'
5
- import { Card, CardContent } from '../primitives/card'
6
-
7
- interface FileItem { id: string; name: string; type: string; size: string; modified: string }
8
-
9
- const FILES: FileItem[] = [
10
- { id: '1', name: 'design-system.fig', type: 'figma', size: '14.2 MB', modified: 'Mar 12' },
11
- { id: '2', name: 'hero-banner.png', type: 'image', size: '3.8 MB', modified: 'Mar 11' },
12
- { id: '3', name: 'api-spec.yaml', type: 'code', size: '42 KB', modified: 'Mar 10' },
13
- { id: '4', name: 'demo-video.mp4', type: 'video', size: '128 MB', modified: 'Mar 9' },
14
- { id: '5', name: 'assets.zip', type: 'archive', size: '56 MB', modified: 'Mar 8' },
15
- { id: '6', name: 'meeting-notes.pdf', type: 'pdf', size: '1.1 MB', modified: 'Mar 7' },
16
- ]
17
-
18
- const TYPE_ICON: Record<string, React.ElementType> = {
19
- figma: FileText, image: Image, code: FileCode, video: Film, archive: Archive, pdf: FileText,
20
- }
21
-
22
- const TYPE_COLOR: Record<string, string> = {
23
- figma: 'text-purple-500', image: 'text-green-500', code: 'text-blue-500',
24
- video: 'text-red-500', archive: 'text-yellow-500', pdf: 'text-orange-500',
25
- }
26
-
27
- function FileCard({ file }: { file: FileItem }) {
28
- const Icon = TYPE_ICON[file.type] ?? File
29
- return (
30
- <Card className="cursor-pointer hover:shadow-md transition-shadow">
31
- <CardContent className="p-4">
32
- <div className={cn('flex h-10 w-10 items-center justify-center rounded-md bg-[hsl(var(--muted))] mb-3', TYPE_COLOR[file.type])}>
33
- <Icon className="h-5 w-5" />
34
- </div>
35
- <p className="text-sm font-medium truncate">{file.name}</p>
36
- <div className="flex items-center justify-between mt-1">
37
- <span className="text-xs text-[hsl(var(--muted-foreground))]">{file.size}</span>
38
- <span className="text-xs text-[hsl(var(--muted-foreground))]">{file.modified}</span>
39
- </div>
40
- </CardContent>
41
- </Card>
42
- )
43
- }
44
-
45
- export interface FileCardsProps {
46
- files?: FileItem[]
47
- className?: string
48
- }
49
-
50
- export function FileCards({ files = FILES, className }: FileCardsProps) {
51
- return (
52
- <div className={cn('grid gap-3', className)} style={{ gridTemplateColumns: 'repeat(auto-fill, minmax(min(100%,160px),1fr))' }}>
53
- {files.map(f => <FileCard key={f.id} file={f} />)}
54
- </div>
55
- )
56
- }
1
+ export * from './FileCards'
@@ -1,72 +1 @@
1
- // @ts-nocheck
2
- import React from 'react'
3
- import { FileText, Image, FileCode, Film, Archive, MoreHorizontal, Download, Trash2 } from 'lucide-react'
4
- import { cn } from '../utils/cn'
5
- import { Button } from '../primitives/button'
6
- import {
7
- DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger,
8
- } from '../primitives/dropdown-menu'
9
-
10
- interface FileItem { id: string; name: string; type: string; size: string; modified: string }
11
-
12
- const FILES: FileItem[] = [
13
- { id: '1', name: 'design-system.fig', type: 'Figma', size: '14.2 MB', modified: 'Mar 12, 2024' },
14
- { id: '2', name: 'hero-banner.png', type: 'PNG Image', size: '3.8 MB', modified: 'Mar 11, 2024' },
15
- { id: '3', name: 'api-spec.yaml', type: 'YAML', size: '42 KB', modified: 'Mar 10, 2024' },
16
- { id: '4', name: 'demo-video.mp4', type: 'MP4 Video', size: '128 MB', modified: 'Mar 9, 2024' },
17
- { id: '5', name: 'assets.zip', type: 'ZIP Archive', size: '56 MB', modified: 'Mar 8, 2024' },
18
- ]
19
-
20
- const TYPE_ICON: Record<string, React.ElementType> = {
21
- Figma: FileText, 'PNG Image': Image, YAML: FileCode, 'MP4 Video': Film, 'ZIP Archive': Archive,
22
- }
23
-
24
- function FileRow({ file, onDelete }: { file: FileItem; onDelete: () => void }) {
25
- const Icon = TYPE_ICON[file.type] ?? FileText
26
- return (
27
- <tr className="border-b border-[hsl(var(--border))] last:border-0 hover:bg-[hsl(var(--accent)/0.5)] transition-colors">
28
- <td className="px-4 py-3">
29
- <div className="flex items-center gap-2.5">
30
- <Icon className="h-4 w-4 shrink-0 text-[hsl(var(--muted-foreground))]" />
31
- <span className="text-sm font-medium">{file.name}</span>
32
- </div>
33
- </td>
34
- <td className="px-4 py-3 text-sm text-[hsl(var(--muted-foreground))]">{file.type}</td>
35
- <td className="px-4 py-3 text-sm text-[hsl(var(--muted-foreground))]">{file.size}</td>
36
- <td className="px-4 py-3 text-sm text-[hsl(var(--muted-foreground))]">{file.modified}</td>
37
- <td className="px-4 py-3 text-right">
38
- <DropdownMenu>
39
- <DropdownMenuTrigger asChild>
40
- <Button size="icon" variant="ghost" className="h-7 w-7"><MoreHorizontal className="h-4 w-4" /></Button>
41
- </DropdownMenuTrigger>
42
- <DropdownMenuContent align="end">
43
- <DropdownMenuItem><Download className="h-4 w-4" />Download</DropdownMenuItem>
44
- <DropdownMenuItem onClick={onDelete} className="text-[hsl(var(--destructive))]">
45
- <Trash2 className="h-4 w-4" />Delete
46
- </DropdownMenuItem>
47
- </DropdownMenuContent>
48
- </DropdownMenu>
49
- </td>
50
- </tr>
51
- )
52
- }
53
-
54
- export interface FilesListProps { className?: string }
55
-
56
- export function FilesList({ className }: FilesListProps) {
57
- const [files, setFiles] = React.useState(FILES)
58
- return (
59
- <div className={cn('rounded-md border border-[hsl(var(--border))] overflow-hidden', className)}>
60
- <table className="w-full">
61
- <thead className="bg-[hsl(var(--muted)/0.5)]">
62
- <tr>{['Name', 'Type', 'Size', 'Modified', ''].map(h => (
63
- <th key={h} className="px-4 py-2.5 text-left text-xs font-medium text-[hsl(var(--muted-foreground))]">{h}</th>
64
- ))}</tr>
65
- </thead>
66
- <tbody>
67
- {files.map(f => <FileRow key={f.id} file={f} onDelete={() => setFiles(fs => fs.filter(x => x.id !== f.id))} />)}
68
- </tbody>
69
- </table>
70
- </div>
71
- )
72
- }
1
+ export * from './FilesList'
@@ -1,50 +1 @@
1
- import React, { useState } from 'react'
2
- import { cn } from '../utils/cn'
3
- import { Badge } from '../primitives/badge'
4
- import { Button } from '../primitives/button'
5
-
6
- export interface IntegrationCardProps {
7
- icon?: React.ReactNode
8
- name?: string
9
- description?: string
10
- connected?: boolean
11
- className?: string
12
- }
13
-
14
- export function IntegrationCard({
15
- icon,
16
- name = 'Slack',
17
- description = 'Send notifications and updates to your Slack workspace.',
18
- connected = false,
19
- className,
20
- }: IntegrationCardProps) {
21
- const [isConnected, setIsConnected] = useState(connected)
22
-
23
- return (
24
- <div className={cn('flex items-center gap-4 rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4', className)}>
25
- <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-md bg-[hsl(var(--muted))] text-[hsl(var(--muted-foreground))]">
26
- {icon ?? <span className="text-base font-bold">{name[0]}</span>}
27
- </div>
28
- <div className="flex-1 min-w-0">
29
- <div className="flex items-center gap-2">
30
- <p className="text-sm font-semibold">{name}</p>
31
- <Badge
32
- variant={isConnected ? 'default' : 'outline'}
33
- className="text-[10px] px-1.5 py-0.5 shrink-0"
34
- >
35
- {isConnected ? 'Connected' : 'Not connected'}
36
- </Badge>
37
- </div>
38
- <p className="text-xs text-[hsl(var(--muted-foreground))] mt-0.5 truncate">{description}</p>
39
- </div>
40
- <Button
41
- size="sm"
42
- variant={isConnected ? 'outline' : 'default'}
43
- className="shrink-0"
44
- onClick={() => setIsConnected(v => !v)}
45
- >
46
- {isConnected ? 'Disconnect' : 'Connect'}
47
- </Button>
48
- </div>
49
- )
50
- }
1
+ export * from './IntegrationCard'
@@ -1,69 +1 @@
1
- import React, { useState } from 'react'
2
- import { X } from 'lucide-react'
3
- import { cn } from '../utils/cn'
4
- import { Input } from '../primitives/input'
5
- import { Button } from '../primitives/button'
6
- import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../primitives/select'
7
- import { Avatar, AvatarFallback } from '../primitives/avatar'
8
- import { Badge } from '../primitives/badge'
9
-
10
- const PENDING = [
11
- { email: 'alex@startup.io', role: 'Member' },
12
- { email: 'sam@startup.io', role: 'Viewer' },
13
- ]
14
-
15
- export interface InviteModalProps { className?: string }
16
-
17
- export function InviteModal({ className }: InviteModalProps) {
18
- const [email, setEmail] = useState('')
19
- const [role, setRole] = useState('Member')
20
- const [pending, setPending] = useState(PENDING)
21
-
22
- const invite = () => {
23
- if (!email.trim()) return
24
- setPending(p => [...p, { email: email.trim(), role }])
25
- setEmail('')
26
- }
27
-
28
- return (
29
- <div className={cn('flex flex-col gap-4 p-6', className)}>
30
- <div>
31
- <h2 className="text-base font-semibold">Invite People</h2>
32
- <p className="text-sm text-[hsl(var(--muted-foreground))] mt-0.5">Invite team members to collaborate.</p>
33
- </div>
34
- <div className="flex gap-2">
35
- <Input
36
- className="flex-1 h-9 text-sm"
37
- placeholder="colleague@company.com"
38
- value={email}
39
- onChange={e => setEmail(e.target.value)}
40
- onKeyDown={e => e.key === 'Enter' && invite()}
41
- />
42
- <Select value={role} onValueChange={setRole}>
43
- <SelectTrigger className="h-9 w-28 text-sm"><SelectValue /></SelectTrigger>
44
- <SelectContent>
45
- {['Admin', 'Member', 'Viewer'].map(r => <SelectItem key={r} value={r}>{r}</SelectItem>)}
46
- </SelectContent>
47
- </Select>
48
- <Button size="sm" className="h-9" onClick={invite}>Send</Button>
49
- </div>
50
- {pending.length > 0 && (
51
- <div className="space-y-1">
52
- <p className="text-xs font-medium text-[hsl(var(--muted-foreground))]">Pending invites</p>
53
- <div className="divide-y divide-[hsl(var(--border))] rounded-md border border-[hsl(var(--border))]">
54
- {pending.map((p, i) => (
55
- <div key={i} className="flex items-center gap-2 px-3 py-2">
56
- <Avatar className="h-6 w-6"><AvatarFallback className="text-[10px]">{p.email[0].toUpperCase()}</AvatarFallback></Avatar>
57
- <span className="flex-1 text-sm truncate">{p.email}</span>
58
- <Badge variant="outline" className="text-[10px] px-1.5 py-0.5 shrink-0">{p.role}</Badge>
59
- <button onClick={() => setPending(ps => ps.filter((_, j) => j !== i))}>
60
- <X className="h-3.5 w-3.5 text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]" />
61
- </button>
62
- </div>
63
- ))}
64
- </div>
65
- </div>
66
- )}
67
- </div>
68
- )
69
- }
1
+ export * from './InviteModal'