@handled-ai/design-system 0.17.1 → 0.18.1
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/dist/charts/empty-chart-state.d.ts +11 -0
- package/dist/charts/empty-chart-state.js +70 -0
- package/dist/charts/empty-chart-state.js.map +1 -0
- package/dist/charts/index.d.ts +1 -0
- package/dist/charts/index.js +1 -0
- package/dist/charts/index.js.map +1 -1
- package/dist/charts/pipeline-overview.d.ts +2 -1
- package/dist/charts/pipeline-overview.js +29 -1
- package/dist/charts/pipeline-overview.js.map +1 -1
- package/dist/components/actor-byline.d.ts +3 -0
- package/dist/components/actor-byline.js +5 -0
- package/dist/components/actor-byline.js.map +1 -0
- package/dist/components/days-open-cell.d.ts +16 -0
- package/dist/components/days-open-cell.js +73 -0
- package/dist/components/days-open-cell.js.map +1 -0
- package/dist/components/detail-drawer.d.ts +16 -0
- package/dist/components/detail-drawer.js +45 -0
- package/dist/components/detail-drawer.js.map +1 -0
- package/dist/components/feedback-primitives.d.ts +66 -0
- package/dist/components/feedback-primitives.js +295 -0
- package/dist/components/feedback-primitives.js.map +1 -0
- package/dist/components/insights-filter-bar.d.ts +2 -1
- package/dist/components/insights-filter-bar.js +13 -5
- package/dist/components/insights-filter-bar.js.map +1 -1
- package/dist/components/linked-entity-cell.d.ts +14 -0
- package/dist/components/linked-entity-cell.js +96 -0
- package/dist/components/linked-entity-cell.js.map +1 -0
- package/dist/components/metric-card.d.ts +14 -1
- package/dist/components/metric-card.js +86 -0
- package/dist/components/metric-card.js.map +1 -1
- package/dist/components/performance-metrics-table.d.ts +2 -1
- package/dist/components/performance-metrics-table.js +78 -46
- package/dist/components/performance-metrics-table.js.map +1 -1
- package/dist/components/pill.d.ts +26 -0
- package/dist/components/pill.js +77 -0
- package/dist/components/pill.js.map +1 -0
- package/dist/components/quick-segment.d.ts +13 -0
- package/dist/components/quick-segment.js +96 -0
- package/dist/components/quick-segment.js.map +1 -0
- package/dist/components/score-why-chips.d.ts +8 -17
- package/dist/components/score-why-chips.js +266 -180
- package/dist/components/score-why-chips.js.map +1 -1
- package/dist/components/signal-priority-popover.d.ts +17 -0
- package/dist/components/signal-priority-popover.js +247 -0
- package/dist/components/signal-priority-popover.js.map +1 -0
- package/dist/components/user-display.d.ts +22 -0
- package/dist/components/user-display.js +138 -0
- package/dist/components/user-display.js.map +1 -0
- package/dist/components/user-pill.d.ts +3 -0
- package/dist/components/user-pill.js +5 -0
- package/dist/components/user-pill.js.map +1 -0
- package/dist/index.d.ts +13 -4
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/user-display.d.ts +31 -0
- package/dist/lib/user-display.js +57 -0
- package/dist/lib/user-display.js.map +1 -0
- package/dist/prototype/index.d.ts +2 -1
- package/dist/prototype/prototype-accounts-view.d.ts +2 -1
- package/dist/prototype/prototype-admin-view.d.ts +2 -1
- package/dist/prototype/prototype-config.d.ts +15 -332
- package/dist/prototype/prototype-inbox-view.d.ts +2 -1
- package/dist/prototype/prototype-inbox-view.js +11 -12
- package/dist/prototype/prototype-inbox-view.js.map +1 -1
- package/dist/prototype/prototype-insights-view.d.ts +2 -1
- package/dist/prototype/prototype-shell.d.ts +2 -1
- package/dist/signal-priority-popover-DQ_VuHac.d.ts +390 -0
- package/package.json +1 -1
- package/src/charts/__tests__/insights-charts.test.tsx +62 -0
- package/src/charts/empty-chart-state.tsx +44 -0
- package/src/charts/index.ts +1 -0
- package/src/charts/pipeline-overview.tsx +38 -1
- package/src/components/__tests__/contextual-quick-action-launcher.test.tsx +99 -188
- package/src/components/__tests__/feedback-primitives.test.tsx +509 -0
- package/src/components/__tests__/insights-primitives.test.tsx +117 -0
- package/src/components/__tests__/performance-metrics-table.test.tsx +54 -0
- package/src/components/__tests__/score-why-chips.test.tsx +540 -0
- package/src/components/__tests__/signal-priority-popover.test.tsx +312 -0
- package/src/components/__tests__/user-display.test.tsx +75 -0
- package/src/components/actor-byline.tsx +1 -0
- package/src/components/days-open-cell.tsx +50 -0
- package/src/components/detail-drawer.tsx +60 -0
- package/src/components/feedback-primitives.tsx +424 -0
- package/src/components/insights-filter-bar.tsx +13 -4
- package/src/components/linked-entity-cell.tsx +74 -0
- package/src/components/metric-card.tsx +82 -0
- package/src/components/performance-metrics-table.tsx +99 -63
- package/src/components/pill.tsx +67 -0
- package/src/components/quick-segment.tsx +68 -0
- package/src/components/score-why-chips.tsx +413 -203
- package/src/components/signal-priority-popover.tsx +359 -0
- package/src/components/user-display.tsx +96 -0
- package/src/components/user-pill.tsx +1 -0
- package/src/index.ts +11 -0
- package/src/lib/__tests__/user-display.test.ts +85 -0
- package/src/lib/user-display.ts +88 -0
- package/src/prototype/__tests__/detail-view-score-why.test.tsx +33 -29
- package/src/prototype/__tests__/detail-view-title-slots.test.tsx +65 -0
- package/src/prototype/prototype-config.ts +28 -4
- package/src/prototype/prototype-inbox-view.tsx +8 -10
- package/src/prototype/__tests__/detail-view-title-subtext.test.tsx +0 -72
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/score-why-chips.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { Info } from \"lucide-react\"\nimport { ScoreBreakdown, type ScoreFactor } from \"./score-breakdown\"\nimport { cn } from \"../lib/utils\"\nimport type {\n QueueItem,\n SignalScoreData,\n SignalScoreExplanationBucket,\n SignalScoreExplanationSignal,\n SignalScoreUrgencyLabel,\n} from \"../prototype/prototype-config\"\n\nexport function getSignalScoreUrgencyLabel(\n score: number,\n providedLabel?: SignalScoreUrgencyLabel,\n): SignalScoreUrgencyLabel {\n if (providedLabel) return providedLabel\n if (score >= 80) return \"Urgent\"\n if (score >= 60) return \"High\"\n if (score >= 35) return \"Medium\"\n return \"Low\"\n}\n\nfunction getUrgencyChipClass(label: SignalScoreUrgencyLabel) {\n switch (label) {\n case \"Urgent\":\n return \"border-red-200 bg-red-50 text-red-700 hover:bg-red-100 dark:border-red-900/50 dark:bg-red-950/30 dark:text-red-300\"\n case \"High\":\n return \"border-orange-200 bg-orange-50 text-orange-700 hover:bg-orange-100 dark:border-orange-900/50 dark:bg-orange-950/30 dark:text-orange-300\"\n case \"Medium\":\n return \"border-amber-200 bg-amber-50 text-amber-700 hover:bg-amber-100 dark:border-amber-900/50 dark:bg-amber-950/30 dark:text-amber-300\"\n case \"Low\":\n return \"border-emerald-200 bg-emerald-50 text-emerald-700 hover:bg-emerald-100 dark:border-emerald-900/50 dark:bg-emerald-950/30 dark:text-emerald-300\"\n }\n}\n\nfunction classificationForScore(score?: number): string | undefined {\n if (score == null) return undefined\n if (score >= 80) return \"Urgent\"\n if (score >= 60) return \"High\"\n if (score >= 35) return \"Medium\"\n return \"Low\"\n}\n\nfunction scoreRangeForUrgency(label: SignalScoreUrgencyLabel): string {\n switch (label) {\n case \"Urgent\":\n return \"80-100\"\n case \"High\":\n return \"60-79\"\n case \"Medium\":\n return \"35-59\"\n case \"Low\":\n return \"0-34\"\n }\n}\n\nfunction makeDomId(...parts: Array<string | undefined>): string {\n return parts\n .filter((part): part is string => Boolean(part))\n .join(\"-\")\n .replace(/[^A-Za-z0-9_-]+/g, \"-\")\n}\n\nfunction scoreFactorToPriorityBucket(factor: ScoreFactor): SignalScoreExplanationBucket {\n return {\n key: factor.key,\n label: factor.label,\n kind: \"factor\",\n score: factor.score ?? undefined,\n classification: factor.risk ?? classificationForScore(factor.score ?? undefined),\n rationale: factor.why,\n factorKeys: [factor.key],\n }\n}\n\nfunction bucketHasSignalRows(bucket: SignalScoreExplanationBucket): boolean {\n return (\n (bucket.signals?.length ?? 0) > 0 ||\n (bucket.signalIds?.length ?? 0) > 0 ||\n Boolean(bucket.primarySignalId)\n )\n}\n\nfunction getSignalScoreBuckets(signalData: SignalScoreData): SignalScoreExplanationBucket[] {\n return (signalData.explanationBuckets ?? []).filter(\n (bucket) => bucket.kind !== \"factor\" && bucketHasSignalRows(bucket),\n )\n}\n\nfunction getBucketSignals(bucket: SignalScoreExplanationBucket): SignalScoreExplanationSignal[] {\n if (bucket.signals && bucket.signals.length > 0) return bucket.signals\n\n const signalIds = bucket.signalIds && bucket.signalIds.length > 0 ? bucket.signalIds : bucket.primarySignalId ? [bucket.primarySignalId] : []\n const uniqueSignalIds = Array.from(new Set(signalIds))\n\n return uniqueSignalIds.map((signalId) => ({\n id: signalId,\n label: `${bucket.label} signal`,\n }))\n}\n\nfunction getPriorityBuckets(signalData: SignalScoreData): SignalScoreExplanationBucket[] {\n if (signalData.explanationBuckets !== undefined) return signalData.explanationBuckets\n // Legacy fallback for consumers that still provide score factors but have not\n // migrated to explanation buckets. WHY chips intentionally do not use this.\n return signalData.factors.map(scoreFactorToPriorityBucket)\n}\n\nfunction isSameExplanation(a?: string, b?: string): boolean {\n return Boolean(a && b && a.trim() === b.trim())\n}\n\nexport interface SignalPriorityChipProps {\n score: number\n urgencyLabel?: SignalScoreUrgencyLabel\n isOpen?: boolean\n controlsId?: string\n onClick?: () => void\n className?: string\n}\n\nexport function SignalPriorityChip({\n score,\n urgencyLabel: providedLabel,\n isOpen,\n controlsId,\n onClick,\n className,\n}: SignalPriorityChipProps) {\n const urgencyLabel = getSignalScoreUrgencyLabel(score, providedLabel)\n\n return (\n <button\n type=\"button\"\n onClick={onClick}\n aria-expanded={isOpen}\n aria-controls={controlsId}\n className={cn(\n \"inline-flex items-center gap-1 rounded-md border px-2.5 py-1 text-xs font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n getUrgencyChipClass(urgencyLabel),\n className,\n )}\n >\n {urgencyLabel} Priority\n <Info className=\"h-3 w-3\" />\n </button>\n )\n}\n\nexport interface SignalPriorityPanelProps {\n signalData: SignalScoreData\n className?: string\n id?: string\n}\n\nexport function SignalPriorityPanel({ signalData, className, id }: SignalPriorityPanelProps) {\n const urgencyLabel = getSignalScoreUrgencyLabel(signalData.score, signalData.urgencyLabel)\n const buckets = React.useMemo(() => getPriorityBuckets(signalData), [signalData])\n const topBucketRationale = buckets.find((bucket) => bucket.rationale)?.rationale\n const primaryUrgencyExplanation = signalData.urgencyExplanation ?? signalData.whyNow ?? topBucketRationale\n const whyNowSection = isSameExplanation(signalData.whyNow, primaryUrgencyExplanation) ? undefined : signalData.whyNow\n const topFactorSection =\n isSameExplanation(topBucketRationale, primaryUrgencyExplanation) || isSameExplanation(topBucketRationale, whyNowSection)\n ? undefined\n : topBucketRationale\n const scoreRange = scoreRangeForUrgency(urgencyLabel)\n\n return (\n <div id={id} className={cn(\"rounded-lg border border-border bg-muted/20 p-3 text-xs\", className)} role=\"region\" aria-label=\"Priority explanation\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div>\n <p className=\"font-semibold text-foreground\">Why this is {urgencyLabel.toLowerCase()} priority</p>\n {primaryUrgencyExplanation ? <p className=\"mt-1 leading-relaxed text-muted-foreground\">{primaryUrgencyExplanation}</p> : null}\n </div>\n <div className=\"flex shrink-0 flex-wrap gap-1.5 text-[11px] text-muted-foreground\">\n <span className=\"rounded-full bg-background px-2 py-0.5\">Score {signalData.score}/100</span>\n <span className=\"rounded-full bg-background px-2 py-0.5\">{urgencyLabel} range: {scoreRange}</span>\n </div>\n </div>\n\n {(whyNowSection || topFactorSection) && (\n <div className=\"mt-3 grid gap-2 text-xs text-muted-foreground sm:grid-cols-2\">\n {whyNowSection ? (\n <div className=\"rounded-md bg-background/80 p-2\">\n <p className=\"font-medium text-foreground\">Why now</p>\n <p className=\"mt-0.5 leading-relaxed\">{whyNowSection}</p>\n </div>\n ) : null}\n {topFactorSection ? (\n <div className=\"rounded-md bg-background/80 p-2\">\n <p className=\"font-medium text-foreground\">Top factor</p>\n <p className=\"mt-0.5 leading-relaxed\">{topFactorSection}</p>\n </div>\n ) : null}\n </div>\n )}\n\n {signalData.factors.length > 0 ? (\n <ScoreBreakdown\n className=\"mt-3\"\n factors={signalData.factors}\n onFactorFeedback={signalData.onFactorFeedback}\n initialFeedback={signalData.initialFactorFeedback}\n />\n ) : null}\n </div>\n )\n}\n\nexport interface ScoreWhyChipsProps {\n item: QueueItem\n signalData: SignalScoreData\n onOpenSignalBucket?: (args: { item: QueueItem; bucketKey: string; signalId: string }) => void\n className?: string\n}\n\ninterface SignalRowProps {\n item: QueueItem\n bucketKey: string\n signal: SignalScoreExplanationSignal\n onOpenSignalBucket?: ScoreWhyChipsProps[\"onOpenSignalBucket\"]\n}\n\nfunction SignalRow({ item, bucketKey, signal, onOpenSignalBucket }: SignalRowProps) {\n const rowContent = (\n <>\n <div className=\"flex items-start justify-between gap-2\">\n <p className=\"font-medium text-foreground\">{signal.label}</p>\n {signal.time ? <span className=\"shrink-0 text-[11px] text-muted-foreground/70\">{signal.time}</span> : null}\n </div>\n {signal.description ? <p className=\"mt-1 leading-relaxed text-muted-foreground\">{signal.description}</p> : null}\n {(signal.source || signal.metric) && (\n <div className=\"mt-1.5 flex flex-wrap gap-1.5 text-[11px] text-muted-foreground/80\">\n {signal.source ? <span className=\"rounded-full bg-muted px-2 py-0.5\">{signal.source}</span> : null}\n {signal.metric ? <span className=\"rounded-full bg-muted px-2 py-0.5\">{signal.metric}</span> : null}\n </div>\n )}\n </>\n )\n\n if (signal.id && onOpenSignalBucket) {\n return (\n <button\n type=\"button\"\n className=\"w-full rounded-md bg-background/80 p-2 text-left text-xs transition-colors hover:bg-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n onClick={() => onOpenSignalBucket({ item, bucketKey, signalId: signal.id! })}\n >\n {rowContent}\n </button>\n )\n }\n\n return <div className=\"rounded-md bg-background/80 p-2 text-xs\">{rowContent}</div>\n}\n\nexport function ScoreWhyChips({\n item,\n signalData,\n onOpenSignalBucket,\n className,\n}: ScoreWhyChipsProps) {\n const [selectedBucketKey, setSelectedBucketKey] = React.useState<string | null>(null)\n\n React.useEffect(() => {\n setSelectedBucketKey(null)\n }, [item.id])\n\n const reactId = React.useId()\n const idPrefix = makeDomId(\"score-why\", reactId, item.id)\n const buckets = React.useMemo(() => getSignalScoreBuckets(signalData), [signalData])\n const selectedBucket = buckets.find((bucket) => bucket.key === selectedBucketKey) ?? null\n const selectedBucketSignals = selectedBucket ? getBucketSignals(selectedBucket) : []\n const selectedPanelId = selectedBucket ? `${idPrefix}-panel-${makeDomId(selectedBucket.key)}` : undefined\n\n if (buckets.length === 0) return null\n\n return (\n <div className={cn(\"mt-4\", className)}>\n <div className=\"mb-2 flex items-center gap-2\">\n <span className=\"text-[10px] font-bold uppercase tracking-wider text-muted-foreground\">Why</span>\n </div>\n <div className=\"flex flex-wrap gap-1.5\">\n {buckets.map((bucket) => {\n const isSelected = selectedBucketKey === bucket.key\n const panelId = `${idPrefix}-panel-${makeDomId(bucket.key)}`\n return (\n <button\n key={bucket.key}\n type=\"button\"\n onClick={() => setSelectedBucketKey((prev) => (prev === bucket.key ? null : bucket.key))}\n aria-expanded={isSelected}\n aria-controls={panelId}\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-[11px] font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n isSelected\n ? \"border-foreground/30 bg-foreground text-background\"\n : \"border-border bg-background text-muted-foreground hover:bg-muted/60 hover:text-foreground\",\n )}\n >\n {bucket.label}\n {bucket.signalCount && bucket.signalCount > 1 ? (\n <span className={cn(\"rounded-full px-1.5 py-0 text-[10px]\", isSelected ? \"bg-background/20\" : \"bg-muted\")}>×{bucket.signalCount}</span>\n ) : null}\n </button>\n )\n })}\n </div>\n\n {selectedBucket && (\n <div\n id={selectedPanelId}\n className=\"mt-3 rounded-lg border border-border bg-muted/20 p-3\"\n role=\"region\"\n aria-label={`${selectedBucket.label} details`}\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div>\n <p className=\"text-sm font-semibold text-foreground\">{selectedBucket.label}</p>\n <div className=\"mt-1 flex flex-wrap gap-1.5 text-[11px] text-muted-foreground\">\n <span className=\"rounded-full bg-background px-2 py-0.5\">\n {selectedBucket.signalCount ?? selectedBucketSignals.length} signals\n </span>\n </div>\n </div>\n </div>\n\n {selectedBucketSignals.length > 0 ? (\n <ul className=\"mt-3 space-y-2\" aria-label=\"Matching signals\">\n {selectedBucketSignals.map((signal, index) => (\n <li key={signal.id ?? `${selectedBucket.key}-signal-${index}`}>\n <SignalRow\n item={item}\n bucketKey={selectedBucket.key}\n signal={signal}\n onOpenSignalBucket={onOpenSignalBucket}\n />\n </li>\n ))}\n </ul>\n ) : selectedBucket.evidence && selectedBucket.evidence.length > 0 ? (\n <ul className=\"mt-3 space-y-1.5\" aria-label=\"Matching signals\">\n {selectedBucket.evidence.map((evidence, index) => (\n <li key={`${selectedBucket.key}-evidence-${index}`} className=\"flex gap-2 text-xs text-muted-foreground\">\n <span className=\"mt-1.5 h-1 w-1 shrink-0 rounded-full bg-primary\" />\n <span className=\"leading-relaxed\">{evidence}</span>\n </li>\n ))}\n </ul>\n ) : null}\n\n </div>\n )}\n </div>\n )\n}\n"],"mappings":"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","names":["_a"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/score-why-chips.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport {\n ChevronDown,\n ChevronUp,\n ChevronRight,\n X,\n TrendingDown,\n ArrowUpRight,\n Radar,\n ArrowDownLeft,\n GitMerge,\n Activity,\n} from \"lucide-react\"\nimport type { LucideIcon } from \"lucide-react\"\nimport { FeedbackFooter } from \"./feedback-primitives\"\nimport type { FeedbackChipTree, FeedbackSubmitData } from \"./feedback-primitives\"\nimport { cn } from \"../lib/utils\"\nimport type {\n QueueItem,\n SignalScoreData,\n SignalScoreExplanationBucket,\n SignalScoreExplanationSignal,\n SignalScoreUrgencyLabel,\n} from \"../prototype/prototype-config\"\n\n// ---------------------------------------------------------------------------\n// Constants & helpers\n// ---------------------------------------------------------------------------\n\nexport function getSignalScoreUrgencyLabel(\n score: number,\n providedLabel?: SignalScoreUrgencyLabel,\n): SignalScoreUrgencyLabel {\n if (providedLabel) return providedLabel\n if (score >= 80) return \"Urgent\"\n if (score >= 60) return \"High\"\n if (score >= 35) return \"Medium\"\n return \"Low\"\n}\n\nexport function scoreRangeForUrgency(label: SignalScoreUrgencyLabel): string {\n switch (label) {\n case \"Urgent\":\n return \"80-100\"\n case \"High\":\n return \"60-79\"\n case \"Medium\":\n return \"35-59\"\n case \"Low\":\n return \"0-34\"\n }\n}\n\nfunction makeDomId(...parts: Array<string | undefined>): string {\n return parts\n .filter((part): part is string => Boolean(part))\n .join(\"-\")\n .replace(/[^A-Za-z0-9_-]+/g, \"-\")\n}\n\nfunction bucketHasSignalRows(bucket: SignalScoreExplanationBucket): boolean {\n return (\n (bucket.signals?.length ?? 0) > 0 ||\n (bucket.signalIds?.length ?? 0) > 0 ||\n Boolean(bucket.primarySignalId)\n )\n}\n\nfunction getSignalScoreBuckets(signalData: SignalScoreData): SignalScoreExplanationBucket[] {\n return (signalData.explanationBuckets ?? []).filter(\n (bucket) => bucket.kind !== \"factor\" && bucketHasSignalRows(bucket),\n )\n}\n\nfunction getBucketSignals(bucket: SignalScoreExplanationBucket): SignalScoreExplanationSignal[] {\n if (bucket.signals && bucket.signals.length > 0) return bucket.signals\n\n const signalIds = bucket.signalIds && bucket.signalIds.length > 0 ? bucket.signalIds : bucket.primarySignalId ? [bucket.primarySignalId] : []\n const uniqueSignalIds = Array.from(new Set(signalIds))\n\n return uniqueSignalIds.map((signalId) => ({\n id: signalId,\n label: `${bucket.label} signal`,\n }))\n}\n\n// ---------------------------------------------------------------------------\n// Signal type icon map - keyed by signal type name (Tailwind v4 source scanned)\n// ---------------------------------------------------------------------------\n\nconst SIGNAL_TYPE_ICONS: Record<string, LucideIcon> = {\n treasury_liquidation: TrendingDown,\n cumulative_treasury_outflow: ArrowUpRight,\n test_transaction: Radar,\n micro_deposit: ArrowDownLeft,\n combined_signal: GitMerge,\n}\n\nfunction resolveIcon(iconName?: string): LucideIcon {\n if (!iconName) return Activity\n return SIGNAL_TYPE_ICONS[iconName] ?? Activity\n}\n\n// ---------------------------------------------------------------------------\n// Static tone class maps (REQUIRED for Tailwind v4 source scanning)\n// ---------------------------------------------------------------------------\n\n/** Shared tone-to-class map. Re-exported for signal-priority-popover. */\nexport const SIGNAL_TONE_CLASSES: Record<string, string> = {\n alert: \"bg-red-50 text-red-600\",\n warn: \"bg-amber-50 text-amber-600\",\n info: \"bg-blue-50 text-blue-600\",\n}\n\n/** Default tone for missing/unknown tone values */\nexport const DEFAULT_TONE_CLASS = \"bg-muted text-muted-foreground\"\n\n// ---------------------------------------------------------------------------\n// Em-dash fallback for missing slot data\n// ---------------------------------------------------------------------------\n\nfunction slotValue(value: string | null | undefined): string {\n return value && value.trim().length > 0 ? value : \"\"\n}\n\n// ---------------------------------------------------------------------------\n// Bucket feedback chip config\n// ---------------------------------------------------------------------------\n\nconst BUCKET_NEGATIVE_CHIPS: FeedbackChipTree[] = [\n {\n label: \"Not relevant for this account\",\n subPrompt: \"Why isn't it relevant?\",\n subChips: [\n \"Business as usual for this account\",\n \"Account in maintenance mode\",\n \"Wrong contact for this signal\",\n \"Other\",\n ],\n },\n { label: \"Bad timing\" },\n {\n label: \"Inaccurate data\",\n subPrompt: \"Which field?\",\n subChips: [\"Balance figures\", \"Counterparty\", \"Timestamp\", \"Other\"],\n },\n { label: \"Wrong account\" },\n { label: \"Already handled\" },\n { label: \"Other\" },\n]\n\n// ---------------------------------------------------------------------------\n// Default visible row count for long lists\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_VISIBLE_ROWS = 8\n\n// ---------------------------------------------------------------------------\n// WhyPill - Bucket toggle button with icon, count badge, chevron, close\n// ---------------------------------------------------------------------------\n\ninterface WhyPillProps {\n bucket: SignalScoreExplanationBucket\n isSelected: boolean\n signalCount: number\n panelId: string\n onToggle: () => void\n onClose: () => void\n}\n\nfunction WhyPill({ bucket, isSelected, signalCount, panelId, onToggle, onClose }: WhyPillProps) {\n const IconComponent = resolveIcon(bucket.icon)\n\n const sharedClasses = cn(\n \"inline-flex items-center text-[11px] font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n isSelected\n ? \"border-border bg-muted text-foreground\"\n : \"border-border bg-background text-muted-foreground hover:bg-muted/60 hover:text-foreground\",\n )\n\n return (\n <div className=\"inline-flex h-[26px] items-stretch\">\n <button\n type=\"button\"\n onClick={onToggle}\n aria-expanded={isSelected}\n aria-controls={panelId}\n className={cn(\n sharedClasses,\n \"gap-1.5 rounded-lg border px-2.5 py-1\",\n isSelected && \"rounded-b-none rounded-r-none border-r-0\",\n )}\n >\n <IconComponent className=\"h-3 w-3 shrink-0\" />\n {bucket.label}\n {signalCount > 1 && (\n <span className={cn(\"rounded-full px-1.5 py-0 text-[10px]\", isSelected ? \"bg-background/60\" : \"bg-muted\")}>\n x{signalCount}\n </span>\n )}\n {isSelected ? (\n <ChevronUp className=\"h-3 w-3 shrink-0\" />\n ) : (\n <ChevronDown className=\"h-3 w-3 shrink-0\" />\n )}\n </button>\n {isSelected && (\n <button\n type=\"button\"\n aria-label={`Close ${bucket.label}`}\n onClick={onClose}\n className={cn(\n sharedClasses,\n \"rounded-lg rounded-b-none rounded-l-none border border-l-0 border-border px-1.5 py-1 hover:bg-background/60\",\n )}\n >\n <X className=\"h-3 w-3\" />\n </button>\n )}\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// CombinedSignalMiniChips - renders component type chips for combined signals\n// ---------------------------------------------------------------------------\n\ninterface CombinedSignalMiniChipsProps {\n components: Array<{ type: string; count: number }>\n}\n\nfunction CombinedSignalMiniChips({ components }: CombinedSignalMiniChipsProps) {\n return (\n <div className=\"flex flex-wrap items-center gap-1\">\n {components.map((comp, idx) => {\n const CompIcon = resolveIcon(comp.type)\n return (\n <React.Fragment key={comp.type}>\n {idx > 0 && <span className=\"text-[10px] text-muted-foreground/60\">+</span>}\n <span className=\"inline-flex items-center gap-0.5 rounded bg-muted px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground\">\n <CompIcon className=\"h-2.5 w-2.5 shrink-0\" />\n {comp.type.replace(/_/g, \" \")} x{comp.count}\n </span>\n </React.Fragment>\n )\n })}\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// StructuredSignalRow - CSS grid slot grammar signal row\n// ---------------------------------------------------------------------------\n\ninterface StructuredSignalRowProps {\n item: QueueItem\n bucketKey: string\n signal: SignalScoreExplanationSignal\n tone?: \"alert\" | \"warn\" | \"info\"\n onOpenSignalBucket?: ScoreWhyChipsProps[\"onOpenSignalBucket\"]\n}\n\nfunction StructuredSignalRow({ item, bucketKey, signal, tone, onOpenSignalBucket }: StructuredSignalRowProps) {\n const IconComponent = resolveIcon(signal.signalTypeName)\n const toneClass = tone ? (SIGNAL_TONE_CLASSES[tone] ?? DEFAULT_TONE_CLASS) : DEFAULT_TONE_CLASS\n const isCombined = signal.signalTypeName === \"combined_signal\" && signal.components && signal.components.length > 0\n\n const rowContent = (\n <>\n {/* Slot 1: Icon */}\n <div className={cn(\"flex h-5 w-5 shrink-0 items-center justify-center rounded\", toneClass)}>\n <IconComponent className=\"h-3 w-3\" />\n </div>\n\n {/* Slot 2: Primary value + qualifier */}\n <div className=\"min-w-0\">\n {isCombined ? (\n <CombinedSignalMiniChips components={signal.components!} />\n ) : (\n <div className=\"flex items-baseline gap-1.5\">\n <span className=\"text-sm font-semibold tabular-nums text-foreground\">\n {slotValue(signal.primaryValue)}\n </span>\n <span className=\"text-xs text-muted-foreground\">\n {slotValue(signal.qualifier)}\n </span>\n </div>\n )}\n </div>\n\n {/* Slot 3: Counterparty */}\n <div className=\"min-w-0\">\n <span className=\"block truncate text-xs text-muted-foreground\">\n {slotValue(signal.counterparty)}\n </span>\n </div>\n\n {/* Slot 4: Time */}\n <span className=\"shrink-0 text-[11px] text-muted-foreground/70\">\n {slotValue(signal.time)}\n </span>\n\n {/* Slot 5: Chevron */}\n <ChevronRight className=\"h-3 w-3 shrink-0 text-muted-foreground/60 transition-transform group-hover:translate-x-0.5 group-hover:text-foreground/50\" />\n </>\n )\n\n if (signal.id && onOpenSignalBucket) {\n return (\n <button\n type=\"button\"\n className=\"group grid w-full cursor-pointer items-center gap-x-3 gap-y-1 rounded-md px-3 py-2 text-left transition-colors hover:bg-muted/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n style={{ gridTemplateColumns: \"20px minmax(0,1fr) minmax(0,1fr) auto 16px\" }}\n onClick={() => onOpenSignalBucket({ item, bucketKey, signalId: signal.id! })}\n >\n {rowContent}\n </button>\n )\n }\n\n return (\n <div\n className=\"grid w-full items-center gap-x-3 gap-y-1 rounded-md px-3 py-2\"\n style={{ gridTemplateColumns: \"20px minmax(0,1fr) minmax(0,1fr) auto 16px\" }}\n >\n {rowContent}\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Legacy SignalRow (for signals without structured data)\n// ---------------------------------------------------------------------------\n\ninterface SignalRowProps {\n item: QueueItem\n bucketKey: string\n signal: SignalScoreExplanationSignal\n onOpenSignalBucket?: ScoreWhyChipsProps[\"onOpenSignalBucket\"]\n}\n\nfunction LegacySignalRow({ item, bucketKey, signal, onOpenSignalBucket }: SignalRowProps) {\n const isClickable = !!(signal.id && onOpenSignalBucket)\n const rowContent = (\n <>\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0 flex-1\">\n <p className=\"font-medium text-foreground\">{signal.label}</p>\n {signal.description ? <p className=\"mt-1 leading-relaxed text-muted-foreground\">{signal.description}</p> : null}\n {(signal.source || signal.metric) && (\n <div className=\"mt-1.5 flex flex-wrap gap-1.5 text-[11px] text-muted-foreground/80\">\n {signal.source ? <span className=\"rounded-full bg-muted px-2 py-0.5\">{signal.source}</span> : null}\n {signal.metric ? <span className=\"rounded-full bg-muted px-2 py-0.5\">{signal.metric}</span> : null}\n </div>\n )}\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n {signal.time ? <span className=\"text-[11px] text-muted-foreground/70\">{signal.time}</span> : null}\n {isClickable && (\n <ChevronRight className=\"h-3 w-3 text-muted-foreground/60 transition-transform group-hover:translate-x-0.5 group-hover:text-foreground/50\" />\n )}\n </div>\n </div>\n </>\n )\n\n if (isClickable) {\n return (\n <button\n type=\"button\"\n className=\"group w-full cursor-pointer rounded-md bg-background/80 p-3 text-left text-xs transition-colors hover:bg-muted/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n onClick={() => onOpenSignalBucket!({ item, bucketKey, signalId: signal.id! })}\n >\n {rowContent}\n </button>\n )\n }\n\n return <div className=\"rounded-md bg-background/80 p-3 text-xs\">{rowContent}</div>\n}\n\n/**\n * Determine whether a signal has structured slot data.\n * If it has primaryValue, counterparty, qualifier, or components, use the structured row.\n */\nfunction hasStructuredData(signal: SignalScoreExplanationSignal): boolean {\n return Boolean(\n signal.primaryValue ||\n signal.qualifier ||\n signal.counterparty ||\n (signal.components && signal.components.length > 0),\n )\n}\n\n// ---------------------------------------------------------------------------\n// WhyCard - Expanded panel under a pill\n// ---------------------------------------------------------------------------\n\ninterface WhyCardProps {\n bucket: SignalScoreExplanationBucket\n signals: SignalScoreExplanationSignal[]\n item: QueueItem\n panelId: string\n onOpenSignalBucket?: ScoreWhyChipsProps[\"onOpenSignalBucket\"]\n onBucketFeedback?: (bucketKey: string, data: FeedbackSubmitData) => void\n}\n\nfunction WhyCard({ bucket, signals, item, panelId, onOpenSignalBucket, onBucketFeedback }: WhyCardProps) {\n const [showAll, setShowAll] = React.useState(false)\n const [bucketFeedback, setBucketFeedback] = React.useState<\"positive\" | \"negative\" | null>(null)\n const totalCount = bucket.signalCount ?? signals.length\n const visibleSignals = showAll ? signals : signals.slice(0, DEFAULT_VISIBLE_ROWS)\n const hiddenCount = signals.length - DEFAULT_VISIBLE_ROWS\n\n // Determine whether to use structured rows (any signal has structured data)\n const useStructured = signals.some(hasStructuredData)\n\n return (\n <div\n id={panelId}\n className=\"rounded-lg rounded-t-none border border-t-0 border-border bg-muted/20 p-3\"\n role=\"region\"\n aria-label={`${bucket.label} details`}\n >\n {/* Card header */}\n <div className=\"mb-2 flex items-center justify-between\">\n <span className=\"text-[10px] font-bold uppercase tracking-wider text-muted-foreground\">\n {totalCount} signal{totalCount !== 1 ? \"s\" : \"\"} – {bucket.label}\n </span>\n </div>\n\n {/* Signal rows */}\n {visibleSignals.length > 0 ? (\n <ul className=\"divide-y divide-border/30\" aria-label=\"Matching signals\">\n {visibleSignals.map((signal, index) => (\n <li key={signal.id ?? `${bucket.key}-signal-${index}`}>\n {useStructured ? (\n <StructuredSignalRow\n item={item}\n bucketKey={bucket.key}\n signal={signal}\n tone={bucket.tone}\n onOpenSignalBucket={onOpenSignalBucket}\n />\n ) : (\n <LegacySignalRow\n item={item}\n bucketKey={bucket.key}\n signal={signal}\n onOpenSignalBucket={onOpenSignalBucket}\n />\n )}\n </li>\n ))}\n </ul>\n ) : bucket.evidence && bucket.evidence.length > 0 ? (\n <ul className=\"mt-3 space-y-1.5\" aria-label=\"Matching signals\">\n {bucket.evidence.map((evidence, index) => (\n <li key={`${bucket.key}-evidence-${index}`} className=\"flex gap-2 text-xs text-muted-foreground\">\n <span className=\"mt-1.5 h-1 w-1 shrink-0 rounded-full bg-primary\" />\n <span className=\"leading-relaxed\">{evidence}</span>\n </li>\n ))}\n </ul>\n ) : null}\n\n {/* \"Show N more\" button */}\n {!showAll && hiddenCount > 0 && (\n <button\n type=\"button\"\n onClick={() => setShowAll(true)}\n className=\"mt-2 flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground\"\n >\n <ChevronDown className=\"h-3 w-3\" />\n Show {hiddenCount} more\n </button>\n )}\n\n {/* Bucket feedback footer */}\n {onBucketFeedback && (\n <div className=\"mt-3 border-t border-border/40 pt-3\">\n <FeedbackFooter\n feedback={bucketFeedback}\n onFeedbackChange={setBucketFeedback}\n onSubmit={(data) => onBucketFeedback(bucket.key, data)}\n negativeChips={BUCKET_NEGATIVE_CHIPS}\n negativePrompt=\"Was this bucket useful?\"\n positivePrompt=\"Thanks! What was useful about this bucket?\"\n />\n </div>\n )}\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// ScoreWhyChips - Main export\n// ---------------------------------------------------------------------------\n\nexport interface ScoreWhyChipsProps {\n item: QueueItem\n signalData: SignalScoreData\n onOpenSignalBucket?: (args: { item: QueueItem; bucketKey: string; signalId: string }) => void\n className?: string\n}\n\nexport function ScoreWhyChips({\n item,\n signalData,\n onOpenSignalBucket,\n className,\n}: ScoreWhyChipsProps) {\n const [selectedBucketKey, setSelectedBucketKey] = React.useState<string | null>(null)\n\n React.useEffect(() => {\n setSelectedBucketKey(null)\n }, [item.id])\n\n const reactId = React.useId()\n const idPrefix = makeDomId(\"score-why\", reactId, item.id)\n const buckets = React.useMemo(() => getSignalScoreBuckets(signalData), [signalData])\n const selectedBucket = buckets.find((bucket) => bucket.key === selectedBucketKey) ?? null\n const selectedBucketSignals = selectedBucket ? getBucketSignals(selectedBucket) : []\n const selectedPanelId = selectedBucket ? `${idPrefix}-panel-${makeDomId(selectedBucket.key)}` : undefined\n\n if (buckets.length === 0) return null\n\n return (\n <div className={cn(\"mt-4\", className)}>\n <div className=\"mb-2 flex items-center gap-2\">\n <span className=\"text-[10px] font-bold uppercase tracking-wider text-muted-foreground\">Why</span>\n </div>\n <div className=\"flex flex-wrap gap-1.5\">\n {buckets.map((bucket) => {\n const isSelected = selectedBucketKey === bucket.key\n const panelId = `${idPrefix}-panel-${makeDomId(bucket.key)}`\n const signals = getBucketSignals(bucket)\n const signalCount = bucket.signalCount ?? signals.length\n return (\n <div key={bucket.key} className=\"flex flex-col\">\n <WhyPill\n bucket={bucket}\n isSelected={isSelected}\n signalCount={signalCount}\n panelId={panelId}\n onToggle={() => setSelectedBucketKey((prev) => (prev === bucket.key ? null : bucket.key))}\n onClose={() => setSelectedBucketKey(null)}\n />\n </div>\n )\n })}\n </div>\n\n {selectedBucket && selectedPanelId && (\n <WhyCard\n bucket={selectedBucket}\n signals={selectedBucketSignals}\n item={item}\n panelId={selectedPanelId}\n onOpenSignalBucket={onOpenSignalBucket}\n onBucketFeedback={signalData.onBucketFeedback}\n />\n )}\n </div>\n )\n}\n"],"mappings":"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","names":["_a"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import 'react';
|
|
2
|
+
import './feedback-primitives.js';
|
|
3
|
+
export { P as PriorityFactor, S as SignalPriorityPopover, k as SignalPriorityPopoverProps } from '../signal-priority-popover-DQ_VuHac.js';
|
|
4
|
+
import './quick-action-sidebar-nav.js';
|
|
5
|
+
import './quick-action-modal.js';
|
|
6
|
+
import './score-breakdown.js';
|
|
7
|
+
import './suggested-actions.js';
|
|
8
|
+
import './detail-view.js';
|
|
9
|
+
import './inbox-toolbar.js';
|
|
10
|
+
import './data-table-filter.js';
|
|
11
|
+
import './data-table-condition-filter.js';
|
|
12
|
+
import './data-table.js';
|
|
13
|
+
import './metric-card.js';
|
|
14
|
+
import '../charts/pipeline-overview.js';
|
|
15
|
+
import './timeline-activity.js';
|
|
16
|
+
import './signal-feedback-inline.js';
|
|
17
|
+
import 'lucide-react';
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
"use client";
|
|
4
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
import { Popover as PopoverPrimitive } from "radix-ui";
|
|
7
|
+
import {
|
|
8
|
+
Radar,
|
|
9
|
+
Wallet,
|
|
10
|
+
Link2,
|
|
11
|
+
MessageSquare,
|
|
12
|
+
TrendingDown,
|
|
13
|
+
ArrowUpRight,
|
|
14
|
+
ArrowDownRight,
|
|
15
|
+
Clock,
|
|
16
|
+
Activity,
|
|
17
|
+
Minus,
|
|
18
|
+
ChevronDown,
|
|
19
|
+
ChevronUp,
|
|
20
|
+
Info
|
|
21
|
+
} from "lucide-react";
|
|
22
|
+
import { cn } from "../lib/utils.js";
|
|
23
|
+
import { FeedbackFooter } from "./feedback-primitives.js";
|
|
24
|
+
import { getSignalScoreUrgencyLabel, scoreRangeForUrgency, SIGNAL_TONE_CLASSES } from "./score-why-chips.js";
|
|
25
|
+
const URGENCY_TRIGGER_DEFAULT = {
|
|
26
|
+
Urgent: "border-red-200 bg-red-50 text-red-700",
|
|
27
|
+
High: "border-orange-200 bg-orange-50 text-orange-700",
|
|
28
|
+
Medium: "border-amber-200 bg-amber-50 text-amber-700",
|
|
29
|
+
Low: "border-blue-200 bg-blue-50 text-blue-700"
|
|
30
|
+
};
|
|
31
|
+
const URGENCY_TRIGGER_HOVER = {
|
|
32
|
+
Urgent: "hover:bg-red-100",
|
|
33
|
+
High: "hover:bg-orange-100",
|
|
34
|
+
Medium: "hover:bg-amber-100",
|
|
35
|
+
Low: "hover:bg-blue-100"
|
|
36
|
+
};
|
|
37
|
+
const URGENCY_TRIGGER_OPEN = {
|
|
38
|
+
Urgent: "bg-red-100",
|
|
39
|
+
High: "bg-orange-100",
|
|
40
|
+
Medium: "bg-amber-100",
|
|
41
|
+
Low: "bg-blue-100"
|
|
42
|
+
};
|
|
43
|
+
const TONE_ICON_CLASSES = SIGNAL_TONE_CLASSES;
|
|
44
|
+
const DIRECTION_CLASSES = {
|
|
45
|
+
raises: "text-red-600",
|
|
46
|
+
lowers: "text-emerald-600",
|
|
47
|
+
neutral: "text-muted-foreground"
|
|
48
|
+
};
|
|
49
|
+
const FACTOR_ICONS = {
|
|
50
|
+
radar: Radar,
|
|
51
|
+
wallet: Wallet,
|
|
52
|
+
"link-2": Link2,
|
|
53
|
+
"message-square": MessageSquare,
|
|
54
|
+
"trending-down": TrendingDown,
|
|
55
|
+
"arrow-up-right": ArrowUpRight,
|
|
56
|
+
clock: Clock,
|
|
57
|
+
activity: Activity
|
|
58
|
+
};
|
|
59
|
+
const URGENCY_DOT_CLASSES = {
|
|
60
|
+
Urgent: "bg-red-500",
|
|
61
|
+
High: "bg-orange-500",
|
|
62
|
+
Medium: "bg-amber-500",
|
|
63
|
+
Low: "bg-blue-500"
|
|
64
|
+
};
|
|
65
|
+
const DEFAULT_PRIORITY_FEEDBACK_CHIPS = [
|
|
66
|
+
{ label: "Wrong factor weighting" },
|
|
67
|
+
{ label: "Missing context" },
|
|
68
|
+
{ label: "Inaccurate data" },
|
|
69
|
+
{ label: "Stale" },
|
|
70
|
+
{ label: "Other" }
|
|
71
|
+
];
|
|
72
|
+
function DirectionIcon({ direction }) {
|
|
73
|
+
const cls = "h-2.5 w-2.5";
|
|
74
|
+
switch (direction) {
|
|
75
|
+
case "raises":
|
|
76
|
+
return /* @__PURE__ */ jsx(ArrowUpRight, { className: cls });
|
|
77
|
+
case "lowers":
|
|
78
|
+
return /* @__PURE__ */ jsx(ArrowDownRight, { className: cls });
|
|
79
|
+
case "neutral":
|
|
80
|
+
return /* @__PURE__ */ jsx(Minus, { className: cls });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function PriorityFactorRow({ factor }) {
|
|
84
|
+
var _a;
|
|
85
|
+
const IconComponent = (_a = FACTOR_ICONS[factor.icon]) != null ? _a : Activity;
|
|
86
|
+
const toneClasses = TONE_ICON_CLASSES[factor.tone];
|
|
87
|
+
const directionClasses = DIRECTION_CLASSES[factor.direction];
|
|
88
|
+
const directionLabel = factor.direction === "raises" ? "Raises" : factor.direction === "lowers" ? "Lowers" : "Neutral";
|
|
89
|
+
return /* @__PURE__ */ jsxs(
|
|
90
|
+
"div",
|
|
91
|
+
{
|
|
92
|
+
className: "grid grid-cols-[20px_1fr_auto] gap-x-3 gap-y-1 px-4 py-3",
|
|
93
|
+
"data-testid": `factor-row-${factor.key}`,
|
|
94
|
+
children: [
|
|
95
|
+
/* @__PURE__ */ jsx(
|
|
96
|
+
"div",
|
|
97
|
+
{
|
|
98
|
+
className: cn(
|
|
99
|
+
"flex h-5 w-5 items-center justify-center rounded",
|
|
100
|
+
toneClasses
|
|
101
|
+
),
|
|
102
|
+
children: /* @__PURE__ */ jsx(IconComponent, { className: "h-3 w-3" })
|
|
103
|
+
}
|
|
104
|
+
),
|
|
105
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
106
|
+
/* @__PURE__ */ jsx("span", { className: "text-[13px] font-semibold text-foreground", children: factor.label }),
|
|
107
|
+
/* @__PURE__ */ jsxs(
|
|
108
|
+
"span",
|
|
109
|
+
{
|
|
110
|
+
className: cn(
|
|
111
|
+
"inline-flex items-center gap-0.5 text-[10px] font-medium",
|
|
112
|
+
directionClasses
|
|
113
|
+
),
|
|
114
|
+
children: [
|
|
115
|
+
/* @__PURE__ */ jsx(DirectionIcon, { direction: factor.direction }),
|
|
116
|
+
directionLabel
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
)
|
|
120
|
+
] }),
|
|
121
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center text-right", children: [
|
|
122
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-bold tabular-nums", children: factor.score }),
|
|
123
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-normal text-muted-foreground", children: "/100" })
|
|
124
|
+
] }),
|
|
125
|
+
/* @__PURE__ */ jsx("div", {}),
|
|
126
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs leading-relaxed text-muted-foreground", children: factor.rationale }),
|
|
127
|
+
/* @__PURE__ */ jsx("div", {}),
|
|
128
|
+
/* @__PURE__ */ jsx("div", {}),
|
|
129
|
+
/* @__PURE__ */ jsx("div", { className: "mt-1 h-0.5 rounded-full bg-muted", children: /* @__PURE__ */ jsx(
|
|
130
|
+
"div",
|
|
131
|
+
{
|
|
132
|
+
className: "h-full rounded-full bg-foreground/20",
|
|
133
|
+
style: { width: `${Math.min(100, Math.max(0, factor.score))}%` }
|
|
134
|
+
}
|
|
135
|
+
) }),
|
|
136
|
+
/* @__PURE__ */ jsx("div", {})
|
|
137
|
+
]
|
|
138
|
+
}
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
function SignalPriorityPopover({
|
|
142
|
+
score,
|
|
143
|
+
urgencyLabel: providedLabel,
|
|
144
|
+
urgencyExplanation,
|
|
145
|
+
factors,
|
|
146
|
+
metaText,
|
|
147
|
+
feedbackChips,
|
|
148
|
+
onFeedbackSubmit,
|
|
149
|
+
className
|
|
150
|
+
}) {
|
|
151
|
+
const urgencyLabel = getSignalScoreUrgencyLabel(score, providedLabel);
|
|
152
|
+
const scoreRange = scoreRangeForUrgency(urgencyLabel);
|
|
153
|
+
const [open, setOpen] = React.useState(false);
|
|
154
|
+
const [feedback, setFeedback] = React.useState(null);
|
|
155
|
+
const triggerDefault = URGENCY_TRIGGER_DEFAULT[urgencyLabel];
|
|
156
|
+
const triggerHover = URGENCY_TRIGGER_HOVER[urgencyLabel];
|
|
157
|
+
const triggerOpen = URGENCY_TRIGGER_OPEN[urgencyLabel];
|
|
158
|
+
return /* @__PURE__ */ jsxs(PopoverPrimitive.Root, { open, onOpenChange: setOpen, children: [
|
|
159
|
+
/* @__PURE__ */ jsx(PopoverPrimitive.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
160
|
+
"button",
|
|
161
|
+
{
|
|
162
|
+
type: "button",
|
|
163
|
+
className: cn(
|
|
164
|
+
"inline-flex items-center gap-1 rounded-md border px-2.5 py-1 text-xs font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
165
|
+
triggerDefault,
|
|
166
|
+
triggerHover,
|
|
167
|
+
open && triggerOpen,
|
|
168
|
+
open && "outline-2 outline-foreground outline-offset-2",
|
|
169
|
+
className
|
|
170
|
+
),
|
|
171
|
+
"data-testid": "priority-popover-trigger",
|
|
172
|
+
children: [
|
|
173
|
+
urgencyLabel,
|
|
174
|
+
" Priority",
|
|
175
|
+
open ? /* @__PURE__ */ jsx(ChevronUp, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3" })
|
|
176
|
+
]
|
|
177
|
+
}
|
|
178
|
+
) }),
|
|
179
|
+
/* @__PURE__ */ jsx(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
|
|
180
|
+
PopoverPrimitive.Content,
|
|
181
|
+
{
|
|
182
|
+
side: "bottom",
|
|
183
|
+
align: "start",
|
|
184
|
+
sideOffset: 8,
|
|
185
|
+
onOpenAutoFocus: (e) => e.preventDefault(),
|
|
186
|
+
className: "z-50 w-[420px] rounded-lg border border-border bg-background shadow-lg p-0",
|
|
187
|
+
"data-testid": "priority-popover-content",
|
|
188
|
+
children: [
|
|
189
|
+
/* @__PURE__ */ jsxs("div", { className: "p-4 pb-3", children: [
|
|
190
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
|
|
191
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm font-semibold text-foreground", children: [
|
|
192
|
+
"Why this is ",
|
|
193
|
+
urgencyLabel.toLowerCase(),
|
|
194
|
+
" priority"
|
|
195
|
+
] }),
|
|
196
|
+
/* @__PURE__ */ jsxs("span", { className: "text-2xl font-bold tabular-nums text-foreground", children: [
|
|
197
|
+
score,
|
|
198
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-normal text-muted-foreground", children: "/100" })
|
|
199
|
+
] })
|
|
200
|
+
] }),
|
|
201
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-1 flex items-center gap-1.5 text-[11px] text-muted-foreground", children: [
|
|
202
|
+
/* @__PURE__ */ jsx(
|
|
203
|
+
"span",
|
|
204
|
+
{
|
|
205
|
+
className: cn(
|
|
206
|
+
"inline-block h-2 w-2 rounded-full",
|
|
207
|
+
URGENCY_DOT_CLASSES[urgencyLabel]
|
|
208
|
+
)
|
|
209
|
+
}
|
|
210
|
+
),
|
|
211
|
+
urgencyLabel,
|
|
212
|
+
" range: ",
|
|
213
|
+
scoreRange
|
|
214
|
+
] }),
|
|
215
|
+
urgencyExplanation && /* @__PURE__ */ jsx("p", { className: "mt-2 text-xs leading-relaxed text-muted-foreground", children: urgencyExplanation })
|
|
216
|
+
] }),
|
|
217
|
+
factors.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
218
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-t border-border px-4 py-2", children: [
|
|
219
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-bold uppercase tracking-wider text-muted-foreground", children: "Contributing factors" }),
|
|
220
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 text-[10px] text-muted-foreground", children: [
|
|
221
|
+
/* @__PURE__ */ jsx(Info, { className: "h-3 w-3" }),
|
|
222
|
+
"Score = weighted sum"
|
|
223
|
+
] })
|
|
224
|
+
] }),
|
|
225
|
+
/* @__PURE__ */ jsx("div", { className: "divide-y divide-border/40", children: factors.map((factor) => /* @__PURE__ */ jsx(PriorityFactorRow, { factor }, factor.key)) })
|
|
226
|
+
] }),
|
|
227
|
+
onFeedbackSubmit && /* @__PURE__ */ jsx("div", { className: "border-t border-border", children: /* @__PURE__ */ jsx(
|
|
228
|
+
FeedbackFooter,
|
|
229
|
+
{
|
|
230
|
+
feedback,
|
|
231
|
+
onFeedbackChange: setFeedback,
|
|
232
|
+
onSubmit: onFeedbackSubmit,
|
|
233
|
+
metaText,
|
|
234
|
+
negativeChips: feedbackChips != null ? feedbackChips : DEFAULT_PRIORITY_FEEDBACK_CHIPS,
|
|
235
|
+
positivePrompt: "Thanks. Anything to keep about this score?",
|
|
236
|
+
className: "px-4 py-3"
|
|
237
|
+
}
|
|
238
|
+
) })
|
|
239
|
+
]
|
|
240
|
+
}
|
|
241
|
+
) })
|
|
242
|
+
] });
|
|
243
|
+
}
|
|
244
|
+
export {
|
|
245
|
+
SignalPriorityPopover
|
|
246
|
+
};
|
|
247
|
+
//# sourceMappingURL=signal-priority-popover.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/signal-priority-popover.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { Popover as PopoverPrimitive } from \"radix-ui\"\nimport type { LucideIcon } from \"lucide-react\"\nimport {\n Radar,\n Wallet,\n Link2,\n MessageSquare,\n TrendingDown,\n ArrowUpRight,\n ArrowDownRight,\n Clock,\n Activity,\n Minus,\n ChevronDown,\n ChevronUp,\n Info,\n} from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\nimport { FeedbackFooter } from \"./feedback-primitives\"\nimport type { FeedbackChipTree, FeedbackSubmitData } from \"./feedback-primitives\"\nimport type { SignalScoreUrgencyLabel } from \"../prototype/prototype-config\"\nimport { getSignalScoreUrgencyLabel, scoreRangeForUrgency, SIGNAL_TONE_CLASSES } from \"./score-why-chips\"\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * A single contributing factor in the priority popover.\n */\nexport interface PriorityFactor {\n key: string\n label: string\n /** Lucide icon name (e.g. \"radar\", \"wallet\", \"link-2\", \"message-square\"). */\n icon: string\n /** Drives icon background tint. */\n tone: \"alert\" | \"warn\" | \"info\"\n /** Explicit semantic label - NOT inferred from score+weight. */\n direction: \"raises\" | \"lowers\" | \"neutral\"\n /** 0-100 */\n score: number\n /** Evidence text (e.g. \"$3.4M moved in 8h - current treasury balance $0.00\"). */\n rationale: string\n}\n\nexport interface SignalPriorityPopoverProps {\n score: number\n urgencyLabel?: SignalScoreUrgencyLabel\n /** Synthesis sentence displayed in the popover head. */\n urgencyExplanation?: string\n factors: PriorityFactor[]\n /** e.g. \"Updated 4m ago - model v3.2\" */\n metaText?: string\n /** Negative feedback issue tree. */\n feedbackChips?: FeedbackChipTree[]\n onFeedbackSubmit?: (data: FeedbackSubmitData) => void\n className?: string\n}\n\n// ---------------------------------------------------------------------------\n// Static class maps (required for Tailwind v4 source scanning)\n// ---------------------------------------------------------------------------\n\nconst URGENCY_TRIGGER_DEFAULT: Record<SignalScoreUrgencyLabel, string> = {\n Urgent: \"border-red-200 bg-red-50 text-red-700\",\n High: \"border-orange-200 bg-orange-50 text-orange-700\",\n Medium: \"border-amber-200 bg-amber-50 text-amber-700\",\n Low: \"border-blue-200 bg-blue-50 text-blue-700\",\n}\n\nconst URGENCY_TRIGGER_HOVER: Record<SignalScoreUrgencyLabel, string> = {\n Urgent: \"hover:bg-red-100\",\n High: \"hover:bg-orange-100\",\n Medium: \"hover:bg-amber-100\",\n Low: \"hover:bg-blue-100\",\n}\n\nconst URGENCY_TRIGGER_OPEN: Record<SignalScoreUrgencyLabel, string> = {\n Urgent: \"bg-red-100\",\n High: \"bg-orange-100\",\n Medium: \"bg-amber-100\",\n Low: \"bg-blue-100\",\n}\n\n/** Re-use shared tone classes from score-why-chips. */\nconst TONE_ICON_CLASSES: Record<PriorityFactor[\"tone\"], string> = SIGNAL_TONE_CLASSES as Record<PriorityFactor[\"tone\"], string>\n\nconst DIRECTION_CLASSES: Record<PriorityFactor[\"direction\"], string> = {\n raises: \"text-red-600\",\n lowers: \"text-emerald-600\",\n neutral: \"text-muted-foreground\",\n}\n\n// ---------------------------------------------------------------------------\n// Icon lookup\n// ---------------------------------------------------------------------------\n\nconst FACTOR_ICONS: Record<string, LucideIcon> = {\n radar: Radar,\n wallet: Wallet,\n \"link-2\": Link2,\n \"message-square\": MessageSquare,\n \"trending-down\": TrendingDown,\n \"arrow-up-right\": ArrowUpRight,\n clock: Clock,\n activity: Activity,\n}\n\n// ---------------------------------------------------------------------------\n// Urgency dot color (static map)\n// ---------------------------------------------------------------------------\n\nconst URGENCY_DOT_CLASSES: Record<SignalScoreUrgencyLabel, string> = {\n Urgent: \"bg-red-500\",\n High: \"bg-orange-500\",\n Medium: \"bg-amber-500\",\n Low: \"bg-blue-500\",\n}\n\n// ---------------------------------------------------------------------------\n// Default feedback chips\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_PRIORITY_FEEDBACK_CHIPS: FeedbackChipTree[] = [\n { label: \"Wrong factor weighting\" },\n { label: \"Missing context\" },\n { label: \"Inaccurate data\" },\n { label: \"Stale\" },\n { label: \"Other\" },\n]\n\n// ---------------------------------------------------------------------------\n// Direction icon component\n// ---------------------------------------------------------------------------\n\nfunction DirectionIcon({ direction }: { direction: PriorityFactor[\"direction\"] }) {\n const cls = \"h-2.5 w-2.5\"\n switch (direction) {\n case \"raises\":\n return <ArrowUpRight className={cls} />\n case \"lowers\":\n return <ArrowDownRight className={cls} />\n case \"neutral\":\n return <Minus className={cls} />\n }\n}\n\n// ---------------------------------------------------------------------------\n// PriorityFactorRow\n// ---------------------------------------------------------------------------\n\nfunction PriorityFactorRow({ factor }: { factor: PriorityFactor }) {\n const IconComponent = FACTOR_ICONS[factor.icon] ?? Activity\n const toneClasses = TONE_ICON_CLASSES[factor.tone]\n const directionClasses = DIRECTION_CLASSES[factor.direction]\n const directionLabel =\n factor.direction === \"raises\"\n ? \"Raises\"\n : factor.direction === \"lowers\"\n ? \"Lowers\"\n : \"Neutral\"\n\n return (\n <div\n className=\"grid grid-cols-[20px_1fr_auto] gap-x-3 gap-y-1 px-4 py-3\"\n data-testid={`factor-row-${factor.key}`}\n >\n {/* Icon */}\n <div\n className={cn(\n \"flex h-5 w-5 items-center justify-center rounded\",\n toneClasses,\n )}\n >\n <IconComponent className=\"h-3 w-3\" />\n </div>\n\n {/* Label + direction tag */}\n <div className=\"flex items-center gap-2\">\n <span className=\"text-[13px] font-semibold text-foreground\">\n {factor.label}\n </span>\n <span\n className={cn(\n \"inline-flex items-center gap-0.5 text-[10px] font-medium\",\n directionClasses,\n )}\n >\n <DirectionIcon direction={factor.direction} />\n {directionLabel}\n </span>\n </div>\n\n {/* Score number */}\n <div className=\"flex items-center text-right\">\n <span className=\"text-sm font-bold tabular-nums\">{factor.score}</span>\n <span className=\"text-xs font-normal text-muted-foreground\">/100</span>\n </div>\n\n {/* empty grid cell under icon column */}\n <div />\n\n {/* Rationale */}\n <p className=\"text-xs leading-relaxed text-muted-foreground\">\n {factor.rationale}\n </p>\n\n {/* empty grid cell under score column */}\n <div />\n\n {/* empty grid cell under icon column */}\n <div />\n\n {/* Score track */}\n <div className=\"mt-1 h-0.5 rounded-full bg-muted\">\n <div\n className=\"h-full rounded-full bg-foreground/20\"\n style={{ width: `${Math.min(100, Math.max(0, factor.score))}%` }}\n />\n </div>\n\n {/* empty grid cell under score column */}\n <div />\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// SignalPriorityPopover\n// ---------------------------------------------------------------------------\n\nexport function SignalPriorityPopover({\n score,\n urgencyLabel: providedLabel,\n urgencyExplanation,\n factors,\n metaText,\n feedbackChips,\n onFeedbackSubmit,\n className,\n}: SignalPriorityPopoverProps) {\n const urgencyLabel = getSignalScoreUrgencyLabel(score, providedLabel)\n const scoreRange = scoreRangeForUrgency(urgencyLabel)\n\n const [open, setOpen] = React.useState(false)\n const [feedback, setFeedback] = React.useState<\"positive\" | \"negative\" | null>(null)\n\n const triggerDefault = URGENCY_TRIGGER_DEFAULT[urgencyLabel]\n const triggerHover = URGENCY_TRIGGER_HOVER[urgencyLabel]\n const triggerOpen = URGENCY_TRIGGER_OPEN[urgencyLabel]\n\n return (\n <PopoverPrimitive.Root open={open} onOpenChange={setOpen}>\n <PopoverPrimitive.Trigger asChild>\n <button\n type=\"button\"\n className={cn(\n \"inline-flex items-center gap-1 rounded-md border px-2.5 py-1 text-xs font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n triggerDefault,\n triggerHover,\n open && triggerOpen,\n open && \"outline-2 outline-foreground outline-offset-2\",\n className,\n )}\n data-testid=\"priority-popover-trigger\"\n >\n {urgencyLabel} Priority\n {open ? (\n <ChevronUp className=\"h-3 w-3\" />\n ) : (\n <ChevronDown className=\"h-3 w-3\" />\n )}\n </button>\n </PopoverPrimitive.Trigger>\n\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n side=\"bottom\"\n align=\"start\"\n sideOffset={8}\n onOpenAutoFocus={(e) => e.preventDefault()}\n className=\"z-50 w-[420px] rounded-lg border border-border bg-background shadow-lg p-0\"\n data-testid=\"priority-popover-content\"\n >\n {/* Head section */}\n <div className=\"p-4 pb-3\">\n <div className=\"flex items-start justify-between gap-3\">\n <p className=\"text-sm font-semibold text-foreground\">\n Why this is {urgencyLabel.toLowerCase()} priority\n </p>\n <span className=\"text-2xl font-bold tabular-nums text-foreground\">\n {score}\n <span className=\"text-sm font-normal text-muted-foreground\">/100</span>\n </span>\n </div>\n\n {/* Band indicator */}\n <div className=\"mt-1 flex items-center gap-1.5 text-[11px] text-muted-foreground\">\n <span\n className={cn(\n \"inline-block h-2 w-2 rounded-full\",\n URGENCY_DOT_CLASSES[urgencyLabel],\n )}\n />\n {urgencyLabel} range: {scoreRange}\n </div>\n\n {/* Synthesis sentence */}\n {urgencyExplanation && (\n <p className=\"mt-2 text-xs leading-relaxed text-muted-foreground\">\n {urgencyExplanation}\n </p>\n )}\n </div>\n\n {/* Section label */}\n {factors.length > 0 && (\n <>\n <div className=\"flex items-center justify-between border-t border-border px-4 py-2\">\n <span className=\"text-[10px] font-bold uppercase tracking-wider text-muted-foreground\">\n Contributing factors\n </span>\n <span className=\"flex items-center gap-1 text-[10px] text-muted-foreground\">\n <Info className=\"h-3 w-3\" />\n Score = weighted sum\n </span>\n </div>\n\n {/* Factor rows */}\n <div className=\"divide-y divide-border/40\">\n {factors.map((factor) => (\n <PriorityFactorRow key={factor.key} factor={factor} />\n ))}\n </div>\n </>\n )}\n\n {/* Feedback footer */}\n {onFeedbackSubmit && (\n <div className=\"border-t border-border\">\n <FeedbackFooter\n feedback={feedback}\n onFeedbackChange={setFeedback}\n onSubmit={onFeedbackSubmit}\n metaText={metaText}\n negativeChips={feedbackChips ?? DEFAULT_PRIORITY_FEEDBACK_CHIPS}\n positivePrompt=\"Thanks. Anything to keep about this score?\"\n className=\"px-4 py-3\"\n />\n </div>\n )}\n </PopoverPrimitive.Content>\n </PopoverPrimitive.Portal>\n </PopoverPrimitive.Root>\n )\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ProfileLike } from '../lib/user-display.js';
|
|
3
|
+
|
|
4
|
+
interface UserPillProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "children"> {
|
|
5
|
+
profile?: ProfileLike | null;
|
|
6
|
+
name?: string | null;
|
|
7
|
+
email?: string | null;
|
|
8
|
+
avatarUrl?: string | null;
|
|
9
|
+
subtitle?: React.ReactNode;
|
|
10
|
+
variant?: "default" | "compact" | "large";
|
|
11
|
+
}
|
|
12
|
+
declare function UserPill({ profile, name, email, avatarUrl, subtitle, variant, className, ...props }: UserPillProps): React.JSX.Element;
|
|
13
|
+
interface ActorBylineProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "children"> {
|
|
14
|
+
actor?: ProfileLike | null;
|
|
15
|
+
verb?: React.ReactNode;
|
|
16
|
+
subject?: React.ReactNode;
|
|
17
|
+
timestamp?: string | Date | null;
|
|
18
|
+
className?: string;
|
|
19
|
+
}
|
|
20
|
+
declare function ActorByline({ actor, verb, subject, timestamp, className, ...props }: ActorBylineProps): React.JSX.Element;
|
|
21
|
+
|
|
22
|
+
export { ActorByline, type ActorBylineProps, UserPill, type UserPillProps };
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
"use client";
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __defProps = Object.defineProperties;
|
|
6
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
7
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
10
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
11
|
+
var __spreadValues = (a, b) => {
|
|
12
|
+
for (var prop in b || (b = {}))
|
|
13
|
+
if (__hasOwnProp.call(b, prop))
|
|
14
|
+
__defNormalProp(a, prop, b[prop]);
|
|
15
|
+
if (__getOwnPropSymbols)
|
|
16
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
17
|
+
if (__propIsEnum.call(b, prop))
|
|
18
|
+
__defNormalProp(a, prop, b[prop]);
|
|
19
|
+
}
|
|
20
|
+
return a;
|
|
21
|
+
};
|
|
22
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
23
|
+
var __objRest = (source, exclude) => {
|
|
24
|
+
var target = {};
|
|
25
|
+
for (var prop in source)
|
|
26
|
+
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
27
|
+
target[prop] = source[prop];
|
|
28
|
+
if (source != null && __getOwnPropSymbols)
|
|
29
|
+
for (var prop of __getOwnPropSymbols(source)) {
|
|
30
|
+
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
31
|
+
target[prop] = source[prop];
|
|
32
|
+
}
|
|
33
|
+
return target;
|
|
34
|
+
};
|
|
35
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
36
|
+
import { Avatar, AvatarFallback, AvatarImage } from "./avatar.js";
|
|
37
|
+
import { cn } from "../lib/utils.js";
|
|
38
|
+
import { displayName, getInitials } from "../lib/user-display.js";
|
|
39
|
+
function UserPill(_a) {
|
|
40
|
+
var _b = _a, {
|
|
41
|
+
profile,
|
|
42
|
+
name,
|
|
43
|
+
email,
|
|
44
|
+
avatarUrl,
|
|
45
|
+
subtitle,
|
|
46
|
+
variant = "default",
|
|
47
|
+
className
|
|
48
|
+
} = _b, props = __objRest(_b, [
|
|
49
|
+
"profile",
|
|
50
|
+
"name",
|
|
51
|
+
"email",
|
|
52
|
+
"avatarUrl",
|
|
53
|
+
"subtitle",
|
|
54
|
+
"variant",
|
|
55
|
+
"className"
|
|
56
|
+
]);
|
|
57
|
+
const userProfile = __spreadProps(__spreadValues({}, profile), {
|
|
58
|
+
name: name != null ? name : profile == null ? void 0 : profile.name,
|
|
59
|
+
email: email != null ? email : profile == null ? void 0 : profile.email,
|
|
60
|
+
avatar_url: avatarUrl != null ? avatarUrl : profile == null ? void 0 : profile.avatar_url
|
|
61
|
+
});
|
|
62
|
+
const resolvedName = displayName(userProfile);
|
|
63
|
+
const resolvedAvatarUrl = avatarUrl != null ? avatarUrl : profile == null ? void 0 : profile.avatar_url;
|
|
64
|
+
const avatarSize = variant === "large" ? "default" : "sm";
|
|
65
|
+
return /* @__PURE__ */ jsxs(
|
|
66
|
+
"div",
|
|
67
|
+
__spreadProps(__spreadValues({
|
|
68
|
+
"data-slot": "user-pill",
|
|
69
|
+
"data-variant": variant,
|
|
70
|
+
className: cn(
|
|
71
|
+
"inline-flex max-w-full items-center gap-2 rounded-full border border-border bg-background text-sm text-foreground shadow-xs",
|
|
72
|
+
variant === "compact" && "px-2 py-0.5",
|
|
73
|
+
variant === "default" && "px-2.5 py-1",
|
|
74
|
+
variant === "large" && "px-3 py-1.5",
|
|
75
|
+
className
|
|
76
|
+
),
|
|
77
|
+
title: resolvedName
|
|
78
|
+
}, props), {
|
|
79
|
+
children: [
|
|
80
|
+
/* @__PURE__ */ jsxs(Avatar, { size: avatarSize, "aria-hidden": "true", children: [
|
|
81
|
+
resolvedAvatarUrl ? /* @__PURE__ */ jsx(AvatarImage, { src: resolvedAvatarUrl, alt: "" }) : null,
|
|
82
|
+
/* @__PURE__ */ jsx(AvatarFallback, { children: getInitials(userProfile) })
|
|
83
|
+
] }),
|
|
84
|
+
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-col leading-tight", children: [
|
|
85
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 truncate font-medium", children: resolvedName }),
|
|
86
|
+
subtitle ? /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate text-xs text-muted-foreground", children: subtitle }) : null
|
|
87
|
+
] })
|
|
88
|
+
]
|
|
89
|
+
})
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
function ActorByline(_c) {
|
|
93
|
+
var _d = _c, {
|
|
94
|
+
actor,
|
|
95
|
+
verb,
|
|
96
|
+
subject,
|
|
97
|
+
timestamp,
|
|
98
|
+
className
|
|
99
|
+
} = _d, props = __objRest(_d, [
|
|
100
|
+
"actor",
|
|
101
|
+
"verb",
|
|
102
|
+
"subject",
|
|
103
|
+
"timestamp",
|
|
104
|
+
"className"
|
|
105
|
+
]);
|
|
106
|
+
const actorName = displayName(actor);
|
|
107
|
+
const renderedTimestamp = timestamp instanceof Date ? timestamp.toISOString() : timestamp;
|
|
108
|
+
return /* @__PURE__ */ jsxs(
|
|
109
|
+
"div",
|
|
110
|
+
__spreadProps(__spreadValues({
|
|
111
|
+
"data-slot": "actor-byline",
|
|
112
|
+
className: cn("text-sm text-muted-foreground", className)
|
|
113
|
+
}, props), {
|
|
114
|
+
children: [
|
|
115
|
+
/* @__PURE__ */ jsx("span", { className: "text-foreground", children: actorName }),
|
|
116
|
+
verb ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
117
|
+
" ",
|
|
118
|
+
verb
|
|
119
|
+
] }) : null,
|
|
120
|
+
subject ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
121
|
+
" ",
|
|
122
|
+
subject
|
|
123
|
+
] }) : null,
|
|
124
|
+
renderedTimestamp ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
125
|
+
" ",
|
|
126
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\xB7" }),
|
|
127
|
+
" ",
|
|
128
|
+
renderedTimestamp
|
|
129
|
+
] }) : null
|
|
130
|
+
]
|
|
131
|
+
})
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
export {
|
|
135
|
+
ActorByline,
|
|
136
|
+
UserPill
|
|
137
|
+
};
|
|
138
|
+
//# sourceMappingURL=user-display.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/user-display.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\n\nimport { Avatar, AvatarFallback, AvatarImage } from \"./avatar\"\nimport { cn } from \"../lib/utils\"\nimport { displayName, getInitials, type ProfileLike } from \"../lib/user-display\"\n\nexport interface UserPillProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"children\"> {\n profile?: ProfileLike | null\n name?: string | null\n email?: string | null\n avatarUrl?: string | null\n subtitle?: React.ReactNode\n variant?: \"default\" | \"compact\" | \"large\"\n}\n\nfunction UserPill({\n profile,\n name,\n email,\n avatarUrl,\n subtitle,\n variant = \"default\",\n className,\n ...props\n}: UserPillProps) {\n const userProfile: ProfileLike = {\n ...profile,\n name: name ?? profile?.name,\n email: email ?? profile?.email,\n avatar_url: avatarUrl ?? profile?.avatar_url,\n }\n const resolvedName = displayName(userProfile)\n const resolvedAvatarUrl = avatarUrl ?? profile?.avatar_url\n const avatarSize = variant === \"large\" ? \"default\" : \"sm\"\n\n return (\n <div\n data-slot=\"user-pill\"\n data-variant={variant}\n className={cn(\n \"inline-flex max-w-full items-center gap-2 rounded-full border border-border bg-background text-sm text-foreground shadow-xs\",\n variant === \"compact\" && \"px-2 py-0.5\",\n variant === \"default\" && \"px-2.5 py-1\",\n variant === \"large\" && \"px-3 py-1.5\",\n className\n )}\n title={resolvedName}\n {...props}\n >\n <Avatar size={avatarSize} aria-hidden=\"true\">\n {resolvedAvatarUrl ? <AvatarImage src={resolvedAvatarUrl} alt=\"\" /> : null}\n <AvatarFallback>{getInitials(userProfile)}</AvatarFallback>\n </Avatar>\n <span className=\"flex min-w-0 flex-col leading-tight\">\n <span className=\"min-w-0 truncate font-medium\">{resolvedName}</span>\n {subtitle ? <span className=\"min-w-0 truncate text-xs text-muted-foreground\">{subtitle}</span> : null}\n </span>\n </div>\n )\n}\n\nexport interface ActorBylineProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"children\"> {\n actor?: ProfileLike | null\n verb?: React.ReactNode\n subject?: React.ReactNode\n timestamp?: string | Date | null\n className?: string\n}\n\nfunction ActorByline({\n actor,\n verb,\n subject,\n timestamp,\n className,\n ...props\n}: ActorBylineProps) {\n const actorName = displayName(actor)\n const renderedTimestamp = timestamp instanceof Date ? timestamp.toISOString() : timestamp\n return (\n <div\n data-slot=\"actor-byline\"\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n >\n <span className=\"text-foreground\">{actorName}</span>\n {verb ? <> {verb}</> : null}\n {subject ? <> {subject}</> : null}\n {renderedTimestamp ? <> <span aria-hidden=\"true\">·</span> {renderedTimestamp}</> : null}\n </div>\n )\n}\n\nexport { ActorByline, UserPill }\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmDM,SAqCQ,UApCe,KADvB;AA/CN,SAAS,QAAQ,gBAAgB,mBAAmB;AACpD,SAAS,UAAU;AACnB,SAAS,aAAa,mBAAqC;AAW3D,SAAS,SAAS,IASA;AATA,eAChB;AAAA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV;AAAA,EAxBF,IAiBkB,IAQb,kBARa,IAQb;AAAA,IAPH;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAGA,QAAM,cAA2B,iCAC5B,UAD4B;AAAA,IAE/B,MAAM,sBAAQ,mCAAS;AAAA,IACvB,OAAO,wBAAS,mCAAS;AAAA,IACzB,YAAY,gCAAa,mCAAS;AAAA,EACpC;AACA,QAAM,eAAe,YAAY,WAAW;AAC5C,QAAM,oBAAoB,gCAAa,mCAAS;AAChD,QAAM,aAAa,YAAY,UAAU,YAAY;AAErD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,gBAAc;AAAA,MACd,WAAW;AAAA,QACT;AAAA,QACA,YAAY,aAAa;AAAA,QACzB,YAAY,aAAa;AAAA,QACzB,YAAY,WAAW;AAAA,QACvB;AAAA,MACF;AAAA,MACA,OAAO;AAAA,OACH,QAXL;AAAA,MAaC;AAAA,6BAAC,UAAO,MAAM,YAAY,eAAY,QACnC;AAAA,8BAAoB,oBAAC,eAAY,KAAK,mBAAmB,KAAI,IAAG,IAAK;AAAA,UACtE,oBAAC,kBAAgB,sBAAY,WAAW,GAAE;AAAA,WAC5C;AAAA,QACA,qBAAC,UAAK,WAAU,uCACd;AAAA,8BAAC,UAAK,WAAU,gCAAgC,wBAAa;AAAA,UAC5D,WAAW,oBAAC,UAAK,WAAU,kDAAkD,oBAAS,IAAU;AAAA,WACnG;AAAA;AAAA;AAAA,EACF;AAEJ;AAUA,SAAS,YAAY,IAOA;AAPA,eACnB;AAAA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EA5EF,IAuEqB,IAMhB,kBANgB,IAMhB;AAAA,IALH;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAGA,QAAM,YAAY,YAAY,KAAK;AACnC,QAAM,oBAAoB,qBAAqB,OAAO,UAAU,YAAY,IAAI;AAChF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,iCAAiC,SAAS;AAAA,OACpD,QAHL;AAAA,MAKC;AAAA,4BAAC,UAAK,WAAU,mBAAmB,qBAAU;AAAA,QAC5C,OAAO,iCAAE;AAAA;AAAA,UAAE;AAAA,WAAK,IAAM;AAAA,QACtB,UAAU,iCAAE;AAAA;AAAA,UAAE;AAAA,WAAQ,IAAM;AAAA,QAC5B,oBAAoB,iCAAE;AAAA;AAAA,UAAC,oBAAC,UAAK,eAAY,QAAO,kBAAC;AAAA,UAAO;AAAA,UAAE;AAAA,WAAkB,IAAM;AAAA;AAAA;AAAA,EACrF;AAEJ;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/user-pill.tsx"],"sourcesContent":["export { UserPill, type UserPillProps } from \"./user-display\"\n"],"mappings":"AAAA,SAAS,gBAAoC;","names":[]}
|