@flamingo-stack/openframe-frontend-core 0.0.222 → 0.0.223
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/{chunk-AIKPR6BE.js → chunk-4VHWSAQR.js} +6069 -5235
- package/dist/chunk-4VHWSAQR.js.map +1 -0
- package/dist/{chunk-GMNE7P7V.cjs → chunk-4WBVMIKQ.cjs} +26 -26
- package/dist/{chunk-GMNE7P7V.cjs.map → chunk-4WBVMIKQ.cjs.map} +1 -1
- package/dist/{chunk-QYRV6MKX.js → chunk-53EA4QPZ.js} +13 -7
- package/dist/chunk-53EA4QPZ.js.map +1 -0
- package/dist/{chunk-4JOWZLEL.cjs → chunk-7TKYFZ3L.cjs} +7 -7
- package/dist/{chunk-4JOWZLEL.cjs.map → chunk-7TKYFZ3L.cjs.map} +1 -1
- package/dist/{chunk-IZA5WF44.cjs → chunk-ABBU7QOM.cjs} +12 -12
- package/dist/{chunk-IZA5WF44.cjs.map → chunk-ABBU7QOM.cjs.map} +1 -1
- package/dist/{chunk-KM4KK2A6.cjs → chunk-ENDUMC7Z.cjs} +2558 -1724
- package/dist/chunk-ENDUMC7Z.cjs.map +1 -0
- package/dist/{chunk-CCSEXVI4.js → chunk-GYYGA24C.js} +2 -2
- package/dist/{chunk-RMOHKAHA.js → chunk-ISWWP2BH.js} +3 -3
- package/dist/{chunk-QNYH3WUU.cjs → chunk-JUWPBMQM.cjs} +13 -7
- package/dist/chunk-JUWPBMQM.cjs.map +1 -0
- package/dist/{chunk-A2SFHYVF.cjs → chunk-KXLTWANQ.cjs} +10 -10
- package/dist/{chunk-A2SFHYVF.cjs.map → chunk-KXLTWANQ.cjs.map} +1 -1
- package/dist/{chunk-ND24XSVT.js → chunk-TA6Z2AKD.js} +3 -3
- package/dist/chunk-TA6Z2AKD.js.map +1 -0
- package/dist/{chunk-HRIAZQ5D.js → chunk-WOKB6RJ7.js} +3 -3
- package/dist/components/chat/chat-archive-page.d.ts.map +1 -1
- package/dist/components/chat/chat-composer.d.ts.map +1 -1
- package/dist/components/chat/chat-message-enhanced.d.ts.map +1 -1
- package/dist/components/chat/chat-message-skeleton.d.ts +19 -4
- package/dist/components/chat/chat-message-skeleton.d.ts.map +1 -1
- package/dist/components/chat/chat-panel-header-mobile.d.ts +24 -0
- package/dist/components/chat/chat-panel-header-mobile.d.ts.map +1 -0
- package/dist/components/chat/chat-panel-header.d.ts +6 -0
- package/dist/components/chat/chat-panel-header.d.ts.map +1 -1
- package/dist/components/chat/chat-quick-action-row.d.ts +31 -0
- package/dist/components/chat/chat-quick-action-row.d.ts.map +1 -0
- package/dist/components/chat/embeddable-chat.d.ts +27 -0
- package/dist/components/chat/embeddable-chat.d.ts.map +1 -1
- package/dist/components/chat/entity-cards/dispatch.d.ts +14 -0
- package/dist/components/chat/entity-cards/dispatch.d.ts.map +1 -1
- package/dist/components/chat/entity-cards/github-activity-card.d.ts +10 -1
- package/dist/components/chat/entity-cards/github-activity-card.d.ts.map +1 -1
- package/dist/components/chat/guide-welcome.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts +9 -0
- package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-slash-commands.d.ts +26 -0
- package/dist/components/chat/hooks/use-slash-commands.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts +12 -1
- package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-unified-chat.d.ts +12 -0
- package/dist/components/chat/hooks/use-unified-chat.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +9 -3
- package/dist/components/chat/index.cjs.map +1 -1
- package/dist/components/chat/index.js +8 -2
- package/dist/components/chat/mingo-chat-history.d.ts +10 -0
- package/dist/components/chat/mingo-chat-history.d.ts.map +1 -1
- package/dist/components/chat/mingo-info-card.d.ts +63 -0
- package/dist/components/chat/mingo-info-card.d.ts.map +1 -0
- package/dist/components/chat/mingo-welcome.d.ts +14 -1
- package/dist/components/chat/mingo-welcome.d.ts.map +1 -1
- package/dist/components/chat/types/chat.types.d.ts +6 -0
- package/dist/components/chat/types/chat.types.d.ts.map +1 -1
- package/dist/components/chat/types/unified-chat-state.types.d.ts +30 -0
- package/dist/components/chat/types/unified-chat-state.types.d.ts.map +1 -1
- package/dist/components/chat/utils/process-historical-messages.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +4 -4
- package/dist/components/contact/index.js +3 -3
- package/dist/components/features/index.cjs +3 -3
- package/dist/components/features/index.js +2 -2
- package/dist/components/index.cjs +110 -104
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +36 -30
- package/dist/components/index.js.map +1 -1
- package/dist/components/navigation/app-layout-drawer.d.ts +7 -0
- package/dist/components/navigation/app-layout-drawer.d.ts.map +1 -1
- package/dist/components/navigation/index.cjs +3 -3
- package/dist/components/navigation/index.js +2 -2
- package/dist/components/onboarding-guides/index.cjs +26 -26
- package/dist/components/onboarding-guides/index.js +4 -4
- package/dist/components/tickets/index.cjs +67 -67
- package/dist/components/tickets/index.js +5 -5
- package/dist/components/ui/actions-menu.d.ts +9 -0
- package/dist/components/ui/actions-menu.d.ts.map +1 -1
- package/dist/components/ui/dropdown-button.d.ts +2 -2
- package/dist/components/ui/dropdown-button.d.ts.map +1 -1
- package/dist/components/ui/index.cjs +9 -3
- package/dist/components/ui/index.cjs.map +1 -1
- package/dist/components/ui/index.js +8 -2
- package/dist/components/ui/modal-v2.d.ts.map +1 -1
- package/dist/components/ui/more-actions-menu.d.ts +13 -0
- package/dist/components/ui/more-actions-menu.d.ts.map +1 -1
- package/dist/components/ui/square-avatar.d.ts +4 -0
- package/dist/components/ui/square-avatar.d.ts.map +1 -1
- package/dist/components/ui/textarea.d.ts.map +1 -1
- package/dist/hooks/index.cjs +2 -2
- package/dist/hooks/index.js +1 -1
- package/dist/hooks/ui/use-auto-limit-tags.d.ts +7 -1
- package/dist/hooks/ui/use-auto-limit-tags.d.ts.map +1 -1
- package/dist/index.cjs +9 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +8 -2
- package/package.json +1 -1
- package/src/components/chat/chat-archive-page.tsx +32 -7
- package/src/components/chat/chat-composer.tsx +6 -2
- package/src/components/chat/chat-message-enhanced.tsx +24 -7
- package/src/components/chat/chat-message-list.tsx +1 -1
- package/src/components/chat/chat-message-skeleton.tsx +60 -60
- package/src/components/chat/chat-panel-header-mobile.tsx +94 -0
- package/src/components/chat/chat-panel-header.tsx +78 -50
- package/src/components/chat/chat-quick-action-row.tsx +146 -0
- package/src/components/chat/embeddable-chat.tsx +140 -45
- package/src/components/chat/entity-cards/dispatch.tsx +770 -232
- package/src/components/chat/entity-cards/github-activity-card.tsx +9 -3
- package/src/components/chat/guide-welcome.tsx +11 -8
- package/src/components/chat/hooks/use-nats-chat-adapter.ts +80 -7
- package/src/components/chat/hooks/use-slash-commands.ts +39 -0
- package/src/components/chat/hooks/use-sse-chat-adapter.ts +49 -45
- package/src/components/chat/hooks/use-unified-chat.ts +31 -8
- package/src/components/chat/mingo-chat-history.tsx +118 -36
- package/src/components/chat/mingo-info-card.tsx +214 -0
- package/src/components/chat/mingo-welcome.tsx +89 -36
- package/src/components/chat/types/chat.types.ts +4 -0
- package/src/components/chat/types/unified-chat-state.types.ts +35 -0
- package/src/components/chat/utils/process-historical-messages.ts +8 -0
- package/src/components/content-loading-container.tsx +1 -1
- package/src/components/empty-state.tsx +3 -3
- package/src/components/faq-accordion.tsx +2 -2
- package/src/components/features/changelog-manager.tsx +2 -2
- package/src/components/features/highlight-config-section.tsx +2 -2
- package/src/components/features/og-editor-preview.tsx +3 -3
- package/src/components/features/release-media-manager.tsx +2 -2
- package/src/components/loading/card-skeleton.tsx +3 -3
- package/src/components/loading/device-card-skeleton.tsx +1 -1
- package/src/components/loading/margin-report-skeleton.tsx +2 -2
- package/src/components/loading/organization-card-skeleton.tsx +1 -1
- package/src/components/loading/organization-icon-skeleton.tsx +1 -1
- package/src/components/loading/page-layout-skeleton.tsx +9 -9
- package/src/components/loading/users-grid-skeleton.tsx +2 -2
- package/src/components/logs-list.tsx +1 -1
- package/src/components/media-carousel.tsx +1 -1
- package/src/components/navigation/app-layout-drawer.tsx +70 -6
- package/src/components/open-source-features.tsx +2 -2
- package/src/components/ui/actions-menu.tsx +36 -13
- package/src/components/ui/allowed-domains-input.tsx +3 -3
- package/src/components/ui/cursor-pagination.tsx +1 -1
- package/src/components/ui/dropdown-button.tsx +6 -6
- package/src/components/ui/info-row.tsx +2 -2
- package/src/components/ui/modal-v2.tsx +8 -1
- package/src/components/ui/more-actions-menu.tsx +13 -0
- package/src/components/ui/square-avatar.tsx +6 -1
- package/src/components/ui/tags-input.tsx +1 -1
- package/src/components/ui/textarea.tsx +18 -4
- package/src/components/vendor-icon.tsx +1 -1
- package/src/components/vendor-tag.tsx +1 -1
- package/src/components/why-it-matters.tsx +1 -1
- package/src/hooks/ui/use-auto-limit-tags.ts +22 -8
- package/src/stories/ActionsMenu.stories.tsx +256 -0
- package/src/stories/AppLayoutSidebar.stories.tsx +6 -0
- package/src/stories/Board.stories.tsx +1 -1
- package/src/stories/ChatMingoInfoCards.stories.tsx +748 -0
- package/src/stories/ChatTicketList.stories.tsx +1 -1
- package/src/stories/DataTable.stories.tsx +17 -13
- package/src/stories/EmbeddableChat.stories.tsx +233 -1
- package/src/stories/MingoInfoCard.stories.tsx +193 -0
- package/src/stories/MingoOnboardingCard.stories.tsx +1 -1
- package/src/stories/Modal.stories.tsx +1 -0
- package/src/stories/NotificationDrawer.stories.tsx +14 -3
- package/src/stories/PageActions.stories.tsx +4 -4
- package/src/stories/PageContainer.stories.tsx +5 -5
- package/src/stories/PageLayout.stories.tsx +1 -1
- package/src/stories/TabSelector.stories.tsx +2 -2
- package/src/stories/Tag.stories.tsx +10 -5
- package/src/stories/TicketInfoSection.stories.tsx +23 -11
- package/src/stories/Toaster.stories.tsx +5 -5
- package/src/stories/__fixtures__/chat-card-decorator.tsx +44 -0
- package/dist/chunk-AIKPR6BE.js.map +0 -1
- package/dist/chunk-KM4KK2A6.cjs.map +0 -1
- package/dist/chunk-ND24XSVT.js.map +0 -1
- package/dist/chunk-QNYH3WUU.cjs.map +0 -1
- package/dist/chunk-QYRV6MKX.js.map +0 -1
- package/src/stories/.TicketDescriptionViewer.stories.md +0 -39
- package/src/stories/.TicketKnowledgeBaseList.stories.md +0 -36
- package/src/stories/TicketDescriptionViewer.stories.tsx +0 -70
- package/src/stories/TicketKnowledgeBaseList.stories.tsx +0 -51
- /package/dist/{chunk-CCSEXVI4.js.map → chunk-GYYGA24C.js.map} +0 -0
- /package/dist/{chunk-RMOHKAHA.js.map → chunk-ISWWP2BH.js.map} +0 -0
- /package/dist/{chunk-HRIAZQ5D.js.map → chunk-WOKB6RJ7.js.map} +0 -0
|
@@ -39,7 +39,6 @@ import { resolveSourceRowCTA, resolveSourceIcon, sourceRowCtxFromRuntime } from
|
|
|
39
39
|
import { resolveHrefForRuntime } from '../utils/chat-nav-resolution'
|
|
40
40
|
import {
|
|
41
41
|
computeIsNewTab,
|
|
42
|
-
newTabAnchorAttrs,
|
|
43
42
|
buildAnchorProps,
|
|
44
43
|
} from '../utils/nav-anchor-props'
|
|
45
44
|
import { safeHref } from '../utils/compact-card-classes'
|
|
@@ -47,52 +46,68 @@ import { useChatPanel } from '../chat-panel-context'
|
|
|
47
46
|
import { getSourceLabel } from '../utils/source-icons'
|
|
48
47
|
import { SourceActionButton } from '../source-action-button'
|
|
49
48
|
import { NavLinkAnchorViaRuntime } from '../nav-link-anchor-via-runtime'
|
|
50
|
-
import { DEFAULT_PROGRAM_CONFIGS } from './program-card-defaults'
|
|
51
49
|
import { defaultBuildProductReleaseCardProps } from './product-release-card-defaults'
|
|
52
50
|
import { ChatVideoEntityCard } from './chat-video-entity-card'
|
|
53
51
|
import { BlockCard } from './block-card'
|
|
54
|
-
import {
|
|
55
|
-
import {
|
|
52
|
+
import { BlogCardSkeleton } from './blog-card'
|
|
53
|
+
import { CaseStudyCardSkeleton } from './case-study-card'
|
|
54
|
+
import { CustomerInterviewCardSkeleton } from './customer-interview-card'
|
|
56
55
|
import {
|
|
57
|
-
CustomerInterviewCard,
|
|
58
|
-
CustomerInterviewCardSkeleton,
|
|
59
|
-
} from './customer-interview-card'
|
|
60
|
-
import {
|
|
61
|
-
ProductReleaseCard,
|
|
62
56
|
ProductReleaseCardSkeleton,
|
|
63
57
|
type ProductReleaseCardProps,
|
|
64
58
|
} from './product-release-card'
|
|
59
|
+
import { ProgramCardSkeleton } from './program-card'
|
|
60
|
+
import { InvestorUpdateCardSkeleton } from './investor-update-card'
|
|
61
|
+
import { OnboardingGuideCardSkeleton } from './onboarding-guide-card'
|
|
62
|
+
import { CampaignCardAdminSkeleton } from './campaign-card-admin'
|
|
63
|
+
import { RoadmapCardSkeleton } from './roadmap-card'
|
|
64
|
+
import { TaskTypeIcon } from './task-type-icon'
|
|
65
65
|
import {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
import {
|
|
71
|
-
InvestorUpdateCard,
|
|
72
|
-
InvestorUpdateCardSkeleton,
|
|
73
|
-
} from './investor-update-card'
|
|
74
|
-
import {
|
|
75
|
-
OnboardingGuideCard,
|
|
76
|
-
OnboardingGuideCardSkeleton,
|
|
77
|
-
} from './onboarding-guide-card'
|
|
78
|
-
import {
|
|
79
|
-
CampaignCardAdmin,
|
|
80
|
-
CampaignCardAdminSkeleton,
|
|
81
|
-
} from './campaign-card-admin'
|
|
82
|
-
import { RoadmapCard, RoadmapCardSkeleton } from './roadmap-card'
|
|
83
|
-
import {
|
|
84
|
-
GitHubActivityCard,
|
|
85
|
-
type GitHubActivityCardAnchorProps,
|
|
66
|
+
parseGithubTitle,
|
|
67
|
+
formatActivityId,
|
|
68
|
+
kindLabel as githubKindLabel,
|
|
69
|
+
reviewStateLabel,
|
|
86
70
|
} from './github-activity-card'
|
|
87
|
-
import {
|
|
88
|
-
import {
|
|
89
|
-
import {
|
|
71
|
+
import { MingoInfoCard, type MingoInfoCardStatus } from '../mingo-info-card'
|
|
72
|
+
import type { ActionsMenuGroup } from '../../ui/actions-menu'
|
|
73
|
+
import { MingoIcon } from '../../icons'
|
|
74
|
+
import { EyeIcon } from '../../icons-v2-generated/interface/eye-icon'
|
|
75
|
+
import { ArrowRightUpIcon } from '../../icons-v2-generated/arrows/arrow-right-up-icon'
|
|
76
|
+
import { TagIcon } from '../../icons-v2-generated/shopping/tag-icon'
|
|
77
|
+
import { SlackLogoGreyIcon } from '../../icons-v2-generated/brand-logos/slack-logo-grey-icon'
|
|
78
|
+
import { FileContentIcon } from '../../icons-v2-generated/documents/file-content-icon'
|
|
79
|
+
import { ChartBar01VerIcon } from '../../icons-v2-generated/charts/chart-bar-01-ver-icon'
|
|
80
|
+
import { ChartPieIcon } from '../../icons-v2-generated/charts/chart-pie-icon'
|
|
81
|
+
import { MoneyBillDollarIcon } from '../../icons-v2-generated/finance/money-bill-dollar-icon'
|
|
82
|
+
import { BankIcon } from '../../icons-v2-generated/finance/bank-icon'
|
|
83
|
+
import { CoinsExchangeCurrencyIcon } from '../../icons-v2-generated/finance/coins-exchange-currency-icon'
|
|
84
|
+
import { NewspaperIcon } from '../../icons-v2-generated/documents/newspaper-icon'
|
|
85
|
+
import { TrophyIcon } from '../../icons-v2-generated/sport/trophy-icon'
|
|
86
|
+
import { MicrophoneIcon } from '../../icons-v2-generated/household/microphone-icon'
|
|
87
|
+
import { Rocket02Icon } from '../../icons-v2-generated/vehicles-and-delivery/rocket-02-icon'
|
|
88
|
+
import { TruckFastIcon } from '../../icons-v2-generated/vehicles-and-delivery/truck-fast-icon'
|
|
89
|
+
import { PresentationBarIcon } from '../../icons-v2-generated/charts/presentation-bar-icon'
|
|
90
|
+
import { PresentationLineIcon } from '../../icons-v2-generated/charts/presentation-line-icon'
|
|
91
|
+
import { CalendarIcon } from '../../icons-v2-generated/date-and-time/calendar-icon'
|
|
92
|
+
import { CompassIcon } from '../../icons-v2-generated/map-and-travel/compass-icon'
|
|
93
|
+
import { MapIcon } from '../../icons-v2-generated/map-and-travel/map-icon'
|
|
94
|
+
import { CheckSquareIcon } from '../../icons-v2-generated/signs-and-symbols/check-square-icon'
|
|
95
|
+
import { Megaphone01Icon } from '../../icons-v2-generated/shopping/megaphone-01-icon'
|
|
96
|
+
import { getStatusColorScheme } from '../utils/agent-status-message'
|
|
97
|
+
import { formatDateShort } from '../../../utils/date-formatters'
|
|
98
|
+
import { formatInvestorUpdatePeriod } from '../types/entities/investor-update'
|
|
99
|
+
import { CodingCommitIcon } from '../../icons-v2-generated/coding/coding-commit-icon'
|
|
100
|
+
import { CodingPullRequestIcon } from '../../icons-v2-generated/coding/coding-pull-request-icon'
|
|
101
|
+
import { CodeIcon } from '../../icons-v2-generated/coding/code-icon'
|
|
90
102
|
import {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
103
|
+
formatDateUTC as formatDate,
|
|
104
|
+
formatDurationCompact,
|
|
105
|
+
formatTimeWithTimezone,
|
|
106
|
+
formatDurationFromRange,
|
|
107
|
+
} from '../../../utils/format'
|
|
108
|
+
import type { PrReviewState } from '../types/entities/github-activity'
|
|
94
109
|
import type { GitHubActivityKind } from '../types/entities/github-activity'
|
|
95
|
-
import type {
|
|
110
|
+
import type { ProgramConfig } from '../types/entities/program-types'
|
|
96
111
|
|
|
97
112
|
// =============================================================================
|
|
98
113
|
// Public option / extras shape
|
|
@@ -143,6 +158,11 @@ export interface ChatCardRenderOptions {
|
|
|
143
158
|
* whatever `ChatCardNavWrap` + `handleChatNavClick` decide at click
|
|
144
159
|
* time. */
|
|
145
160
|
isNewTab: boolean
|
|
161
|
+
/** Host-provided "Ask Mingo"/"Display" affordance for this card, resolved
|
|
162
|
+
* once by `ChatCardLoader`. Each wrapper threads it into its "⋯" menu via
|
|
163
|
+
* `cardMenuGroups`. Undefined when the host supplied no `onDiscuss`/
|
|
164
|
+
* `onDisplay` (then the menu shows only "Open in new tab"). */
|
|
165
|
+
discuss?: CardDiscussAction
|
|
146
166
|
}
|
|
147
167
|
|
|
148
168
|
// =============================================================================
|
|
@@ -155,40 +175,157 @@ export interface ChatCardRenderOptions {
|
|
|
155
175
|
// close-on-nav gate.
|
|
156
176
|
// =============================================================================
|
|
157
177
|
|
|
178
|
+
/** Leading glyph per GitHub activity kind. */
|
|
179
|
+
function githubKindIcon(kind: GitHubActivityKind) {
|
|
180
|
+
switch (kind) {
|
|
181
|
+
case 'pull_request':
|
|
182
|
+
return <CodingPullRequestIcon size={24} />
|
|
183
|
+
case 'pr_review':
|
|
184
|
+
return <CodeIcon size={24} />
|
|
185
|
+
case 'commit':
|
|
186
|
+
default:
|
|
187
|
+
return <CodingCommitIcon size={24} />
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** PR review state → status-pill colour. */
|
|
192
|
+
function reviewStateToVariant(state: PrReviewState): MingoInfoCardStatus['variant'] {
|
|
193
|
+
switch (state) {
|
|
194
|
+
case 'APPROVED':
|
|
195
|
+
return 'success'
|
|
196
|
+
case 'CHANGES_REQUESTED':
|
|
197
|
+
return 'error'
|
|
198
|
+
case 'PENDING':
|
|
199
|
+
return 'warning'
|
|
200
|
+
case 'COMMENTED':
|
|
201
|
+
case 'DISMISSED':
|
|
202
|
+
default:
|
|
203
|
+
return 'grey'
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
158
207
|
function GitHubChatCard({
|
|
159
208
|
chatRef,
|
|
160
209
|
kind,
|
|
161
210
|
isNewTab,
|
|
211
|
+
discuss,
|
|
162
212
|
}: {
|
|
163
213
|
chatRef: ChatRef
|
|
164
214
|
kind: GitHubActivityKind
|
|
165
215
|
isNewTab: boolean
|
|
216
|
+
discuss?: CardDiscussAction
|
|
166
217
|
}) {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
218
|
+
// Reuse the card's own title parser so the display title + review state
|
|
219
|
+
// read identically here and in the (legacy) GitHubActivityCard.
|
|
220
|
+
const { display, reviewState } = parseGithubTitle(chatRef.title, kind)
|
|
221
|
+
const title =
|
|
222
|
+
kind === 'pr_review'
|
|
223
|
+
? display.replace(/^by\s+/i, '').trim() || 'Reviewer'
|
|
224
|
+
: display
|
|
225
|
+
|
|
226
|
+
// Review state pill for reviews; otherwise the neutral activity-type pill.
|
|
227
|
+
const status: MingoInfoCardStatus =
|
|
228
|
+
kind === 'pr_review' && reviewState
|
|
229
|
+
? { label: reviewStateLabel(reviewState), variant: reviewStateToVariant(reviewState) }
|
|
230
|
+
: { label: githubKindLabel(kind), variant: 'grey' }
|
|
231
|
+
|
|
232
|
+
// Description: "<id> · <date>" (short SHA / #num, then updated date).
|
|
233
|
+
const idLabel = formatActivityId(chatRef.id, kind)
|
|
234
|
+
const dateText = formatDate(chatRef.date ?? null, { fallback: '', timezone: 'local' })
|
|
235
|
+
const description = [idLabel, dateText].filter(Boolean).join(' · ') || undefined
|
|
236
|
+
|
|
171
237
|
return (
|
|
172
|
-
<
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
variant="compact"
|
|
181
|
-
anchorProps={anchorProps}
|
|
238
|
+
<MingoInfoCard
|
|
239
|
+
title={title}
|
|
240
|
+
description={description}
|
|
241
|
+
icon={githubKindIcon(kind)}
|
|
242
|
+
status={status}
|
|
243
|
+
anchorProps={buildAnchorProps(chatRef.url, isNewTab)}
|
|
244
|
+
menuGroups={cardMenuGroups(chatRef.url, discuss)}
|
|
245
|
+
menuAriaLabel="Activity actions"
|
|
182
246
|
/>
|
|
183
247
|
)
|
|
184
248
|
}
|
|
185
249
|
|
|
250
|
+
/** The host-provided "Ask Mingo" / "Display" affordance, resolved once per
|
|
251
|
+
* card in `ChatCardLoader` and threaded through `renderOpts.discuss`. Mirrors
|
|
252
|
+
* the legacy `ChatCardWithDiscuss` source-row button, relocated into the "⋯"
|
|
253
|
+
* menu. */
|
|
254
|
+
export interface CardDiscussAction {
|
|
255
|
+
label: string
|
|
256
|
+
icon: React.ReactNode
|
|
257
|
+
run: () => void
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** Overflow-menu groups for a card (Figma `7740:55075`): the optional "Ask
|
|
261
|
+
* Mingo"/"Display" item (when the host supplied a handler) followed by "Open
|
|
262
|
+
* Details" — whose main row navigates to the card (same tab) and whose trailing
|
|
263
|
+
* ↗ side-button opens it in a new tab. Returns `undefined` when neither applies
|
|
264
|
+
* so the "⋯" button is omitted entirely. The href is already absolute
|
|
265
|
+
* (ChatCardLoader pre-resolves it). */
|
|
266
|
+
function cardMenuGroups(
|
|
267
|
+
href: string | null | undefined,
|
|
268
|
+
discuss?: CardDiscussAction,
|
|
269
|
+
/** Per-entity override for the "Open Details" row — e.g. HubSpot tickets use
|
|
270
|
+
* `{ label: 'Open Ticket Details', icon: <HubSpot logo> }` so the row reads
|
|
271
|
+
* in the entity's own terms and mirrors the card's leading icon. Defaults to
|
|
272
|
+
* a generic "Open Details" + eye glyph. */
|
|
273
|
+
openDetails?: { label: string; icon: React.ReactNode },
|
|
274
|
+
) {
|
|
275
|
+
const items: ActionsMenuGroup['items'] = []
|
|
276
|
+
if (discuss) {
|
|
277
|
+
items.push({
|
|
278
|
+
id: 'discuss',
|
|
279
|
+
label: discuss.label,
|
|
280
|
+
icon: discuss.icon,
|
|
281
|
+
onClick: discuss.run,
|
|
282
|
+
})
|
|
283
|
+
}
|
|
284
|
+
if (href) {
|
|
285
|
+
items.push({
|
|
286
|
+
id: 'open-details',
|
|
287
|
+
label: openDetails?.label ?? 'Open Details',
|
|
288
|
+
icon: openDetails?.icon ?? <EyeIcon size={20} />,
|
|
289
|
+
// Main row → open the entity (same tab, soft Link nav).
|
|
290
|
+
href,
|
|
291
|
+
// Trailing 40px ↗ side-button → open in a new tab.
|
|
292
|
+
iconAction: {
|
|
293
|
+
icon: <ArrowRightUpIcon size={20} />,
|
|
294
|
+
'aria-label': 'Open in new tab',
|
|
295
|
+
href,
|
|
296
|
+
openInNewTab: true,
|
|
297
|
+
},
|
|
298
|
+
})
|
|
299
|
+
}
|
|
300
|
+
return items.length ? [{ items }] : undefined
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/** Title-case a status/priority token ("WAITING_ON_US" → "Waiting on us"). */
|
|
304
|
+
function formatStatusToken(token: string | undefined): string | undefined {
|
|
305
|
+
if (!token) return undefined
|
|
306
|
+
const lower = token.toLowerCase().replace(/_/g, ' ')
|
|
307
|
+
return lower.charAt(0).toUpperCase() + lower.slice(1)
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/** HubSpot ticket status → `MingoInfoCard` status pill (Closed→green,
|
|
311
|
+
* Open→amber, anything else→neutral grey). */
|
|
312
|
+
function hubspotStatusToVariant(
|
|
313
|
+
status: string | undefined,
|
|
314
|
+
): MingoInfoCardStatus['variant'] {
|
|
315
|
+
const s = (status || '').toUpperCase()
|
|
316
|
+
if (s === 'CLOSED') return 'success'
|
|
317
|
+
if (s === 'OPEN') return 'warning'
|
|
318
|
+
return 'grey'
|
|
319
|
+
}
|
|
320
|
+
|
|
186
321
|
function HubspotTicketChatCard({
|
|
187
322
|
chatRef,
|
|
188
323
|
isNewTab,
|
|
324
|
+
discuss,
|
|
189
325
|
}: {
|
|
190
326
|
chatRef: ChatRef
|
|
191
327
|
isNewTab: boolean
|
|
328
|
+
discuss?: CardDiscussAction
|
|
192
329
|
}) {
|
|
193
330
|
const status =
|
|
194
331
|
typeof chatRef.metadata?.status === 'string' ? (chatRef.metadata.status as string) : undefined
|
|
@@ -196,72 +333,69 @@ function HubspotTicketChatCard({
|
|
|
196
333
|
typeof chatRef.metadata?.statusLabel === 'string'
|
|
197
334
|
? (chatRef.metadata.statusLabel as string)
|
|
198
335
|
: undefined
|
|
199
|
-
const
|
|
200
|
-
typeof chatRef.metadata?.priority === 'string'
|
|
201
|
-
? (chatRef.metadata.priority as string)
|
|
202
|
-
: undefined
|
|
203
|
-
const customerCompany =
|
|
204
|
-
typeof chatRef.metadata?.customerCompany === 'string'
|
|
205
|
-
? (chatRef.metadata.customerCompany as string)
|
|
206
|
-
: undefined
|
|
207
|
-
const customerEmail =
|
|
208
|
-
typeof chatRef.metadata?.customerEmail === 'string'
|
|
209
|
-
? (chatRef.metadata.customerEmail as string)
|
|
210
|
-
: undefined
|
|
336
|
+
const statusText = statusLabel ?? formatStatusToken(status)
|
|
211
337
|
return (
|
|
212
|
-
<
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
customerEmail,
|
|
222
|
-
url: chatRef.url ?? null,
|
|
223
|
-
dateUpdated: chatRef.date ?? null,
|
|
224
|
-
}}
|
|
225
|
-
variant="compact"
|
|
338
|
+
<MingoInfoCard
|
|
339
|
+
title={chatRef.title}
|
|
340
|
+
description={chatRef.preview ?? undefined}
|
|
341
|
+
icon={<TagIcon size={24} />}
|
|
342
|
+
status={
|
|
343
|
+
statusText
|
|
344
|
+
? { label: statusText, variant: hubspotStatusToVariant(status) }
|
|
345
|
+
: undefined
|
|
346
|
+
}
|
|
226
347
|
anchorProps={buildAnchorProps(chatRef.url, isNewTab)}
|
|
348
|
+
menuGroups={cardMenuGroups(chatRef.url, discuss, {
|
|
349
|
+
label: 'Open Ticket Details',
|
|
350
|
+
icon: <TagIcon size={20} />,
|
|
351
|
+
})}
|
|
352
|
+
menuAriaLabel="Ticket actions"
|
|
227
353
|
/>
|
|
228
354
|
)
|
|
229
355
|
}
|
|
230
356
|
|
|
231
|
-
function SlackChatCard({
|
|
357
|
+
function SlackChatCard({
|
|
358
|
+
chatRef,
|
|
359
|
+
isNewTab,
|
|
360
|
+
discuss,
|
|
361
|
+
}: {
|
|
362
|
+
chatRef: ChatRef
|
|
363
|
+
isNewTab: boolean
|
|
364
|
+
discuss?: CardDiscussAction
|
|
365
|
+
}) {
|
|
232
366
|
const channelName =
|
|
233
367
|
typeof chatRef.metadata?.channelName === 'string'
|
|
234
|
-
? (chatRef.metadata.channelName as string)
|
|
368
|
+
? (chatRef.metadata.channelName as string).trim()
|
|
235
369
|
: undefined
|
|
370
|
+
// Prefix the channel (with a leading "#") onto the author title, e.g.
|
|
371
|
+
// "Pavlo Shylo · #general".
|
|
372
|
+
const channelPretty = channelName
|
|
373
|
+
? channelName.startsWith('#')
|
|
374
|
+
? channelName
|
|
375
|
+
: `#${channelName}`
|
|
376
|
+
: undefined
|
|
377
|
+
const title = channelPretty ? `${chatRef.title} · ${channelPretty}` : chatRef.title
|
|
236
378
|
return (
|
|
237
|
-
<
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
preview: chatRef.preview,
|
|
242
|
-
url: chatRef.url ?? null,
|
|
243
|
-
dateUpdated: chatRef.date ?? null,
|
|
244
|
-
channel: channelName,
|
|
245
|
-
}}
|
|
246
|
-
variant="compact"
|
|
379
|
+
<MingoInfoCard
|
|
380
|
+
title={title}
|
|
381
|
+
description={chatRef.preview ?? undefined}
|
|
382
|
+
icon={<SlackLogoGreyIcon size={24} />}
|
|
247
383
|
anchorProps={buildAnchorProps(chatRef.url, isNewTab)}
|
|
384
|
+
menuGroups={cardMenuGroups(chatRef.url, discuss)}
|
|
385
|
+
menuAriaLabel="Message actions"
|
|
248
386
|
/>
|
|
249
387
|
)
|
|
250
388
|
}
|
|
251
389
|
|
|
252
390
|
function DataRoomDocChatCard({
|
|
253
391
|
chatRef,
|
|
254
|
-
baseRoute,
|
|
255
|
-
chipBasePlatform,
|
|
256
392
|
isNewTab,
|
|
393
|
+
discuss,
|
|
257
394
|
}: {
|
|
258
395
|
chatRef: ChatRef
|
|
259
|
-
baseRoute?: string
|
|
260
|
-
chipBasePlatform?: string
|
|
261
396
|
isNewTab: boolean
|
|
397
|
+
discuss?: CardDiscussAction
|
|
262
398
|
}) {
|
|
263
|
-
const path =
|
|
264
|
-
typeof chatRef.metadata?.path === 'string' ? (chatRef.metadata.path as string) : undefined
|
|
265
399
|
// Provenance label. NEVER fall back to "Data room" — that would
|
|
266
400
|
// falsely label non-data-room content (openframe-docs, etc.) as
|
|
267
401
|
// private investor material when `sourceRepo` is missing or
|
|
@@ -269,58 +403,41 @@ function DataRoomDocChatCard({
|
|
|
269
403
|
// can't be misled about sensitivity / scope.
|
|
270
404
|
const badgeText = chatRef.sourceRepo ? getSourceLabel(chatRef.sourceRepo) : 'Document'
|
|
271
405
|
return (
|
|
272
|
-
<
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
path,
|
|
278
|
-
url: chatRef.url ?? null,
|
|
279
|
-
sourceRepo: chatRef.sourceRepo ?? null,
|
|
280
|
-
baseRoute,
|
|
281
|
-
chipBasePlatform,
|
|
282
|
-
}}
|
|
283
|
-
badgeText={badgeText}
|
|
406
|
+
<MingoInfoCard
|
|
407
|
+
title={chatRef.title}
|
|
408
|
+
description={chatRef.preview ?? undefined}
|
|
409
|
+
icon={<FileContentIcon size={24} />}
|
|
410
|
+
status={{ label: badgeText, variant: 'grey' }}
|
|
284
411
|
anchorProps={buildAnchorProps(chatRef.url, isNewTab)}
|
|
412
|
+
menuGroups={cardMenuGroups(chatRef.url, discuss)}
|
|
413
|
+
menuAriaLabel="Document actions"
|
|
285
414
|
/>
|
|
286
415
|
)
|
|
287
416
|
}
|
|
288
417
|
|
|
289
418
|
function GenericFinancialChatCard({
|
|
290
419
|
chatRef,
|
|
291
|
-
|
|
292
|
-
scheme,
|
|
420
|
+
icon,
|
|
293
421
|
isNewTab,
|
|
422
|
+
discuss,
|
|
294
423
|
}: {
|
|
295
424
|
chatRef: ChatRef
|
|
296
|
-
|
|
297
|
-
scheme: 'cyan' | 'warning' | 'success' | 'error' | 'default'
|
|
425
|
+
icon: React.ReactNode
|
|
298
426
|
isNewTab: boolean
|
|
427
|
+
discuss?: CardDiscussAction
|
|
299
428
|
}) {
|
|
300
|
-
const facts = Array.isArray(chatRef.metadata?.facts)
|
|
301
|
-
? (chatRef.metadata!.facts as Array<{ label: string; value: string }>)
|
|
302
|
-
: null
|
|
303
429
|
const subtitle =
|
|
304
430
|
typeof chatRef.metadata?.subtitle === 'string'
|
|
305
431
|
? (chatRef.metadata.subtitle as string)
|
|
306
|
-
:
|
|
307
|
-
const anchorProps: GenericEntityCardAnchorProps | undefined = buildAnchorProps(
|
|
308
|
-
chatRef.url,
|
|
309
|
-
isNewTab,
|
|
310
|
-
)
|
|
432
|
+
: undefined
|
|
311
433
|
return (
|
|
312
|
-
<
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
badge: { text: badge, scheme },
|
|
320
|
-
facts,
|
|
321
|
-
dateUpdated: chatRef.date ?? null,
|
|
322
|
-
}}
|
|
323
|
-
anchorProps={anchorProps}
|
|
434
|
+
<MingoInfoCard
|
|
435
|
+
title={chatRef.title}
|
|
436
|
+
description={subtitle}
|
|
437
|
+
icon={icon}
|
|
438
|
+
anchorProps={buildAnchorProps(chatRef.url, isNewTab)}
|
|
439
|
+
menuGroups={cardMenuGroups(chatRef.url, discuss)}
|
|
440
|
+
menuAriaLabel="Document actions"
|
|
324
441
|
/>
|
|
325
442
|
)
|
|
326
443
|
}
|
|
@@ -394,21 +511,29 @@ function ProductReleaseChatCard({
|
|
|
394
511
|
chatRef,
|
|
395
512
|
buildProps,
|
|
396
513
|
isNewTab,
|
|
514
|
+
discuss,
|
|
397
515
|
}: {
|
|
398
516
|
item: any
|
|
399
517
|
chatRef: ChatRef
|
|
400
518
|
buildProps: NonNullable<ChatCardDispatchExtras['buildProductReleaseCardProps']>
|
|
401
519
|
isNewTab: boolean
|
|
520
|
+
discuss?: CardDiscussAction
|
|
402
521
|
}) {
|
|
403
522
|
const releaseProps = buildProps(item)
|
|
523
|
+
const status: MingoInfoCardStatus = item?.version
|
|
524
|
+
? { label: String(item.version), variant: 'grey' }
|
|
525
|
+
: { label: 'Release', variant: 'grey' }
|
|
404
526
|
return (
|
|
405
|
-
<
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
{
|
|
411
|
-
|
|
527
|
+
<EntityMingoCard
|
|
528
|
+
title={item?.title ?? ''}
|
|
529
|
+
description={item?.summary || releaseProps.formattedDate || undefined}
|
|
530
|
+
cover={releaseProps.coverImage || undefined}
|
|
531
|
+
fallbackIcon={<Rocket02Icon size={24} />}
|
|
532
|
+
status={status}
|
|
533
|
+
chatRef={chatRef}
|
|
534
|
+
isNewTab={isNewTab}
|
|
535
|
+
discuss={discuss}
|
|
536
|
+
menuAriaLabel="Release actions"
|
|
412
537
|
/>
|
|
413
538
|
)
|
|
414
539
|
}
|
|
@@ -422,15 +547,391 @@ function CampaignChatCard({
|
|
|
422
547
|
item,
|
|
423
548
|
chatRef,
|
|
424
549
|
isNewTab,
|
|
550
|
+
discuss,
|
|
551
|
+
}: {
|
|
552
|
+
item: any
|
|
553
|
+
chatRef: ChatRef
|
|
554
|
+
isNewTab: boolean
|
|
555
|
+
discuss?: CardDiscussAction
|
|
556
|
+
}) {
|
|
557
|
+
// Mirror CampaignCardAdmin's compact meta — "date · N goals · Marketing
|
|
558
|
+
// campaign" — instead of a status pill (the original showed no status badge).
|
|
559
|
+
const goalsCount = Array.isArray(item?.goals) ? item.goals.length : 0
|
|
560
|
+
const meta = [
|
|
561
|
+
item?.start_date ? formatDateShort(item.start_date) : null,
|
|
562
|
+
goalsCount > 0 ? `${goalsCount} goal${goalsCount !== 1 ? 's' : ''}` : null,
|
|
563
|
+
'Marketing campaign',
|
|
564
|
+
]
|
|
565
|
+
.filter(Boolean)
|
|
566
|
+
.join(' · ')
|
|
567
|
+
return (
|
|
568
|
+
<EntityMingoCard
|
|
569
|
+
title={item?.name ?? ''}
|
|
570
|
+
description={meta || item?.description || undefined}
|
|
571
|
+
fallbackIcon={<Megaphone01Icon size={24} />}
|
|
572
|
+
chatRef={chatRef}
|
|
573
|
+
isNewTab={isNewTab}
|
|
574
|
+
discuss={discuss}
|
|
575
|
+
menuAriaLabel="Campaign actions"
|
|
576
|
+
/>
|
|
577
|
+
)
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Shared `MingoInfoCard` shell for fetch-mode entity cards. `cover` renders as
|
|
582
|
+
* the 40px image when present; otherwise `fallbackIcon` shows. Anchor + menu +
|
|
583
|
+
* alt are wired uniformly so each registry render only computes the per-type
|
|
584
|
+
* title / description / cover / icon / status.
|
|
585
|
+
*/
|
|
586
|
+
function EntityMingoCard({
|
|
587
|
+
title,
|
|
588
|
+
description,
|
|
589
|
+
cover,
|
|
590
|
+
fallbackIcon,
|
|
591
|
+
status,
|
|
592
|
+
chatRef,
|
|
593
|
+
isNewTab,
|
|
594
|
+
menuAriaLabel,
|
|
595
|
+
discuss,
|
|
596
|
+
}: {
|
|
597
|
+
title: React.ReactNode
|
|
598
|
+
description?: React.ReactNode
|
|
599
|
+
cover?: string
|
|
600
|
+
fallbackIcon: React.ReactNode
|
|
601
|
+
status?: MingoInfoCardStatus
|
|
602
|
+
chatRef: ChatRef
|
|
603
|
+
isNewTab: boolean
|
|
604
|
+
menuAriaLabel: string
|
|
605
|
+
discuss?: CardDiscussAction
|
|
606
|
+
}) {
|
|
607
|
+
return (
|
|
608
|
+
<MingoInfoCard
|
|
609
|
+
title={title}
|
|
610
|
+
description={description}
|
|
611
|
+
imageSrc={cover}
|
|
612
|
+
imageAlt={typeof title === 'string' ? title : undefined}
|
|
613
|
+
icon={fallbackIcon}
|
|
614
|
+
status={status}
|
|
615
|
+
anchorProps={buildAnchorProps(chatRef.url, isNewTab)}
|
|
616
|
+
menuGroups={cardMenuGroups(chatRef.url, discuss)}
|
|
617
|
+
menuAriaLabel={menuAriaLabel}
|
|
618
|
+
/>
|
|
619
|
+
)
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/** First non-empty cover URL: entity image → OG placeholder → none. */
|
|
623
|
+
function entityCover(image: unknown, ogPlaceholder?: string | null): string | undefined {
|
|
624
|
+
return (typeof image === 'string' && image) || ogPlaceholder || undefined
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/** Free-text status string → coloured status pill, reusing the shared
|
|
628
|
+
* `getStatusColorScheme` mapping (cyan/default collapse to neutral grey). */
|
|
629
|
+
function statusPill(status: unknown): MingoInfoCardStatus | undefined {
|
|
630
|
+
const s = typeof status === 'string' ? status.trim() : ''
|
|
631
|
+
if (!s) return undefined
|
|
632
|
+
const scheme = getStatusColorScheme(s)
|
|
633
|
+
const variant: MingoInfoCardStatus['variant'] =
|
|
634
|
+
scheme === 'success' || scheme === 'error' || scheme === 'warning' ? scheme : 'grey'
|
|
635
|
+
return { label: s, variant }
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/** Blog post → image card. Cover is `featured_image`, falling back to the
|
|
639
|
+
* host's OG placeholder, then to a newspaper glyph when neither exists. */
|
|
640
|
+
function BlogChatCard({
|
|
641
|
+
item,
|
|
642
|
+
chatRef,
|
|
643
|
+
isNewTab,
|
|
644
|
+
ogPlaceholder,
|
|
645
|
+
hasEmbeddedVideo,
|
|
646
|
+
discuss,
|
|
425
647
|
}: {
|
|
426
648
|
item: any
|
|
427
649
|
chatRef: ChatRef
|
|
428
650
|
isNewTab: boolean
|
|
651
|
+
ogPlaceholder?: string | null
|
|
652
|
+
hasEmbeddedVideo?: boolean
|
|
653
|
+
discuss?: CardDiscussAction
|
|
429
654
|
}) {
|
|
655
|
+
const category = Array.isArray(item?.categories)
|
|
656
|
+
? (item.categories.find((c: { name?: string }) => c && c.name)?.name as string | undefined)
|
|
657
|
+
: undefined
|
|
658
|
+
// A post with embedded video surfaces a yellow "Video" pill (mirrors the
|
|
659
|
+
// original BlogCard's compact video badge + the Figma `7741:26583` sample);
|
|
660
|
+
// otherwise the first category.
|
|
661
|
+
const status: MingoInfoCardStatus | undefined = hasEmbeddedVideo
|
|
662
|
+
? { label: 'Video', variant: 'primary' }
|
|
663
|
+
: category
|
|
664
|
+
? { label: category, variant: 'grey' }
|
|
665
|
+
: undefined
|
|
430
666
|
return (
|
|
431
|
-
<
|
|
432
|
-
|
|
433
|
-
|
|
667
|
+
<EntityMingoCard
|
|
668
|
+
title={item?.title ?? ''}
|
|
669
|
+
description={item?.summary ?? undefined}
|
|
670
|
+
cover={entityCover(item?.featured_image, ogPlaceholder)}
|
|
671
|
+
fallbackIcon={<NewspaperIcon size={24} />}
|
|
672
|
+
status={status}
|
|
673
|
+
chatRef={chatRef}
|
|
674
|
+
isNewTab={isNewTab}
|
|
675
|
+
discuss={discuss}
|
|
676
|
+
menuAriaLabel="Article actions"
|
|
677
|
+
/>
|
|
678
|
+
)
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/** Case study → trophy icon + "Case study" pill. */
|
|
682
|
+
function CaseStudyChatCard({
|
|
683
|
+
item,
|
|
684
|
+
chatRef,
|
|
685
|
+
isNewTab,
|
|
686
|
+
ogPlaceholder,
|
|
687
|
+
discuss,
|
|
688
|
+
}: {
|
|
689
|
+
item: any
|
|
690
|
+
chatRef: ChatRef
|
|
691
|
+
isNewTab: boolean
|
|
692
|
+
ogPlaceholder?: string | null
|
|
693
|
+
discuss?: CardDiscussAction
|
|
694
|
+
}) {
|
|
695
|
+
const meta = [item?.msp?.name, item?.user?.full_name].filter(Boolean).join(' · ')
|
|
696
|
+
return (
|
|
697
|
+
<EntityMingoCard
|
|
698
|
+
title={item?.title ?? ''}
|
|
699
|
+
description={item?.summary || meta || undefined}
|
|
700
|
+
cover={entityCover(item?.featured_image, ogPlaceholder)}
|
|
701
|
+
fallbackIcon={<TrophyIcon size={24} />}
|
|
702
|
+
status={{ label: 'Case study', variant: 'grey' }}
|
|
703
|
+
chatRef={chatRef}
|
|
704
|
+
isNewTab={isNewTab}
|
|
705
|
+
discuss={discuss}
|
|
706
|
+
menuAriaLabel="Case study actions"
|
|
707
|
+
/>
|
|
708
|
+
)
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/** Customer interview → microphone icon + "Interview" pill. */
|
|
712
|
+
function CustomerInterviewChatCard({
|
|
713
|
+
item,
|
|
714
|
+
chatRef,
|
|
715
|
+
isNewTab,
|
|
716
|
+
ogPlaceholder,
|
|
717
|
+
discuss,
|
|
718
|
+
}: {
|
|
719
|
+
item: any
|
|
720
|
+
chatRef: ChatRef
|
|
721
|
+
isNewTab: boolean
|
|
722
|
+
ogPlaceholder?: string | null
|
|
723
|
+
discuss?: CardDiscussAction
|
|
724
|
+
}) {
|
|
725
|
+
const meta = [item?.user?.full_name, item?.msp?.name].filter(Boolean).join(' · ')
|
|
726
|
+
return (
|
|
727
|
+
<EntityMingoCard
|
|
728
|
+
title={item?.title ?? ''}
|
|
729
|
+
// Original compact body reads `video_summary` (NOT `summary`); fall back
|
|
730
|
+
// to the "user · msp" meta line when absent.
|
|
731
|
+
description={item?.video_summary || meta || undefined}
|
|
732
|
+
cover={entityCover(item?.featured_image, ogPlaceholder)}
|
|
733
|
+
fallbackIcon={<MicrophoneIcon size={24} />}
|
|
734
|
+
status={{ label: 'Interview', variant: 'grey' }}
|
|
735
|
+
chatRef={chatRef}
|
|
736
|
+
isNewTab={isNewTab}
|
|
737
|
+
discuss={discuss}
|
|
738
|
+
menuAriaLabel="Interview actions"
|
|
739
|
+
/>
|
|
740
|
+
)
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
/** Investor update → presentation icon + "Investor update" pill. Title falls
|
|
744
|
+
* back to "Update #N" when the row has no explicit title. */
|
|
745
|
+
function InvestorUpdateChatCard({
|
|
746
|
+
item,
|
|
747
|
+
chatRef,
|
|
748
|
+
isNewTab,
|
|
749
|
+
ogPlaceholder,
|
|
750
|
+
discuss,
|
|
751
|
+
}: {
|
|
752
|
+
item: any
|
|
753
|
+
chatRef: ChatRef
|
|
754
|
+
isNewTab: boolean
|
|
755
|
+
ogPlaceholder?: string | null
|
|
756
|
+
discuss?: CardDiscussAction
|
|
757
|
+
}) {
|
|
758
|
+
const title = item?.title || `Update #${item?.update_number ?? '?'}`
|
|
759
|
+
return (
|
|
760
|
+
<EntityMingoCard
|
|
761
|
+
title={title}
|
|
762
|
+
// Original compact body reads `strategic_update || content` (NOT
|
|
763
|
+
// `summary`); fall back to the period text.
|
|
764
|
+
description={
|
|
765
|
+
item?.strategic_update ||
|
|
766
|
+
item?.content ||
|
|
767
|
+
formatInvestorUpdatePeriod(item?.period_start, item?.period_end) ||
|
|
768
|
+
undefined
|
|
769
|
+
}
|
|
770
|
+
cover={entityCover(item?.featured_image, ogPlaceholder)}
|
|
771
|
+
fallbackIcon={<PresentationLineIcon size={24} />}
|
|
772
|
+
status={{ label: 'Investor update', variant: 'grey' }}
|
|
773
|
+
chatRef={chatRef}
|
|
774
|
+
isNewTab={isNewTab}
|
|
775
|
+
discuss={discuss}
|
|
776
|
+
menuAriaLabel="Update actions"
|
|
777
|
+
/>
|
|
778
|
+
)
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/** Onboarding guide → compass icon + "Guide" pill. Cover prefers the featured
|
|
782
|
+
* image, then the video thumbnail / OG image. */
|
|
783
|
+
function OnboardingGuideChatCard({
|
|
784
|
+
item,
|
|
785
|
+
chatRef,
|
|
786
|
+
isNewTab,
|
|
787
|
+
ogPlaceholder,
|
|
788
|
+
discuss,
|
|
789
|
+
}: {
|
|
790
|
+
item: any
|
|
791
|
+
chatRef: ChatRef
|
|
792
|
+
isNewTab: boolean
|
|
793
|
+
ogPlaceholder?: string | null
|
|
794
|
+
discuss?: CardDiscussAction
|
|
795
|
+
}) {
|
|
796
|
+
const cover =
|
|
797
|
+
entityCover(item?.featured_image) ??
|
|
798
|
+
entityCover(item?.main_video_thumbnail) ??
|
|
799
|
+
entityCover(item?.og_image_url, ogPlaceholder)
|
|
800
|
+
return (
|
|
801
|
+
<EntityMingoCard
|
|
802
|
+
title={item?.title ?? ''}
|
|
803
|
+
description={item?.summary ?? item?.description ?? undefined}
|
|
804
|
+
cover={cover}
|
|
805
|
+
fallbackIcon={<CompassIcon size={24} />}
|
|
806
|
+
status={{ label: 'Guide', variant: 'grey' }}
|
|
807
|
+
chatRef={chatRef}
|
|
808
|
+
isNewTab={isNewTab}
|
|
809
|
+
discuss={discuss}
|
|
810
|
+
menuAriaLabel="Guide actions"
|
|
811
|
+
/>
|
|
812
|
+
)
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
/** Program (podcast / webinar / event) → per-type icon + type pill. Cover is
|
|
816
|
+
* the program `cover_url`. */
|
|
817
|
+
function ProgramChatCard({
|
|
818
|
+
item,
|
|
819
|
+
chatRef,
|
|
820
|
+
isNewTab,
|
|
821
|
+
configKey,
|
|
822
|
+
label,
|
|
823
|
+
ogPlaceholder,
|
|
824
|
+
discuss,
|
|
825
|
+
}: {
|
|
826
|
+
item: any
|
|
827
|
+
chatRef: ChatRef
|
|
828
|
+
isNewTab: boolean
|
|
829
|
+
configKey: 'podcast' | 'webinar' | 'event'
|
|
830
|
+
label: string
|
|
831
|
+
ogPlaceholder?: string | null
|
|
832
|
+
discuss?: CardDiscussAction
|
|
833
|
+
}) {
|
|
834
|
+
const icon =
|
|
835
|
+
configKey === 'webinar' ? (
|
|
836
|
+
<PresentationBarIcon size={24} />
|
|
837
|
+
) : configKey === 'event' ? (
|
|
838
|
+
<CalendarIcon size={24} />
|
|
839
|
+
) : (
|
|
840
|
+
<MicrophoneIcon size={24} />
|
|
841
|
+
)
|
|
842
|
+
|
|
843
|
+
// Rich meta line mirroring ProgramCard's compact subtitle: "date · <typeMeta>"
|
|
844
|
+
// where typeMeta is podcast duration / event location / webinar time·duration.
|
|
845
|
+
// The type label itself already lives in the status pill, so it's omitted here.
|
|
846
|
+
const isScheduled = item?.status === 'scheduled'
|
|
847
|
+
let typeMeta: string | undefined
|
|
848
|
+
if (
|
|
849
|
+
configKey === 'podcast' &&
|
|
850
|
+
typeof item?.duration_seconds === 'number' &&
|
|
851
|
+
item.duration_seconds > 0 &&
|
|
852
|
+
!isScheduled
|
|
853
|
+
) {
|
|
854
|
+
typeMeta = formatDurationCompact(item.duration_seconds)
|
|
855
|
+
} else if (
|
|
856
|
+
configKey === 'event' &&
|
|
857
|
+
typeof item?.location_name === 'string' &&
|
|
858
|
+
item.location_name.trim().length > 0
|
|
859
|
+
) {
|
|
860
|
+
typeMeta = item.location_name
|
|
861
|
+
} else if (configKey === 'webinar' && item?.start_at) {
|
|
862
|
+
const time = formatTimeWithTimezone(item.start_at, null)
|
|
863
|
+
const dur = formatDurationFromRange(item.start_at, item.end_at)
|
|
864
|
+
typeMeta = dur ? `${time} · ${dur}` : time
|
|
865
|
+
}
|
|
866
|
+
const itemDate = formatDate(item?.date ?? null, { fallback: '', timezone: 'local' })
|
|
867
|
+
const meta = [itemDate, typeMeta].filter(Boolean).join(' · ')
|
|
868
|
+
|
|
869
|
+
return (
|
|
870
|
+
<EntityMingoCard
|
|
871
|
+
title={item?.title ?? ''}
|
|
872
|
+
description={meta || item?.description || undefined}
|
|
873
|
+
cover={entityCover(item?.cover_url, ogPlaceholder)}
|
|
874
|
+
fallbackIcon={icon}
|
|
875
|
+
status={{ label, variant: 'grey' }}
|
|
876
|
+
chatRef={chatRef}
|
|
877
|
+
isNewTab={isNewTab}
|
|
878
|
+
discuss={discuss}
|
|
879
|
+
menuAriaLabel={`${label} actions`}
|
|
880
|
+
/>
|
|
881
|
+
)
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
/**
|
|
885
|
+
* Roadmap / delivery / task → coloured status pill, no cover image. Icon
|
|
886
|
+
* priority mirrors `RoadmapCard`:
|
|
887
|
+
* 1. task-type glyph for internal tasks (and logo-less typed items)
|
|
888
|
+
* 2. the server-provided integration logo (`item.icon`, ClickUp, …),
|
|
889
|
+
* shown contained inside the icon box
|
|
890
|
+
* 3. a per-type default glyph
|
|
891
|
+
*/
|
|
892
|
+
function RoadmapChatCard({
|
|
893
|
+
item,
|
|
894
|
+
chatRef,
|
|
895
|
+
isNewTab,
|
|
896
|
+
cardType,
|
|
897
|
+
discuss,
|
|
898
|
+
}: {
|
|
899
|
+
item: any
|
|
900
|
+
chatRef: ChatRef
|
|
901
|
+
isNewTab: boolean
|
|
902
|
+
cardType: 'roadmap_item' | 'delivery_item' | 'internal_task'
|
|
903
|
+
discuss?: CardDiscussAction
|
|
904
|
+
}) {
|
|
905
|
+
const logoUrl =
|
|
906
|
+
typeof item?.icon === 'string' && item.icon.startsWith('http') ? item.icon : undefined
|
|
907
|
+
const useTypeIcon = cardType === 'internal_task' || (!logoUrl && item?.customItemId != null)
|
|
908
|
+
const defaultIcon =
|
|
909
|
+
cardType === 'delivery_item' ? (
|
|
910
|
+
<TruckFastIcon size={24} />
|
|
911
|
+
) : cardType === 'internal_task' ? (
|
|
912
|
+
<CheckSquareIcon size={24} />
|
|
913
|
+
) : (
|
|
914
|
+
<MapIcon size={24} />
|
|
915
|
+
)
|
|
916
|
+
const icon = useTypeIcon ? (
|
|
917
|
+
<TaskTypeIcon customItemId={item?.customItemId} className="size-6" />
|
|
918
|
+
) : logoUrl ? (
|
|
919
|
+
// Contained (not full-bleed) so the integration logo keeps its aspect
|
|
920
|
+
// ratio inside the bordered icon box — matches RoadmapCard.
|
|
921
|
+
<img src={logoUrl} alt="" className="size-6 object-contain" />
|
|
922
|
+
) : (
|
|
923
|
+
defaultIcon
|
|
924
|
+
)
|
|
925
|
+
return (
|
|
926
|
+
<EntityMingoCard
|
|
927
|
+
title={item?.title ?? ''}
|
|
928
|
+
description={item?.description ?? undefined}
|
|
929
|
+
fallbackIcon={icon}
|
|
930
|
+
status={statusPill(item?.status)}
|
|
931
|
+
chatRef={chatRef}
|
|
932
|
+
isNewTab={isNewTab}
|
|
933
|
+
discuss={discuss}
|
|
934
|
+
menuAriaLabel="Roadmap actions"
|
|
434
935
|
/>
|
|
435
936
|
)
|
|
436
937
|
}
|
|
@@ -452,6 +953,9 @@ type ChatCardRegistryEntry =
|
|
|
452
953
|
label: string
|
|
453
954
|
contentRefType: string
|
|
454
955
|
displayAction?: boolean
|
|
956
|
+
/** Render the card alone — skip the `ChatCardWithDiscuss` "Ask" +
|
|
957
|
+
* provenance source row (mirrors the no-fetch `bareInline`). */
|
|
958
|
+
bareInline?: boolean
|
|
455
959
|
skeleton: () => React.ReactNode
|
|
456
960
|
render: (item: any, chatRef: ChatRef, opts: ChatCardRenderOptions) => React.ReactNode
|
|
457
961
|
/** Optional post-fetch URL synthesizer. When `chatRef.url` is null
|
|
@@ -466,32 +970,32 @@ type ChatCardRegistryEntry =
|
|
|
466
970
|
fallbackHref?: (item: any) => string | null
|
|
467
971
|
}
|
|
468
972
|
|
|
469
|
-
|
|
470
|
-
interface FinancialBadgeConfig {
|
|
973
|
+
interface FinancialCardConfig {
|
|
471
974
|
label: string
|
|
472
|
-
|
|
473
|
-
|
|
975
|
+
/** Per-type leading glyph — carries the type signal now that the
|
|
976
|
+
* badge pill is dropped. */
|
|
977
|
+
icon: () => React.ReactNode
|
|
474
978
|
}
|
|
475
|
-
const
|
|
476
|
-
financial_kpi: { label: 'Financial KPI',
|
|
477
|
-
cap_table: { label: 'Cap table entry',
|
|
478
|
-
profit_loss: { label: 'P&L period',
|
|
479
|
-
balance_sheet: { label: 'Balance sheet',
|
|
480
|
-
cash_flow: { label: 'Cash flow',
|
|
979
|
+
const FINANCIAL_CARD_CONFIGS: Record<string, FinancialCardConfig> = {
|
|
980
|
+
financial_kpi: { label: 'Financial KPI', icon: () => <ChartBar01VerIcon size={24} /> },
|
|
981
|
+
cap_table: { label: 'Cap table entry', icon: () => <ChartPieIcon size={24} /> },
|
|
982
|
+
profit_loss: { label: 'P&L period', icon: () => <MoneyBillDollarIcon size={24} /> },
|
|
983
|
+
balance_sheet: { label: 'Balance sheet', icon: () => <BankIcon size={24} /> },
|
|
984
|
+
cash_flow: { label: 'Cash flow', icon: () => <CoinsExchangeCurrencyIcon size={24} /> },
|
|
481
985
|
}
|
|
482
986
|
function financialRegistryEntries(): Record<string, ChatCardRegistryEntry> {
|
|
483
987
|
const out: Record<string, ChatCardRegistryEntry> = {}
|
|
484
|
-
for (const [docType, cfg] of Object.entries(
|
|
485
|
-
const scheme = cfg.scheme ?? 'default'
|
|
988
|
+
for (const [docType, cfg] of Object.entries(FINANCIAL_CARD_CONFIGS)) {
|
|
486
989
|
out[docType] = {
|
|
487
990
|
mode: 'no-fetch',
|
|
488
991
|
label: cfg.label,
|
|
992
|
+
bareInline: true,
|
|
489
993
|
render: (chatRef, opts) => (
|
|
490
994
|
<GenericFinancialChatCard
|
|
491
995
|
chatRef={chatRef}
|
|
492
|
-
|
|
493
|
-
scheme={scheme}
|
|
996
|
+
icon={cfg.icon()}
|
|
494
997
|
isNewTab={opts.isNewTab}
|
|
998
|
+
discuss={opts.discuss}
|
|
495
999
|
/>
|
|
496
1000
|
),
|
|
497
1001
|
}
|
|
@@ -517,8 +1021,10 @@ function githubRegistryEntries(): Record<string, ChatCardRegistryEntry> {
|
|
|
517
1021
|
out[docType] = {
|
|
518
1022
|
mode: 'no-fetch',
|
|
519
1023
|
label: cfg.label,
|
|
1024
|
+
bareInline: true,
|
|
520
1025
|
render: (chatRef, opts) => (
|
|
521
|
-
<GitHubChatCard chatRef={chatRef} kind={cfg.kind} isNewTab={opts.isNewTab}
|
|
1026
|
+
<GitHubChatCard chatRef={chatRef} kind={cfg.kind} isNewTab={opts.isNewTab}
|
|
1027
|
+
discuss={opts.discuss} />
|
|
522
1028
|
),
|
|
523
1029
|
}
|
|
524
1030
|
}
|
|
@@ -543,25 +1049,19 @@ function programRegistryEntries(): Record<string, ChatCardRegistryEntry> {
|
|
|
543
1049
|
mode: 'fetch',
|
|
544
1050
|
label: cfg.label,
|
|
545
1051
|
contentRefType: cfg.contentRefType,
|
|
1052
|
+
bareInline: true,
|
|
546
1053
|
skeleton: () => <ProgramCardSkeleton size="sm" />,
|
|
547
|
-
render: (item, chatRef, opts) =>
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
opts
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
size="sm"
|
|
559
|
-
href={chatRef.url ?? ''}
|
|
560
|
-
targetPlatform={chatRef.targetPlatform ?? null}
|
|
561
|
-
{...newTabAnchorAttrs(opts.isNewTab)}
|
|
562
|
-
/>
|
|
563
|
-
)
|
|
564
|
-
},
|
|
1054
|
+
render: (item, chatRef, opts) => (
|
|
1055
|
+
<ProgramChatCard
|
|
1056
|
+
item={item}
|
|
1057
|
+
chatRef={chatRef}
|
|
1058
|
+
isNewTab={opts.isNewTab}
|
|
1059
|
+
discuss={opts.discuss}
|
|
1060
|
+
configKey={cfg.configKey}
|
|
1061
|
+
label={cfg.configKey.charAt(0).toUpperCase() + cfg.configKey.slice(1)}
|
|
1062
|
+
ogPlaceholder={opts?.extras?.buildOgPlaceholderUrl?.(item?.title ?? '') ?? null}
|
|
1063
|
+
/>
|
|
1064
|
+
),
|
|
565
1065
|
}
|
|
566
1066
|
}
|
|
567
1067
|
return out
|
|
@@ -593,16 +1093,14 @@ function roadmapRegistryEntries(): Record<string, ChatCardRegistryEntry> {
|
|
|
593
1093
|
mode: 'fetch',
|
|
594
1094
|
label: cfg.label,
|
|
595
1095
|
contentRefType: cfg.contentRefType,
|
|
1096
|
+
bareInline: true,
|
|
596
1097
|
skeleton: () => <RoadmapCardSkeleton size="sm" />,
|
|
597
1098
|
render: (item, chatRef, opts) => (
|
|
598
|
-
<
|
|
1099
|
+
<RoadmapChatCard
|
|
599
1100
|
item={item}
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
{
|
|
603
|
-
userVote={null}
|
|
604
|
-
onVote={() => {}}
|
|
605
|
-
size="sm"
|
|
1101
|
+
chatRef={chatRef}
|
|
1102
|
+
isNewTab={opts.isNewTab}
|
|
1103
|
+
discuss={opts.discuss}
|
|
606
1104
|
cardType={cfg.cardType}
|
|
607
1105
|
/>
|
|
608
1106
|
),
|
|
@@ -617,53 +1115,56 @@ const CHAT_CARD_REGISTRY: Record<string, ChatCardRegistryEntry> = {
|
|
|
617
1115
|
slack_message: {
|
|
618
1116
|
mode: 'no-fetch',
|
|
619
1117
|
label: 'Slack message',
|
|
1118
|
+
bareInline: true,
|
|
620
1119
|
render: (chatRef, opts) => (
|
|
621
|
-
<SlackChatCard chatRef={chatRef} isNewTab={opts.isNewTab}
|
|
1120
|
+
<SlackChatCard chatRef={chatRef} isNewTab={opts.isNewTab}
|
|
1121
|
+
discuss={opts.discuss} />
|
|
622
1122
|
),
|
|
623
1123
|
},
|
|
624
1124
|
hubspot_ticket: {
|
|
625
1125
|
mode: 'no-fetch',
|
|
626
1126
|
label: 'HubSpot ticket',
|
|
1127
|
+
// `MingoInfoCard` is the whole card — no "Ask"/provenance source row.
|
|
1128
|
+
bareInline: true,
|
|
627
1129
|
render: (chatRef, opts) => (
|
|
628
|
-
<HubspotTicketChatCard chatRef={chatRef} isNewTab={opts.isNewTab}
|
|
1130
|
+
<HubspotTicketChatCard chatRef={chatRef} isNewTab={opts.isNewTab}
|
|
1131
|
+
discuss={opts.discuss} />
|
|
629
1132
|
),
|
|
630
1133
|
},
|
|
631
1134
|
hubspot_ticket_anon: {
|
|
632
1135
|
mode: 'no-fetch',
|
|
633
1136
|
label: 'HubSpot ticket (anon)',
|
|
1137
|
+
bareInline: true,
|
|
634
1138
|
render: (chatRef, opts) => (
|
|
635
|
-
<HubspotTicketChatCard chatRef={chatRef} isNewTab={opts.isNewTab}
|
|
1139
|
+
<HubspotTicketChatCard chatRef={chatRef} isNewTab={opts.isNewTab}
|
|
1140
|
+
discuss={opts.discuss} />
|
|
636
1141
|
),
|
|
637
1142
|
},
|
|
638
1143
|
hubspot_ticket_self: {
|
|
639
1144
|
mode: 'no-fetch',
|
|
640
1145
|
label: 'HubSpot ticket (self)',
|
|
1146
|
+
bareInline: true,
|
|
641
1147
|
render: (chatRef, opts) => (
|
|
642
|
-
<HubspotTicketChatCard chatRef={chatRef} isNewTab={opts.isNewTab}
|
|
1148
|
+
<HubspotTicketChatCard chatRef={chatRef} isNewTab={opts.isNewTab}
|
|
1149
|
+
discuss={opts.discuss} />
|
|
643
1150
|
),
|
|
644
1151
|
},
|
|
645
1152
|
data_room_doc: {
|
|
646
1153
|
mode: 'no-fetch',
|
|
647
1154
|
label: 'Data-room doc',
|
|
1155
|
+
bareInline: true,
|
|
648
1156
|
render: (chatRef, opts) => (
|
|
649
|
-
<DataRoomDocChatCard
|
|
650
|
-
|
|
651
|
-
baseRoute={opts?.baseRoute}
|
|
652
|
-
chipBasePlatform={opts?.chipBasePlatform}
|
|
653
|
-
isNewTab={opts.isNewTab}
|
|
654
|
-
/>
|
|
1157
|
+
<DataRoomDocChatCard chatRef={chatRef} isNewTab={opts.isNewTab}
|
|
1158
|
+
discuss={opts.discuss} />
|
|
655
1159
|
),
|
|
656
1160
|
},
|
|
657
1161
|
markdown: {
|
|
658
1162
|
mode: 'no-fetch',
|
|
659
1163
|
label: 'Doc page (markdown)',
|
|
1164
|
+
bareInline: true,
|
|
660
1165
|
render: (chatRef, opts) => (
|
|
661
|
-
<DataRoomDocChatCard
|
|
662
|
-
|
|
663
|
-
baseRoute={opts?.baseRoute}
|
|
664
|
-
chipBasePlatform={opts?.chipBasePlatform}
|
|
665
|
-
isNewTab={opts.isNewTab}
|
|
666
|
-
/>
|
|
1166
|
+
<DataRoomDocChatCard chatRef={chatRef} isNewTab={opts.isNewTab}
|
|
1167
|
+
discuss={opts.discuss} />
|
|
667
1168
|
),
|
|
668
1169
|
},
|
|
669
1170
|
video: {
|
|
@@ -679,15 +1180,15 @@ const CHAT_CARD_REGISTRY: Record<string, ChatCardRegistryEntry> = {
|
|
|
679
1180
|
mode: 'fetch',
|
|
680
1181
|
label: 'Blog post',
|
|
681
1182
|
contentRefType: 'blog_post_existing',
|
|
1183
|
+
bareInline: true,
|
|
682
1184
|
skeleton: () => <BlogCardSkeleton size="sm" />,
|
|
683
1185
|
render: (item, chatRef, opts) => (
|
|
684
|
-
<
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
{...newTabAnchorAttrs(opts.isNewTab)}
|
|
1186
|
+
<BlogChatCard
|
|
1187
|
+
item={item}
|
|
1188
|
+
chatRef={chatRef}
|
|
1189
|
+
isNewTab={opts.isNewTab}
|
|
1190
|
+
discuss={opts.discuss}
|
|
1191
|
+
ogPlaceholder={opts?.extras?.buildOgPlaceholderUrl?.(item?.title ?? '') ?? null}
|
|
691
1192
|
hasEmbeddedVideo={chatRef.metadata?.hasEmbeddedVideo === true}
|
|
692
1193
|
/>
|
|
693
1194
|
),
|
|
@@ -696,15 +1197,15 @@ const CHAT_CARD_REGISTRY: Record<string, ChatCardRegistryEntry> = {
|
|
|
696
1197
|
mode: 'fetch',
|
|
697
1198
|
label: 'Case study',
|
|
698
1199
|
contentRefType: 'case_study',
|
|
1200
|
+
bareInline: true,
|
|
699
1201
|
skeleton: () => <CaseStudyCardSkeleton size="sm" />,
|
|
700
1202
|
render: (item, chatRef, opts) => (
|
|
701
|
-
<
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
{...newTabAnchorAttrs(opts.isNewTab)}
|
|
1203
|
+
<CaseStudyChatCard
|
|
1204
|
+
item={item}
|
|
1205
|
+
chatRef={chatRef}
|
|
1206
|
+
isNewTab={opts.isNewTab}
|
|
1207
|
+
discuss={opts.discuss}
|
|
1208
|
+
ogPlaceholder={opts?.extras?.buildOgPlaceholderUrl?.(item?.title ?? '') ?? null}
|
|
708
1209
|
/>
|
|
709
1210
|
),
|
|
710
1211
|
},
|
|
@@ -712,15 +1213,15 @@ const CHAT_CARD_REGISTRY: Record<string, ChatCardRegistryEntry> = {
|
|
|
712
1213
|
mode: 'fetch',
|
|
713
1214
|
label: 'Customer interview',
|
|
714
1215
|
contentRefType: 'customer_interview',
|
|
1216
|
+
bareInline: true,
|
|
715
1217
|
skeleton: () => <CustomerInterviewCardSkeleton size="sm" />,
|
|
716
1218
|
render: (item, chatRef, opts) => (
|
|
717
|
-
<
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
{...newTabAnchorAttrs(opts.isNewTab)}
|
|
1219
|
+
<CustomerInterviewChatCard
|
|
1220
|
+
item={item}
|
|
1221
|
+
chatRef={chatRef}
|
|
1222
|
+
isNewTab={opts.isNewTab}
|
|
1223
|
+
discuss={opts.discuss}
|
|
1224
|
+
ogPlaceholder={opts?.extras?.buildOgPlaceholderUrl?.(item?.title ?? '') ?? null}
|
|
724
1225
|
/>
|
|
725
1226
|
),
|
|
726
1227
|
},
|
|
@@ -728,6 +1229,7 @@ const CHAT_CARD_REGISTRY: Record<string, ChatCardRegistryEntry> = {
|
|
|
728
1229
|
mode: 'fetch',
|
|
729
1230
|
label: 'Product release',
|
|
730
1231
|
contentRefType: 'product_release',
|
|
1232
|
+
bareInline: true,
|
|
731
1233
|
skeleton: () => <ProductReleaseCardSkeleton size="sm" />,
|
|
732
1234
|
render: (item, chatRef, opts) => {
|
|
733
1235
|
// Embedder-provided builder wins; lib default fills in otherwise
|
|
@@ -744,6 +1246,7 @@ const CHAT_CARD_REGISTRY: Record<string, ChatCardRegistryEntry> = {
|
|
|
744
1246
|
chatRef={chatRef}
|
|
745
1247
|
buildProps={builder}
|
|
746
1248
|
isNewTab={opts.isNewTab}
|
|
1249
|
+
discuss={opts.discuss}
|
|
747
1250
|
/>
|
|
748
1251
|
)
|
|
749
1252
|
},
|
|
@@ -753,15 +1256,15 @@ const CHAT_CARD_REGISTRY: Record<string, ChatCardRegistryEntry> = {
|
|
|
753
1256
|
mode: 'fetch',
|
|
754
1257
|
label: 'Investor update',
|
|
755
1258
|
contentRefType: 'investor_update',
|
|
1259
|
+
bareInline: true,
|
|
756
1260
|
skeleton: () => <InvestorUpdateCardSkeleton size="sm" />,
|
|
757
1261
|
render: (item, chatRef, opts) => (
|
|
758
|
-
<
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
{...newTabAnchorAttrs(opts.isNewTab)}
|
|
1262
|
+
<InvestorUpdateChatCard
|
|
1263
|
+
item={item}
|
|
1264
|
+
chatRef={chatRef}
|
|
1265
|
+
isNewTab={opts.isNewTab}
|
|
1266
|
+
discuss={opts.discuss}
|
|
1267
|
+
ogPlaceholder={opts?.extras?.buildOgPlaceholderUrl?.(item?.title ?? '') ?? null}
|
|
765
1268
|
/>
|
|
766
1269
|
),
|
|
767
1270
|
},
|
|
@@ -769,16 +1272,17 @@ const CHAT_CARD_REGISTRY: Record<string, ChatCardRegistryEntry> = {
|
|
|
769
1272
|
mode: 'fetch',
|
|
770
1273
|
label: 'Onboarding guide',
|
|
771
1274
|
contentRefType: 'onboarding_guide',
|
|
1275
|
+
bareInline: true,
|
|
1276
|
+
// "Display" affordance (verbatim guide body) instead of "Ask".
|
|
772
1277
|
displayAction: true,
|
|
773
1278
|
skeleton: () => <OnboardingGuideCardSkeleton size="sm" />,
|
|
774
1279
|
render: (item, chatRef, opts) => (
|
|
775
|
-
<
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
{...newTabAnchorAttrs(opts.isNewTab)}
|
|
1280
|
+
<OnboardingGuideChatCard
|
|
1281
|
+
item={item}
|
|
1282
|
+
chatRef={chatRef}
|
|
1283
|
+
isNewTab={opts.isNewTab}
|
|
1284
|
+
discuss={opts.discuss}
|
|
1285
|
+
ogPlaceholder={opts?.extras?.buildOgPlaceholderUrl?.(item?.title ?? '') ?? null}
|
|
782
1286
|
/>
|
|
783
1287
|
),
|
|
784
1288
|
},
|
|
@@ -786,6 +1290,7 @@ const CHAT_CARD_REGISTRY: Record<string, ChatCardRegistryEntry> = {
|
|
|
786
1290
|
mode: 'fetch',
|
|
787
1291
|
label: 'Marketing campaign',
|
|
788
1292
|
contentRefType: 'marketing_campaign',
|
|
1293
|
+
bareInline: true,
|
|
789
1294
|
skeleton: () => <CampaignCardAdminSkeleton />,
|
|
790
1295
|
// No public viewer — synthesize the hub-internal admin URL post-fetch
|
|
791
1296
|
// so the wrapper + isNewTab computation see the actual destination.
|
|
@@ -796,6 +1301,7 @@ const CHAT_CARD_REGISTRY: Record<string, ChatCardRegistryEntry> = {
|
|
|
796
1301
|
item={item}
|
|
797
1302
|
chatRef={chatRef}
|
|
798
1303
|
isNewTab={opts.isNewTab}
|
|
1304
|
+
discuss={opts.discuss}
|
|
799
1305
|
/>
|
|
800
1306
|
),
|
|
801
1307
|
},
|
|
@@ -992,7 +1498,36 @@ export function ChatCardLoader({
|
|
|
992
1498
|
// via `renderOpts.isNewTab` so the inner `<a>` agrees with the
|
|
993
1499
|
// runtime nav decision in `ChatCardNavWrap` + `handleChatNavClick`.
|
|
994
1500
|
const isNewTab = computeIsNewTab(runtime, finalChatRef.url, finalChatRef.targetPlatform ?? null)
|
|
995
|
-
|
|
1501
|
+
|
|
1502
|
+
// Resolve the "Ask Mingo"/"Display" affordance ONCE — same precedence as the
|
|
1503
|
+
// legacy `ChatCardWithDiscuss` (displayAction + onDisplay → "Display",
|
|
1504
|
+
// otherwise onDiscuss → "Ask Mingo"). Threaded into each card's "⋯" menu.
|
|
1505
|
+
const useDisplay = !!entry.displayAction && !!onDisplay
|
|
1506
|
+
const discussFn = useDisplay ? onDisplay : onDiscuss
|
|
1507
|
+
const discuss: CardDiscussAction | undefined = discussFn
|
|
1508
|
+
? {
|
|
1509
|
+
label: useDisplay ? 'Display' : 'Ask Mingo',
|
|
1510
|
+
icon: useDisplay ? (
|
|
1511
|
+
<FileContentIcon size={20} />
|
|
1512
|
+
) : (
|
|
1513
|
+
<MingoIcon
|
|
1514
|
+
className="size-5"
|
|
1515
|
+
color="white"
|
|
1516
|
+
eyesColor="var(--ods-flamingo-cyan-base)"
|
|
1517
|
+
cornerColor="var(--ods-flamingo-cyan-base)"
|
|
1518
|
+
/>
|
|
1519
|
+
),
|
|
1520
|
+
run: () => discussFn(finalChatRef),
|
|
1521
|
+
}
|
|
1522
|
+
: undefined
|
|
1523
|
+
|
|
1524
|
+
const renderOpts: ChatCardRenderOptions = {
|
|
1525
|
+
baseRoute,
|
|
1526
|
+
chipBasePlatform,
|
|
1527
|
+
extras,
|
|
1528
|
+
isNewTab,
|
|
1529
|
+
discuss,
|
|
1530
|
+
}
|
|
996
1531
|
|
|
997
1532
|
// Wrap EVERY rendered card with ChatCardNavWrap so the inner anchor's
|
|
998
1533
|
// primary click routes through the chat runtime (same handler as the
|
|
@@ -1051,6 +1586,9 @@ export function ChatCardLoader({
|
|
|
1051
1586
|
}
|
|
1052
1587
|
if (isLoading) return <>{entry.skeleton()}</>
|
|
1053
1588
|
if (!item) return null
|
|
1589
|
+
if (entry.bareInline) {
|
|
1590
|
+
return <>{navWrap(entry.render(item, finalChatRef, renderOpts))}</>
|
|
1591
|
+
}
|
|
1054
1592
|
return (
|
|
1055
1593
|
<ChatCardWithDiscuss
|
|
1056
1594
|
chatRef={finalChatRef}
|