@handled-ai/design-system 0.8.0 → 0.9.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/README.md +14 -4
- package/dist/charts/bar-chart-component.d.ts +24 -0
- package/dist/charts/bar-chart-component.js +123 -0
- package/dist/charts/bar-chart-component.js.map +1 -0
- package/dist/charts/chart-tooltip.d.ts +26 -0
- package/dist/charts/chart-tooltip.js +69 -0
- package/dist/charts/chart-tooltip.js.map +1 -0
- package/dist/charts/chart.d.ts +64 -0
- package/dist/charts/chart.js +285 -0
- package/dist/charts/chart.js.map +1 -0
- package/dist/charts/donut-chart.d.ts +21 -0
- package/dist/charts/donut-chart.js +96 -0
- package/dist/charts/donut-chart.js.map +1 -0
- package/dist/charts/index.d.ts +11 -0
- package/dist/charts/index.js +10 -0
- package/dist/charts/index.js.map +1 -0
- package/dist/charts/pipeline-overview.d.ts +76 -0
- package/dist/charts/pipeline-overview.js +372 -0
- package/dist/charts/pipeline-overview.js.map +1 -0
- package/dist/charts/sankey-chart.d.ts +52 -0
- package/dist/charts/sankey-chart.js +219 -0
- package/dist/charts/sankey-chart.js.map +1 -0
- package/dist/charts/top-line-metrics.d.ts +26 -0
- package/dist/charts/top-line-metrics.js +224 -0
- package/dist/charts/top-line-metrics.js.map +1 -0
- package/dist/charts/trend-area-chart.d.ts +21 -0
- package/dist/charts/trend-area-chart.js +150 -0
- package/dist/charts/trend-area-chart.js.map +1 -0
- package/dist/charts/volume-analysis-chart.d.ts +19 -0
- package/dist/charts/volume-analysis-chart.js +121 -0
- package/dist/charts/volume-analysis-chart.js.map +1 -0
- package/dist/components/activity-detail.d.ts +38 -0
- package/dist/components/activity-detail.js +163 -0
- package/dist/components/activity-detail.js.map +1 -0
- package/dist/components/activity-log.d.ts +21 -0
- package/dist/components/activity-log.js +61 -0
- package/dist/components/activity-log.js.map +1 -0
- package/dist/components/agent-popover.d.ts +71 -0
- package/dist/components/agent-popover.js +282 -0
- package/dist/components/agent-popover.js.map +1 -0
- package/dist/components/agent-widget.d.ts +24 -0
- package/dist/components/agent-widget.js +117 -0
- package/dist/components/agent-widget.js.map +1 -0
- package/dist/components/avatar.d.ts +13 -0
- package/dist/components/avatar.js +140 -0
- package/dist/components/avatar.js.map +1 -0
- package/dist/components/badge.d.ts +12 -0
- package/dist/components/badge.js +75 -0
- package/dist/components/badge.js.map +1 -0
- package/dist/components/button.d.ts +13 -0
- package/dist/components/button.js +83 -0
- package/dist/components/button.js.map +1 -0
- package/dist/components/card.d.ts +11 -0
- package/dist/components/card.js +119 -0
- package/dist/components/card.js.map +1 -0
- package/dist/components/contact-list.d.ts +34 -0
- package/dist/components/contact-list.js +84 -0
- package/dist/components/contact-list.js.map +1 -0
- package/dist/components/dashboard-cards.d.ts +10 -0
- package/dist/components/dashboard-cards.js +164 -0
- package/dist/components/dashboard-cards.js.map +1 -0
- package/dist/components/data-table-display.d.ts +19 -0
- package/dist/components/data-table-display.js +109 -0
- package/dist/components/data-table-display.js.map +1 -0
- package/dist/components/data-table-filter.d.ts +18 -0
- package/dist/components/data-table-filter.js +107 -0
- package/dist/components/data-table-filter.js.map +1 -0
- package/dist/components/data-table-quick-views.d.ts +13 -0
- package/dist/components/data-table-quick-views.js +90 -0
- package/dist/components/data-table-quick-views.js.map +1 -0
- package/dist/components/data-table-toolbar.d.ts +18 -0
- package/dist/components/data-table-toolbar.js +45 -0
- package/dist/components/data-table-toolbar.js.map +1 -0
- package/dist/components/data-table.d.ts +39 -0
- package/dist/components/data-table.js +821 -0
- package/dist/components/data-table.js.map +1 -0
- package/dist/components/detail-view.d.ts +44 -0
- package/dist/components/detail-view.js +165 -0
- package/dist/components/detail-view.js.map +1 -0
- package/dist/components/dialog.d.ts +19 -0
- package/dist/components/dialog.js +188 -0
- package/dist/components/dialog.js.map +1 -0
- package/dist/components/dropdown-menu.d.ts +27 -0
- package/dist/components/dropdown-menu.js +279 -0
- package/dist/components/dropdown-menu.js.map +1 -0
- package/dist/components/entity-panel.d.ts +69 -0
- package/dist/components/entity-panel.js +584 -0
- package/dist/components/entity-panel.js.map +1 -0
- package/dist/components/inbox-row.d.ts +27 -0
- package/dist/components/inbox-row.js +139 -0
- package/dist/components/inbox-row.js.map +1 -0
- package/dist/components/inbox-toolbar.d.ts +21 -0
- package/dist/components/inbox-toolbar.js +203 -0
- package/dist/components/inbox-toolbar.js.map +1 -0
- package/dist/components/input.d.ts +5 -0
- package/dist/components/input.js +50 -0
- package/dist/components/input.js.map +1 -0
- package/dist/components/insights-filter-bar.d.ts +21 -0
- package/dist/components/insights-filter-bar.js +99 -0
- package/dist/components/insights-filter-bar.js.map +1 -0
- package/dist/components/item-list-display.d.ts +22 -0
- package/dist/components/item-list-display.js +240 -0
- package/dist/components/item-list-display.js.map +1 -0
- package/dist/components/item-list-filter.d.ts +16 -0
- package/dist/components/item-list-filter.js +87 -0
- package/dist/components/item-list-filter.js.map +1 -0
- package/dist/components/item-list-toolbar.d.ts +25 -0
- package/dist/components/item-list-toolbar.js +79 -0
- package/dist/components/item-list-toolbar.js.map +1 -0
- package/dist/components/item-list.d.ts +20 -0
- package/dist/components/item-list.js +702 -0
- package/dist/components/item-list.js.map +1 -0
- package/dist/components/label.d.ts +6 -0
- package/dist/components/label.js +55 -0
- package/dist/components/label.js.map +1 -0
- package/dist/components/message.d.ts +23 -0
- package/dist/components/message.js +117 -0
- package/dist/components/message.js.map +1 -0
- package/dist/components/metric-card.d.ts +25 -0
- package/dist/components/metric-card.js +107 -0
- package/dist/components/metric-card.js.map +1 -0
- package/dist/components/performance-metrics-table.d.ts +38 -0
- package/dist/components/performance-metrics-table.js +342 -0
- package/dist/components/performance-metrics-table.js.map +1 -0
- package/dist/components/preview-list.d.ts +14 -0
- package/dist/components/preview-list.js +83 -0
- package/dist/components/preview-list.js.map +1 -0
- package/dist/components/progress.d.ts +6 -0
- package/dist/components/progress.js +69 -0
- package/dist/components/progress.js.map +1 -0
- package/dist/components/quick-action-chat-area.d.ts +24 -0
- package/dist/components/quick-action-chat-area.js +178 -0
- package/dist/components/quick-action-chat-area.js.map +1 -0
- package/dist/components/quick-action-modal.d.ts +30 -0
- package/dist/components/quick-action-modal.js +288 -0
- package/dist/components/quick-action-modal.js.map +1 -0
- package/dist/components/quick-action-sidebar-nav.d.ts +51 -0
- package/dist/components/quick-action-sidebar-nav.js +528 -0
- package/dist/components/quick-action-sidebar-nav.js.map +1 -0
- package/dist/components/recommended-actions-section.d.ts +23 -0
- package/dist/components/recommended-actions-section.js +215 -0
- package/dist/components/recommended-actions-section.js.map +1 -0
- package/dist/components/report-card.d.ts +26 -0
- package/dist/components/report-card.js +69 -0
- package/dist/components/report-card.js.map +1 -0
- package/dist/components/score-analysis-modal.d.ts +26 -0
- package/dist/components/score-analysis-modal.js +141 -0
- package/dist/components/score-analysis-modal.js.map +1 -0
- package/dist/components/score-breakdown.d.ts +17 -0
- package/dist/components/score-breakdown.js +162 -0
- package/dist/components/score-breakdown.js.map +1 -0
- package/dist/components/score-feedback.d.ts +40 -0
- package/dist/components/score-feedback.js +209 -0
- package/dist/components/score-feedback.js.map +1 -0
- package/dist/components/score-ring.d.ts +14 -0
- package/dist/components/score-ring.js +79 -0
- package/dist/components/score-ring.js.map +1 -0
- package/dist/components/scroll-area.d.ts +7 -0
- package/dist/components/scroll-area.js +101 -0
- package/dist/components/scroll-area.js.map +1 -0
- package/dist/components/select.d.ts +17 -0
- package/dist/components/select.js +228 -0
- package/dist/components/select.js.map +1 -0
- package/dist/components/separator.d.ts +6 -0
- package/dist/components/separator.js +61 -0
- package/dist/components/separator.js.map +1 -0
- package/dist/components/sheet.d.ts +16 -0
- package/dist/components/sheet.js +168 -0
- package/dist/components/sheet.js.map +1 -0
- package/dist/components/sidebar.d.ts +73 -0
- package/dist/components/sidebar.js +723 -0
- package/dist/components/sidebar.js.map +1 -0
- package/dist/components/signal-feedback-inline.d.ts +51 -0
- package/dist/components/signal-feedback-inline.js +548 -0
- package/dist/components/signal-feedback-inline.js.map +1 -0
- package/dist/components/simple-data-table.d.ts +15 -0
- package/dist/components/simple-data-table.js +91 -0
- package/dist/components/simple-data-table.js.map +1 -0
- package/dist/components/skeleton.d.ts +5 -0
- package/dist/components/skeleton.js +44 -0
- package/dist/components/skeleton.js.map +1 -0
- package/dist/components/status-badge.d.ts +10 -0
- package/dist/components/status-badge.js +82 -0
- package/dist/components/status-badge.js.map +1 -0
- package/dist/components/styled-bar-list.d.ts +20 -0
- package/dist/components/styled-bar-list.js +59 -0
- package/dist/components/styled-bar-list.js.map +1 -0
- package/dist/components/suggested-actions.d.ts +110 -0
- package/dist/components/suggested-actions.js +1538 -0
- package/dist/components/suggested-actions.js.map +1 -0
- package/dist/components/table.d.ts +12 -0
- package/dist/components/table.js +147 -0
- package/dist/components/table.js.map +1 -0
- package/dist/components/tabs.d.ts +14 -0
- package/dist/components/tabs.js +129 -0
- package/dist/components/tabs.js.map +1 -0
- package/dist/components/textarea.d.ts +5 -0
- package/dist/components/textarea.js +47 -0
- package/dist/components/textarea.js.map +1 -0
- package/dist/components/timeline-activity.d.ts +34 -0
- package/dist/components/timeline-activity.js +181 -0
- package/dist/components/timeline-activity.js.map +1 -0
- package/dist/components/tooltip.d.ts +9 -0
- package/dist/components/tooltip.js +93 -0
- package/dist/components/tooltip.js.map +1 -0
- package/dist/components/view-mode-toggle.d.ts +16 -0
- package/dist/components/view-mode-toggle.js +24 -0
- package/dist/components/view-mode-toggle.js.map +1 -0
- package/dist/hooks/use-mobile.d.ts +3 -0
- package/dist/hooks/use-mobile.js +21 -0
- package/dist/hooks/use-mobile.js.map +1 -0
- package/dist/index.d.ts +68 -1878
- package/dist/index.js +69 -10918
- package/dist/index.js.map +1 -1
- package/dist/lib/icons.d.ts +18 -0
- package/dist/lib/icons.js +21 -0
- package/dist/lib/icons.js.map +1 -0
- package/dist/lib/utils.d.ts +5 -0
- package/dist/lib/utils.js +9 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/prototype/index.d.ts +20 -0
- package/dist/prototype/index.js +8 -0
- package/dist/prototype/index.js.map +1 -0
- package/dist/prototype/prototype-accounts-view.d.ts +22 -0
- package/dist/prototype/prototype-accounts-view.js +70 -0
- package/dist/prototype/prototype-accounts-view.js.map +1 -0
- package/dist/prototype/prototype-admin-view.d.ts +21 -0
- package/dist/prototype/prototype-admin-view.js +53 -0
- package/dist/prototype/prototype-admin-view.js.map +1 -0
- package/dist/prototype/prototype-config.d.ts +226 -0
- package/dist/prototype/prototype-config.js +1 -0
- package/dist/prototype/prototype-config.js.map +1 -0
- package/dist/prototype/prototype-inbox-view.d.ts +48 -0
- package/dist/prototype/prototype-inbox-view.js +701 -0
- package/dist/prototype/prototype-inbox-view.js.map +1 -0
- package/dist/prototype/prototype-insights-view.d.ts +23 -0
- package/dist/prototype/prototype-insights-view.js +335 -0
- package/dist/prototype/prototype-insights-view.js.map +1 -0
- package/dist/prototype/prototype-shell.d.ts +40 -0
- package/dist/prototype/prototype-shell.js +190 -0
- package/dist/prototype/prototype-shell.js.map +1 -0
- package/dist/prototype/prototype-work-queue-view.d.ts +8 -0
- package/dist/prototype/prototype-work-queue-view.js +17 -0
- package/dist/prototype/prototype-work-queue-view.js.map +1 -0
- package/dist/three/agent-orb.d.ts +39 -0
- package/dist/three/agent-orb.js +500 -0
- package/dist/three/agent-orb.js.map +1 -0
- package/dist/three/index.d.ts +2 -0
- package/dist/three/index.js +2 -0
- package/dist/three/index.js.map +1 -0
- package/package.json +98 -17
- package/src/charts/bar-chart-component.tsx +150 -0
- package/src/charts/chart-tooltip.tsx +86 -0
- package/src/charts/chart.tsx +371 -0
- package/src/charts/donut-chart.tsx +112 -0
- package/src/charts/index.ts +13 -0
- package/src/charts/pipeline-overview.tsx +476 -0
- package/src/charts/sankey-chart.tsx +290 -0
- package/src/charts/top-line-metrics.tsx +261 -0
- package/src/charts/trend-area-chart.tsx +150 -0
- package/src/charts/volume-analysis-chart.tsx +124 -0
- package/src/components/activity-detail.tsx +233 -0
- package/src/components/activity-log.tsx +89 -0
- package/src/components/agent-popover.tsx +373 -0
- package/src/components/agent-widget.tsx +163 -0
- package/src/components/avatar.tsx +109 -0
- package/src/components/badge.tsx +48 -0
- package/src/components/button.tsx +59 -0
- package/src/components/card.tsx +92 -0
- package/src/components/contact-list.tsx +121 -0
- package/src/components/dashboard-cards.tsx +170 -0
- package/src/components/data-table-display.tsx +139 -0
- package/src/components/data-table-filter.tsx +138 -0
- package/src/components/data-table-quick-views.tsx +103 -0
- package/src/components/data-table-toolbar.tsx +56 -0
- package/src/components/data-table.tsx +915 -0
- package/src/components/detail-view.tsx +237 -0
- package/src/components/dialog.tsx +158 -0
- package/src/components/dropdown-menu.tsx +257 -0
- package/src/components/entity-panel.tsx +767 -0
- package/src/components/inbox-row.tsx +132 -0
- package/src/components/inbox-toolbar.tsx +213 -0
- package/src/components/input.tsx +21 -0
- package/src/components/insights-filter-bar.tsx +132 -0
- package/src/components/item-list-display.tsx +278 -0
- package/src/components/item-list-filter.tsx +118 -0
- package/src/components/item-list-toolbar.tsx +97 -0
- package/src/components/item-list.tsx +843 -0
- package/src/components/label.tsx +24 -0
- package/src/components/message.tsx +83 -0
- package/src/components/metric-card.tsx +178 -0
- package/src/components/performance-metrics-table.tsx +442 -0
- package/src/components/preview-list.tsx +62 -0
- package/src/components/progress.tsx +31 -0
- package/src/components/quick-action-chat-area.tsx +156 -0
- package/src/components/quick-action-modal.tsx +331 -0
- package/src/components/quick-action-sidebar-nav.tsx +592 -0
- package/src/components/recommended-actions-section.tsx +258 -0
- package/src/components/report-card.tsx +106 -0
- package/src/components/score-analysis-modal.tsx +172 -0
- package/src/components/score-breakdown.tsx +179 -0
- package/src/components/score-feedback.tsx +288 -0
- package/src/components/score-ring.tsx +87 -0
- package/src/components/scroll-area.tsx +58 -0
- package/src/components/select.tsx +190 -0
- package/src/components/separator.tsx +28 -0
- package/src/components/sheet.tsx +143 -0
- package/src/components/sidebar.tsx +726 -0
- package/src/components/signal-feedback-inline.tsx +591 -0
- package/src/components/simple-data-table.tsx +124 -0
- package/src/components/skeleton.tsx +15 -0
- package/src/components/status-badge.tsx +63 -0
- package/src/components/styled-bar-list.tsx +70 -0
- package/src/components/suggested-actions.tsx +1985 -0
- package/src/components/table.tsx +116 -0
- package/src/components/tabs.tsx +91 -0
- package/src/components/textarea.tsx +18 -0
- package/src/components/timeline-activity.tsx +234 -0
- package/src/components/tooltip.tsx +57 -0
- package/src/components/view-mode-toggle.tsx +39 -0
- package/src/hooks/use-mobile.ts +21 -0
- package/src/index.ts +77 -0
- package/src/lib/icons.ts +18 -0
- package/src/lib/utils.ts +6 -0
- package/src/prototype/index.ts +11 -0
- package/src/prototype/prototype-accounts-view.tsx +112 -0
- package/src/prototype/prototype-admin-view.tsx +67 -0
- package/src/prototype/prototype-config.ts +243 -0
- package/src/prototype/prototype-inbox-view.tsx +810 -0
- package/src/prototype/prototype-insights-view.tsx +379 -0
- package/src/prototype/prototype-shell.tsx +219 -0
- package/src/prototype/prototype-work-queue-view.tsx +30 -0
- package/src/styles/globals.css +299 -0
- package/src/three/agent-orb.tsx +557 -0
- package/src/three/index.ts +5 -0
- package/src/types/r3f.d.ts +8 -0
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import {
|
|
5
|
+
AlertTriangle,
|
|
6
|
+
CheckCircle2,
|
|
7
|
+
ChevronDown,
|
|
8
|
+
ChevronLeft,
|
|
9
|
+
ChevronRight,
|
|
10
|
+
} from "lucide-react"
|
|
11
|
+
|
|
12
|
+
import { cn } from "../lib/utils"
|
|
13
|
+
import { Avatar, AvatarFallback } from "./avatar"
|
|
14
|
+
import { Button } from "./button"
|
|
15
|
+
import {
|
|
16
|
+
DropdownMenu,
|
|
17
|
+
DropdownMenuContent,
|
|
18
|
+
DropdownMenuItem,
|
|
19
|
+
DropdownMenuTrigger,
|
|
20
|
+
} from "./dropdown-menu"
|
|
21
|
+
import { Input } from "./input"
|
|
22
|
+
import { ScrollArea, ScrollBar } from "./scroll-area"
|
|
23
|
+
import {
|
|
24
|
+
Table,
|
|
25
|
+
TableBody,
|
|
26
|
+
TableCell,
|
|
27
|
+
TableHead,
|
|
28
|
+
TableHeader,
|
|
29
|
+
TableRow,
|
|
30
|
+
} from "./table"
|
|
31
|
+
|
|
32
|
+
export interface PerformanceMetricsTableRow {
|
|
33
|
+
id: string
|
|
34
|
+
label: string
|
|
35
|
+
avatarFallback: string
|
|
36
|
+
role?: string
|
|
37
|
+
primaryValue: number
|
|
38
|
+
primaryTarget: number
|
|
39
|
+
ratePercent: number
|
|
40
|
+
metricOne: number
|
|
41
|
+
metricTwo: string
|
|
42
|
+
metricThree: number
|
|
43
|
+
metricFour: number
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface PerformanceMetricsTableSortOption {
|
|
47
|
+
id: "primary-desc" | "primary-asc" | "rate-desc" | "metric-four-desc"
|
|
48
|
+
label: string
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface PerformanceMetricsTableProps {
|
|
52
|
+
title?: string
|
|
53
|
+
entityColumnLabel?: string
|
|
54
|
+
primaryMetricColumnLabel?: string
|
|
55
|
+
rateColumnLabel?: string
|
|
56
|
+
metricOneColumnLabel?: string
|
|
57
|
+
metricTwoColumnLabel?: string
|
|
58
|
+
metricThreeColumnLabel?: string
|
|
59
|
+
metricFourColumnLabel?: string
|
|
60
|
+
viewOptions?: string[]
|
|
61
|
+
roleOptions?: string[]
|
|
62
|
+
sortOptions?: PerformanceMetricsTableSortOption[]
|
|
63
|
+
rows?: PerformanceMetricsTableRow[]
|
|
64
|
+
pageSize?: number
|
|
65
|
+
searchPlaceholder?: string
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const DEFAULT_ROWS: PerformanceMetricsTableRow[] = [
|
|
69
|
+
{
|
|
70
|
+
id: "member-1",
|
|
71
|
+
label: "Jennifer Davis",
|
|
72
|
+
avatarFallback: "JD",
|
|
73
|
+
role: "Senior Coordinator",
|
|
74
|
+
primaryValue: 188,
|
|
75
|
+
primaryTarget: 200,
|
|
76
|
+
ratePercent: 78,
|
|
77
|
+
metricOne: 256,
|
|
78
|
+
metricTwo: "8.5h",
|
|
79
|
+
metricThree: 401,
|
|
80
|
+
metricFour: 42,
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
id: "member-2",
|
|
84
|
+
label: "Robert Taylor",
|
|
85
|
+
avatarFallback: "RT",
|
|
86
|
+
role: "Coordinator",
|
|
87
|
+
primaryValue: 168,
|
|
88
|
+
primaryTarget: 200,
|
|
89
|
+
ratePercent: 70,
|
|
90
|
+
metricOne: 210,
|
|
91
|
+
metricTwo: "7.4h",
|
|
92
|
+
metricThree: 330,
|
|
93
|
+
metricFour: 36,
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
id: "member-3",
|
|
97
|
+
label: "Karen Park",
|
|
98
|
+
avatarFallback: "KP",
|
|
99
|
+
role: "Coordinator",
|
|
100
|
+
primaryValue: 165,
|
|
101
|
+
primaryTarget: 200,
|
|
102
|
+
ratePercent: 68,
|
|
103
|
+
metricOne: 195,
|
|
104
|
+
metricTwo: "6.9h",
|
|
105
|
+
metricThree: 298,
|
|
106
|
+
metricFour: 33,
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: "member-4",
|
|
110
|
+
label: "Alex Chen",
|
|
111
|
+
avatarFallback: "AC",
|
|
112
|
+
role: "Junior Coordinator",
|
|
113
|
+
primaryValue: 142,
|
|
114
|
+
primaryTarget: 200,
|
|
115
|
+
ratePercent: 65,
|
|
116
|
+
metricOne: 201,
|
|
117
|
+
metricTwo: "7.2h",
|
|
118
|
+
metricThree: 315,
|
|
119
|
+
metricFour: 29,
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
id: "member-5",
|
|
123
|
+
label: "Sarah Mitchell",
|
|
124
|
+
avatarFallback: "SM",
|
|
125
|
+
role: "Senior Coordinator",
|
|
126
|
+
primaryValue: 130,
|
|
127
|
+
primaryTarget: 200,
|
|
128
|
+
ratePercent: 76,
|
|
129
|
+
metricOne: 247,
|
|
130
|
+
metricTwo: "8.2h",
|
|
131
|
+
metricThree: 389,
|
|
132
|
+
metricFour: 31,
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
id: "member-6",
|
|
136
|
+
label: "Mike Rodriguez",
|
|
137
|
+
avatarFallback: "MR",
|
|
138
|
+
role: "Coordinator",
|
|
139
|
+
primaryValue: 115,
|
|
140
|
+
primaryTarget: 200,
|
|
141
|
+
ratePercent: 72,
|
|
142
|
+
metricOne: 218,
|
|
143
|
+
metricTwo: "7.8h",
|
|
144
|
+
metricThree: 342,
|
|
145
|
+
metricFour: 25,
|
|
146
|
+
},
|
|
147
|
+
]
|
|
148
|
+
|
|
149
|
+
const DEFAULT_SORT_OPTIONS: PerformanceMetricsTableSortOption[] = [
|
|
150
|
+
{ id: "primary-desc", label: "Primary Metric (High to Low)" },
|
|
151
|
+
{ id: "primary-asc", label: "Primary Metric (Low to High)" },
|
|
152
|
+
{ id: "rate-desc", label: "Rate (High to Low)" },
|
|
153
|
+
{ id: "metric-four-desc", label: "Metric Four (High to Low)" },
|
|
154
|
+
]
|
|
155
|
+
|
|
156
|
+
function getProgressStatus(value: number, target: number) {
|
|
157
|
+
const percent = (value / target) * 100
|
|
158
|
+
|
|
159
|
+
if (percent >= 80) {
|
|
160
|
+
return {
|
|
161
|
+
color: "bg-emerald-500",
|
|
162
|
+
textColor: "text-emerald-700",
|
|
163
|
+
icon: <CheckCircle2 className="h-3.5 w-3.5 text-emerald-600" />,
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (percent >= 65) {
|
|
168
|
+
return {
|
|
169
|
+
color: "bg-amber-500",
|
|
170
|
+
textColor: "text-amber-700",
|
|
171
|
+
icon: <AlertTriangle className="h-3.5 w-3.5 text-amber-600" />,
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
color: "bg-red-500",
|
|
177
|
+
textColor: "text-red-700",
|
|
178
|
+
icon: <AlertTriangle className="h-3.5 w-3.5 text-red-600" />,
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function sortRows(
|
|
183
|
+
rows: PerformanceMetricsTableRow[],
|
|
184
|
+
sortId: PerformanceMetricsTableSortOption["id"]
|
|
185
|
+
) {
|
|
186
|
+
const copy = [...rows]
|
|
187
|
+
switch (sortId) {
|
|
188
|
+
case "primary-asc":
|
|
189
|
+
return copy.sort((a, b) => a.primaryValue - b.primaryValue)
|
|
190
|
+
case "rate-desc":
|
|
191
|
+
return copy.sort((a, b) => b.ratePercent - a.ratePercent)
|
|
192
|
+
case "metric-four-desc":
|
|
193
|
+
return copy.sort((a, b) => b.metricFour - a.metricFour)
|
|
194
|
+
case "primary-desc":
|
|
195
|
+
default:
|
|
196
|
+
return copy.sort((a, b) => b.primaryValue - a.primaryValue)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export function PerformanceMetricsTable({
|
|
201
|
+
title = "Performance Table",
|
|
202
|
+
entityColumnLabel = "Entity",
|
|
203
|
+
primaryMetricColumnLabel = "Primary Goal",
|
|
204
|
+
rateColumnLabel = "Rate",
|
|
205
|
+
metricOneColumnLabel = "Metric One",
|
|
206
|
+
metricTwoColumnLabel = "Metric Two",
|
|
207
|
+
metricThreeColumnLabel = "Metric Three",
|
|
208
|
+
metricFourColumnLabel = "Metric Four",
|
|
209
|
+
viewOptions = ["By Entity"],
|
|
210
|
+
roleOptions = ["All", "Senior Coordinator", "Coordinator", "Junior Coordinator"],
|
|
211
|
+
sortOptions = DEFAULT_SORT_OPTIONS,
|
|
212
|
+
rows = DEFAULT_ROWS,
|
|
213
|
+
pageSize = 6,
|
|
214
|
+
searchPlaceholder = "Search rows...",
|
|
215
|
+
}: PerformanceMetricsTableProps) {
|
|
216
|
+
const [view, setView] = React.useState(viewOptions[0] ?? "By Entity")
|
|
217
|
+
const [sortId, setSortId] =
|
|
218
|
+
React.useState<PerformanceMetricsTableSortOption["id"]>(
|
|
219
|
+
sortOptions[0]?.id ?? "primary-desc"
|
|
220
|
+
)
|
|
221
|
+
const [roleFilter, setRoleFilter] = React.useState(roleOptions[0] ?? "All")
|
|
222
|
+
const [search, setSearch] = React.useState("")
|
|
223
|
+
const [page, setPage] = React.useState(1)
|
|
224
|
+
|
|
225
|
+
const filteredRows = React.useMemo(() => {
|
|
226
|
+
const normalized = search.trim().toLowerCase()
|
|
227
|
+
return rows.filter((row) => {
|
|
228
|
+
if (roleFilter !== "All" && row.role !== roleFilter) {
|
|
229
|
+
return false
|
|
230
|
+
}
|
|
231
|
+
if (!normalized) {
|
|
232
|
+
return true
|
|
233
|
+
}
|
|
234
|
+
return row.label.toLowerCase().includes(normalized)
|
|
235
|
+
})
|
|
236
|
+
}, [roleFilter, rows, search])
|
|
237
|
+
|
|
238
|
+
const sortedRows = React.useMemo(
|
|
239
|
+
() => sortRows(filteredRows, sortId),
|
|
240
|
+
[filteredRows, sortId]
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
const pageCount = Math.max(1, Math.ceil(sortedRows.length / pageSize))
|
|
244
|
+
const start = (page - 1) * pageSize
|
|
245
|
+
const paginatedRows = sortedRows.slice(start, start + pageSize)
|
|
246
|
+
|
|
247
|
+
React.useEffect(() => {
|
|
248
|
+
setPage(1)
|
|
249
|
+
}, [search, roleFilter, sortId, view])
|
|
250
|
+
|
|
251
|
+
React.useEffect(() => {
|
|
252
|
+
setPage((previous) => Math.min(previous, pageCount))
|
|
253
|
+
}, [pageCount])
|
|
254
|
+
|
|
255
|
+
const sortLabel =
|
|
256
|
+
sortOptions.find((option) => option.id === sortId)?.label ?? "Sort"
|
|
257
|
+
|
|
258
|
+
return (
|
|
259
|
+
<div className="overflow-hidden rounded-xl border border-border bg-card">
|
|
260
|
+
<div className="flex items-center justify-between border-b border-border px-4 py-3">
|
|
261
|
+
<h3 className="text-sm font-semibold text-foreground">{title}</h3>
|
|
262
|
+
<div className="flex items-center gap-2">
|
|
263
|
+
<DropdownMenu>
|
|
264
|
+
<DropdownMenuTrigger asChild>
|
|
265
|
+
<Button variant="outline" size="sm" className="h-8 text-xs">
|
|
266
|
+
{view}
|
|
267
|
+
<ChevronDown className="ml-2 h-3.5 w-3.5" />
|
|
268
|
+
</Button>
|
|
269
|
+
</DropdownMenuTrigger>
|
|
270
|
+
<DropdownMenuContent>
|
|
271
|
+
{viewOptions.map((option) => (
|
|
272
|
+
<DropdownMenuItem key={option} onClick={() => setView(option)}>
|
|
273
|
+
{option}
|
|
274
|
+
</DropdownMenuItem>
|
|
275
|
+
))}
|
|
276
|
+
</DropdownMenuContent>
|
|
277
|
+
</DropdownMenu>
|
|
278
|
+
|
|
279
|
+
<DropdownMenu>
|
|
280
|
+
<DropdownMenuTrigger asChild>
|
|
281
|
+
<Button variant="outline" size="sm" className="h-8 text-xs">
|
|
282
|
+
Sort: {sortLabel.replace(/\s*\(.+\)/, "")}
|
|
283
|
+
<ChevronDown className="ml-2 h-3.5 w-3.5" />
|
|
284
|
+
</Button>
|
|
285
|
+
</DropdownMenuTrigger>
|
|
286
|
+
<DropdownMenuContent align="end" className="w-56">
|
|
287
|
+
{sortOptions.map((option) => (
|
|
288
|
+
<DropdownMenuItem key={option.id} onClick={() => setSortId(option.id)}>
|
|
289
|
+
{option.label}
|
|
290
|
+
</DropdownMenuItem>
|
|
291
|
+
))}
|
|
292
|
+
</DropdownMenuContent>
|
|
293
|
+
</DropdownMenu>
|
|
294
|
+
|
|
295
|
+
<DropdownMenu>
|
|
296
|
+
<DropdownMenuTrigger asChild>
|
|
297
|
+
<Button variant="outline" size="sm" className="h-8 text-xs">
|
|
298
|
+
Role: {roleFilter}
|
|
299
|
+
<ChevronDown className="ml-2 h-3.5 w-3.5" />
|
|
300
|
+
</Button>
|
|
301
|
+
</DropdownMenuTrigger>
|
|
302
|
+
<DropdownMenuContent align="end">
|
|
303
|
+
{roleOptions.map((option) => (
|
|
304
|
+
<DropdownMenuItem key={option} onClick={() => setRoleFilter(option)}>
|
|
305
|
+
{option}
|
|
306
|
+
</DropdownMenuItem>
|
|
307
|
+
))}
|
|
308
|
+
</DropdownMenuContent>
|
|
309
|
+
</DropdownMenu>
|
|
310
|
+
|
|
311
|
+
<Input
|
|
312
|
+
value={search}
|
|
313
|
+
onChange={(event) => setSearch(event.target.value)}
|
|
314
|
+
placeholder={searchPlaceholder}
|
|
315
|
+
className="h-8 w-48 text-xs"
|
|
316
|
+
/>
|
|
317
|
+
</div>
|
|
318
|
+
</div>
|
|
319
|
+
|
|
320
|
+
<ScrollArea>
|
|
321
|
+
<div className="min-w-[1180px]">
|
|
322
|
+
<Table>
|
|
323
|
+
<TableHeader className="bg-muted/30">
|
|
324
|
+
<TableRow className="hover:bg-transparent">
|
|
325
|
+
<TableHead className="px-4 py-3 text-xs font-semibold uppercase tracking-wider text-muted-foreground">
|
|
326
|
+
{entityColumnLabel}
|
|
327
|
+
</TableHead>
|
|
328
|
+
<TableHead className="w-[260px] px-4 py-3 text-xs font-semibold uppercase tracking-wider text-muted-foreground">
|
|
329
|
+
{primaryMetricColumnLabel}
|
|
330
|
+
</TableHead>
|
|
331
|
+
<TableHead className="px-4 py-3 text-right text-xs font-semibold uppercase tracking-wider text-muted-foreground">
|
|
332
|
+
{rateColumnLabel}
|
|
333
|
+
</TableHead>
|
|
334
|
+
<TableHead className="px-4 py-3 text-right text-xs font-semibold uppercase tracking-wider text-muted-foreground">
|
|
335
|
+
{metricOneColumnLabel}
|
|
336
|
+
</TableHead>
|
|
337
|
+
<TableHead className="px-4 py-3 text-right text-xs font-semibold uppercase tracking-wider text-muted-foreground">
|
|
338
|
+
{metricTwoColumnLabel}
|
|
339
|
+
</TableHead>
|
|
340
|
+
<TableHead className="px-4 py-3 text-right text-xs font-semibold uppercase tracking-wider text-muted-foreground">
|
|
341
|
+
{metricThreeColumnLabel}
|
|
342
|
+
</TableHead>
|
|
343
|
+
<TableHead className="px-4 py-3 text-right text-xs font-semibold uppercase tracking-wider text-muted-foreground">
|
|
344
|
+
{metricFourColumnLabel}
|
|
345
|
+
</TableHead>
|
|
346
|
+
</TableRow>
|
|
347
|
+
</TableHeader>
|
|
348
|
+
<TableBody>
|
|
349
|
+
{paginatedRows.map((row) => {
|
|
350
|
+
const percentage = (row.primaryValue / row.primaryTarget) * 100
|
|
351
|
+
const progress = getProgressStatus(row.primaryValue, row.primaryTarget)
|
|
352
|
+
|
|
353
|
+
return (
|
|
354
|
+
<TableRow key={row.id} className="hover:bg-muted/30">
|
|
355
|
+
<TableCell className="px-4 py-3">
|
|
356
|
+
<div className="flex items-center gap-3">
|
|
357
|
+
<Avatar className="h-8 w-8 border border-border">
|
|
358
|
+
<AvatarFallback className="bg-emerald-100 text-[11px] font-medium text-emerald-700">
|
|
359
|
+
{row.avatarFallback}
|
|
360
|
+
</AvatarFallback>
|
|
361
|
+
</Avatar>
|
|
362
|
+
<span className="text-sm font-medium text-foreground">{row.label}</span>
|
|
363
|
+
</div>
|
|
364
|
+
</TableCell>
|
|
365
|
+
|
|
366
|
+
<TableCell className="px-4 py-3">
|
|
367
|
+
<div className="flex items-center gap-2">
|
|
368
|
+
<span className="shrink-0">{progress.icon}</span>
|
|
369
|
+
<div className="w-full max-w-[180px]">
|
|
370
|
+
<div className="mb-1 text-sm font-bold text-foreground">
|
|
371
|
+
{row.primaryValue}/{row.primaryTarget}
|
|
372
|
+
</div>
|
|
373
|
+
<div className="h-1.5 w-full overflow-hidden rounded-full bg-muted">
|
|
374
|
+
<div
|
|
375
|
+
className={cn("h-full rounded-full", progress.color)}
|
|
376
|
+
style={{ width: `${Math.min(100, percentage)}%` }}
|
|
377
|
+
/>
|
|
378
|
+
</div>
|
|
379
|
+
<div className={cn("mt-1 text-xs font-medium", progress.textColor)}>
|
|
380
|
+
{Math.round(percentage)}%
|
|
381
|
+
</div>
|
|
382
|
+
</div>
|
|
383
|
+
</div>
|
|
384
|
+
</TableCell>
|
|
385
|
+
|
|
386
|
+
<TableCell className="px-4 py-3 text-right text-sm font-semibold text-emerald-600">
|
|
387
|
+
{row.ratePercent}%
|
|
388
|
+
</TableCell>
|
|
389
|
+
<TableCell className="px-4 py-3 text-right text-sm font-medium text-foreground">
|
|
390
|
+
{row.metricOne}
|
|
391
|
+
</TableCell>
|
|
392
|
+
<TableCell className="px-4 py-3 text-right text-sm text-muted-foreground">
|
|
393
|
+
{row.metricTwo}
|
|
394
|
+
</TableCell>
|
|
395
|
+
<TableCell className="px-4 py-3 text-right text-sm font-medium text-foreground">
|
|
396
|
+
{row.metricThree}
|
|
397
|
+
</TableCell>
|
|
398
|
+
<TableCell className="px-4 py-3 text-right text-sm font-medium text-foreground">
|
|
399
|
+
{row.metricFour}
|
|
400
|
+
</TableCell>
|
|
401
|
+
</TableRow>
|
|
402
|
+
)
|
|
403
|
+
})}
|
|
404
|
+
</TableBody>
|
|
405
|
+
</Table>
|
|
406
|
+
</div>
|
|
407
|
+
<ScrollBar orientation="horizontal" />
|
|
408
|
+
</ScrollArea>
|
|
409
|
+
|
|
410
|
+
<div className="flex items-center justify-between border-t border-border bg-muted/20 px-4 py-3">
|
|
411
|
+
<span className="text-xs text-muted-foreground">
|
|
412
|
+
Showing {sortedRows.length === 0 ? 0 : start + 1} to{" "}
|
|
413
|
+
{Math.min(start + pageSize, sortedRows.length)} of {sortedRows.length} rows
|
|
414
|
+
</span>
|
|
415
|
+
<div className="flex items-center gap-2">
|
|
416
|
+
<Button
|
|
417
|
+
variant="outline"
|
|
418
|
+
size="sm"
|
|
419
|
+
className="h-7 text-xs"
|
|
420
|
+
disabled={page <= 1}
|
|
421
|
+
onClick={() => setPage((previous) => Math.max(previous - 1, 1))}
|
|
422
|
+
>
|
|
423
|
+
<ChevronLeft className="mr-1 h-3.5 w-3.5" />
|
|
424
|
+
Previous
|
|
425
|
+
</Button>
|
|
426
|
+
<Button
|
|
427
|
+
variant="outline"
|
|
428
|
+
size="sm"
|
|
429
|
+
className="h-7 text-xs"
|
|
430
|
+
disabled={page >= pageCount}
|
|
431
|
+
onClick={() =>
|
|
432
|
+
setPage((previous) => Math.min(previous + 1, pageCount))
|
|
433
|
+
}
|
|
434
|
+
>
|
|
435
|
+
Next
|
|
436
|
+
<ChevronRight className="ml-1 h-3.5 w-3.5" />
|
|
437
|
+
</Button>
|
|
438
|
+
</div>
|
|
439
|
+
</div>
|
|
440
|
+
</div>
|
|
441
|
+
)
|
|
442
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cn } from "../lib/utils"
|
|
3
|
+
|
|
4
|
+
export function PreviewList({
|
|
5
|
+
children,
|
|
6
|
+
className,
|
|
7
|
+
...props
|
|
8
|
+
}: React.HTMLAttributes<HTMLDivElement>) {
|
|
9
|
+
const items = React.Children.toArray(children)
|
|
10
|
+
const visibleItems = items.slice(0, 8)
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<div className={cn("flex flex-col", className)} {...props}>
|
|
14
|
+
{visibleItems}
|
|
15
|
+
</div>
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface PreviewListItemProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {
|
|
20
|
+
icon?: React.ReactNode
|
|
21
|
+
title: React.ReactNode
|
|
22
|
+
subtitle?: React.ReactNode
|
|
23
|
+
meta?: React.ReactNode // e.g. time, badges on the right
|
|
24
|
+
action?: React.ReactNode // hover action like a "Join" button
|
|
25
|
+
isHoverable?: boolean
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const PreviewListItem = React.forwardRef<HTMLDivElement, PreviewListItemProps>(
|
|
29
|
+
({ className, icon, title, subtitle, meta, action, isHoverable = true, ...props }, ref) => {
|
|
30
|
+
return (
|
|
31
|
+
<div
|
|
32
|
+
ref={ref}
|
|
33
|
+
className={cn(
|
|
34
|
+
"flex items-center justify-between group relative bg-background transition-colors",
|
|
35
|
+
"px-6 h-16 border-b border-border last:border-0",
|
|
36
|
+
isHoverable && "hover:bg-muted/30 cursor-pointer",
|
|
37
|
+
className
|
|
38
|
+
)}
|
|
39
|
+
{...props}
|
|
40
|
+
>
|
|
41
|
+
<div className="flex items-center gap-3 overflow-hidden min-w-0 h-full py-2">
|
|
42
|
+
{icon && <div className="shrink-0 flex items-center justify-center">{icon}</div>}
|
|
43
|
+
<div className="min-w-0 flex flex-col justify-center gap-1">
|
|
44
|
+
<div className="text-sm font-semibold text-foreground truncate leading-snug">{title}</div>
|
|
45
|
+
{subtitle && <div className="text-xs text-muted-foreground truncate leading-snug">{subtitle}</div>}
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<div className="flex items-center gap-4 shrink-0 pl-4">
|
|
50
|
+
{meta && <div className={cn("flex items-center gap-2", action && "group-hover:opacity-0 transition-opacity")}>{meta}</div>}
|
|
51
|
+
|
|
52
|
+
{action && (
|
|
53
|
+
<div className="absolute right-4 opacity-0 group-hover:opacity-100 transition-opacity flex items-center bg-background/95 backdrop-blur-sm pl-2">
|
|
54
|
+
{action}
|
|
55
|
+
</div>
|
|
56
|
+
)}
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
)
|
|
62
|
+
PreviewListItem.displayName = "PreviewListItem"
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Progress as ProgressPrimitive } from "radix-ui"
|
|
5
|
+
|
|
6
|
+
import { cn } from "../lib/utils"
|
|
7
|
+
|
|
8
|
+
function Progress({
|
|
9
|
+
className,
|
|
10
|
+
value,
|
|
11
|
+
...props
|
|
12
|
+
}: React.ComponentProps<typeof ProgressPrimitive.Root>) {
|
|
13
|
+
return (
|
|
14
|
+
<ProgressPrimitive.Root
|
|
15
|
+
data-slot="progress"
|
|
16
|
+
className={cn(
|
|
17
|
+
"bg-primary/20 relative h-2 w-full overflow-hidden rounded-full",
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
{...props}
|
|
21
|
+
>
|
|
22
|
+
<ProgressPrimitive.Indicator
|
|
23
|
+
data-slot="progress-indicator"
|
|
24
|
+
className="bg-primary h-full w-full flex-1 transition-all"
|
|
25
|
+
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
|
|
26
|
+
/>
|
|
27
|
+
</ProgressPrimitive.Root>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export { Progress }
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { ArrowUp, Paperclip } from "lucide-react"
|
|
5
|
+
|
|
6
|
+
import { cn } from "../lib/utils"
|
|
7
|
+
import { Button } from "./button"
|
|
8
|
+
|
|
9
|
+
export type QuickActionPriority = "normal" | "high" | "urgent"
|
|
10
|
+
|
|
11
|
+
export interface QuickActionSubmitPayload {
|
|
12
|
+
message: string
|
|
13
|
+
priority: QuickActionPriority
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface QuickActionChatAreaProps
|
|
17
|
+
extends Omit<React.ComponentProps<"div">, "onSubmit"> {
|
|
18
|
+
placeholder?: string
|
|
19
|
+
submitLabel?: string
|
|
20
|
+
value?: string
|
|
21
|
+
defaultValue?: string
|
|
22
|
+
onValueChange?: (value: string) => void
|
|
23
|
+
priority?: QuickActionPriority
|
|
24
|
+
defaultPriority?: QuickActionPriority
|
|
25
|
+
onPriorityChange?: (priority: QuickActionPriority) => void
|
|
26
|
+
onSubmit: (payload: QuickActionSubmitPayload) => void
|
|
27
|
+
allowEmptySubmit?: boolean
|
|
28
|
+
clearOnSubmit?: boolean
|
|
29
|
+
showEnterHint?: boolean
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function QuickActionChatArea({
|
|
33
|
+
className,
|
|
34
|
+
placeholder = "How can I help you today?",
|
|
35
|
+
submitLabel = "Create Task",
|
|
36
|
+
value,
|
|
37
|
+
defaultValue = "",
|
|
38
|
+
onValueChange,
|
|
39
|
+
priority,
|
|
40
|
+
defaultPriority = "normal",
|
|
41
|
+
onPriorityChange,
|
|
42
|
+
onSubmit,
|
|
43
|
+
allowEmptySubmit = false,
|
|
44
|
+
clearOnSubmit = true,
|
|
45
|
+
showEnterHint = true,
|
|
46
|
+
...props
|
|
47
|
+
}: QuickActionChatAreaProps) {
|
|
48
|
+
const [internalValue, setInternalValue] = React.useState(defaultValue)
|
|
49
|
+
const [internalPriority, setInternalPriority] =
|
|
50
|
+
React.useState<QuickActionPriority>(defaultPriority)
|
|
51
|
+
|
|
52
|
+
const composerValue = value ?? internalValue
|
|
53
|
+
const selectedPriority = priority ?? internalPriority
|
|
54
|
+
|
|
55
|
+
const setComposerValue = React.useCallback(
|
|
56
|
+
(nextValue: string) => {
|
|
57
|
+
if (value === undefined) {
|
|
58
|
+
setInternalValue(nextValue)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
onValueChange?.(nextValue)
|
|
62
|
+
},
|
|
63
|
+
[onValueChange, value]
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
const setPriorityValue = React.useCallback(
|
|
67
|
+
(nextPriority: QuickActionPriority) => {
|
|
68
|
+
if (priority === undefined) {
|
|
69
|
+
setInternalPriority(nextPriority)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
onPriorityChange?.(nextPriority)
|
|
73
|
+
},
|
|
74
|
+
[onPriorityChange, priority]
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
const canSubmit = allowEmptySubmit || composerValue.trim().length > 0
|
|
78
|
+
|
|
79
|
+
const handleSubmit = React.useCallback(() => {
|
|
80
|
+
if (!canSubmit) {
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
onSubmit({
|
|
85
|
+
message: composerValue,
|
|
86
|
+
priority: selectedPriority,
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
if (clearOnSubmit) {
|
|
90
|
+
setComposerValue("")
|
|
91
|
+
}
|
|
92
|
+
}, [canSubmit, clearOnSubmit, composerValue, onSubmit, selectedPriority, setComposerValue])
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<div
|
|
96
|
+
className={cn("border-t border-border bg-background p-4", className)}
|
|
97
|
+
{...props}
|
|
98
|
+
>
|
|
99
|
+
<div className="mx-auto w-full max-w-2xl overflow-hidden rounded-md border border-border bg-background shadow-sm transition-all focus-within:border-[#1B4332] focus-within:ring-2 focus-within:ring-[#1B4332]/20">
|
|
100
|
+
<textarea
|
|
101
|
+
value={composerValue}
|
|
102
|
+
onChange={(event) => setComposerValue(event.target.value)}
|
|
103
|
+
placeholder={placeholder}
|
|
104
|
+
className="min-h-[80px] w-full resize-none p-4 text-foreground outline-none placeholder:text-muted-foreground"
|
|
105
|
+
onKeyDown={(event) => {
|
|
106
|
+
if (event.key === "Enter" && !event.shiftKey) {
|
|
107
|
+
event.preventDefault()
|
|
108
|
+
handleSubmit()
|
|
109
|
+
}
|
|
110
|
+
}}
|
|
111
|
+
/>
|
|
112
|
+
<div className="flex items-center justify-between border-t border-border bg-muted px-3 py-2">
|
|
113
|
+
<div className="flex items-center gap-2">
|
|
114
|
+
<button
|
|
115
|
+
type="button"
|
|
116
|
+
className="rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
|
|
117
|
+
title="Attach file"
|
|
118
|
+
aria-label="Attach file"
|
|
119
|
+
>
|
|
120
|
+
<Paperclip className="h-4 w-4" />
|
|
121
|
+
</button>
|
|
122
|
+
<div className="mx-1 h-4 w-px bg-muted" />
|
|
123
|
+
<select
|
|
124
|
+
value={selectedPriority}
|
|
125
|
+
onChange={(event) =>
|
|
126
|
+
setPriorityValue(event.target.value as QuickActionPriority)
|
|
127
|
+
}
|
|
128
|
+
className="cursor-pointer rounded-md bg-transparent px-2 py-1 text-xs font-medium text-muted-foreground transition-colors hover:bg-muted"
|
|
129
|
+
aria-label="Task priority"
|
|
130
|
+
>
|
|
131
|
+
<option value="normal">Normal</option>
|
|
132
|
+
<option value="high">High</option>
|
|
133
|
+
<option value="urgent">Urgent</option>
|
|
134
|
+
</select>
|
|
135
|
+
</div>
|
|
136
|
+
<div className="flex items-center gap-3">
|
|
137
|
+
{showEnterHint ? (
|
|
138
|
+
<span className="hidden text-xs text-muted-foreground sm:inline">
|
|
139
|
+
Press ⏎ to create
|
|
140
|
+
</span>
|
|
141
|
+
) : null}
|
|
142
|
+
<Button
|
|
143
|
+
size="sm"
|
|
144
|
+
onClick={handleSubmit}
|
|
145
|
+
disabled={!canSubmit}
|
|
146
|
+
className="gap-2 rounded-md bg-[#1B4332] text-white hover:bg-[#235742] disabled:cursor-not-allowed disabled:opacity-50"
|
|
147
|
+
>
|
|
148
|
+
{submitLabel}
|
|
149
|
+
<ArrowUp className="h-4 w-4" />
|
|
150
|
+
</Button>
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
)
|
|
156
|
+
}
|