Skip to content

Commit b990773

Browse files
committed
improvement(billing): show the Chat plan card above the composer instead of redirecting on send
1 parent 2da47b9 commit b990773

15 files changed

Lines changed: 67 additions & 71 deletions

File tree

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
'use client'
2+
3+
import { CHAT_PLAN_REQUIRED_UPGRADE } from '@/lib/billing/chat-plan'
4+
import { PlanUsageLimitDisplay } from '@/app/workspace/[workspaceId]/home/components/message-content/components/special-tags'
5+
import { useChatPlanRequired } from '@/app/workspace/[workspaceId]/home/hooks/use-chat-plan-required'
6+
7+
/**
8+
* The upgrade card above the composer while the workspace's plan does not include Chat, the same
9+
* card run admission writes, so a free payer learns why Chat is held before sending. Render it
10+
* beside every `UserInput`, which disables send on the same verdict.
11+
*/
12+
export function ChatPlanNotice() {
13+
const chatPlanRequired = useChatPlanRequired()
14+
if (!chatPlanRequired) return null
15+
16+
return (
17+
<div className='mx-auto mb-2 w-full max-w-chat'>
18+
<PlanUsageLimitDisplay data={CHAT_PLAN_REQUIRED_UPGRADE} />
19+
</div>
20+
)
21+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { ChatPlanNotice } from './chat-plan-notice'

‎apps/sim/app/workspace/[workspaceId]/home/components/index.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export { ChatMessageAttachments } from './chat-message-attachments'
2+
export { ChatPlanNotice } from './chat-plan-notice'
23
export { ChatSurfaceProvider, useChatSurface } from './chat-surface-context'
34
export { ContextMentionIcon } from './context-mention-icon'
45
export { CreditsChip } from './credits-chip'

‎apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/index.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export {
2727
credentialTagHasVisibleCard,
2828
formatCredentialSubmissionMessage,
2929
isHttpUrl,
30+
PlanUsageLimitDisplay,
3031
parseCredentialSubmissionMessage,
3132
parseCredentialSubmissionProgress,
3233
parseCredentialTagBody,

‎apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3500,7 +3500,7 @@ interface PlanUsageLimitDisplayProps {
35003500
* The payer's plan limit, or a free plan that does not include Chat: the card links to where
35013501
* the plan or the limit can change, and a workspace's plan to its upgrade page.
35023502
*/
3503-
function PlanUsageLimitDisplay({ data }: PlanUsageLimitDisplayProps) {
3503+
export function PlanUsageLimitDisplay({ data }: PlanUsageLimitDisplayProps) {
35043504
const { data: session } = useSession()
35053505
const hostContext = useOptionalWorkspaceHostContext()
35063506
const organizationContext = useOptionalOrganizationContext()

‎apps/sim/app/workspace/[workspaceId]/home/components/mothership-chat/mothership-chat.tsx‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { inter } from '@/app/_styles/fonts/inter/inter'
3333
import { FindBar } from '@/app/workspace/[workspaceId]/components/find-bar'
3434
import { MessageActions } from '@/app/workspace/[workspaceId]/components/message-actions'
3535
import { ChatMessageAttachments } from '@/app/workspace/[workspaceId]/home/components/chat-message-attachments'
36+
import { ChatPlanNotice } from '@/app/workspace/[workspaceId]/home/components/chat-plan-notice'
3637
import { ChatSurfaceProvider } from '@/app/workspace/[workspaceId]/home/components/chat-surface-context'
3738
import {
3839
assistantMessageHasRenderableContent,
@@ -1033,6 +1034,7 @@ export function MothershipChat({
10331034
>
10341035
<div className={styles.footerInner}>
10351036
<ComputerUseActivity />
1037+
{!isLoading && <ChatPlanNotice />}
10361038
<QueuedMessages
10371039
messageQueue={messageQueue}
10381040
editingQueuedId={editingQueuedId}

‎apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx‎

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import { InputToolbar } from '@/app/workspace/[workspaceId]/home/components/user
3535
import type { ModelSelectorOptions } from '@/app/workspace/[workspaceId]/home/components/user-input/components/model-selector'
3636
import { usePromptHistory } from '@/app/workspace/[workspaceId]/home/components/user-input/hooks/use-prompt-history'
3737
import { handleMothershipAddContextEvent } from '@/app/workspace/[workspaceId]/home/components/user-input/mothership-context-event'
38-
import { useChatPlanGate } from '@/app/workspace/[workspaceId]/home/hooks/use-chat-plan-gate'
38+
import { useChatPlanRequired } from '@/app/workspace/[workspaceId]/home/hooks/use-chat-plan-required'
3939
import type {
4040
ChatMessage,
4141
FileAttachmentForApi,
@@ -316,10 +316,12 @@ const UserInputImpl = forwardRef<UserInputHandle, UserInputProps>(function UserI
316316
}, [editor.contexts])
317317

318318
const hasAdditionalAttachments = Boolean(attachments)
319+
const chatPlanRequired = useChatPlanRequired()
319320
const canSubmit =
320321
(editor.value.trim().length > 0 || hasFiles || hasAdditionalAttachments) &&
321322
!isSending &&
322-
!hasUploadingFiles
323+
!hasUploadingFiles &&
324+
!chatPlanRequired
323325

324326
/**
325327
* Sync the editor when the `defaultValue` prop changes post-mount — e.g.
@@ -508,9 +510,6 @@ const UserInputImpl = forwardRef<UserInputHandle, UserInputProps>(function UserI
508510
textareaRef.current?.focus()
509511
}
510512

511-
/** Refuses a send the workspace plan excludes here, before `onSubmit`, so the draft survives. */
512-
const stopSendWithoutChatPlan = useChatPlanGate()
513-
514513
/** Empties the text, chips, attachments, transcript, and the saved draft in one step. */
515514
const clearComposer = useCallback(() => {
516515
history.reset()
@@ -531,7 +530,7 @@ const UserInputImpl = forwardRef<UserInputHandle, UserInputProps>(function UserI
531530
}, [resetTranscript, history.reset])
532531

533532
const handleSubmit = useCallback(() => {
534-
if (stopSendWithoutChatPlan()) return
533+
if (chatPlanRequired) return
535534
const currentFiles = filesRef.current
536535
const currentEditor = editorRef.current
537536

@@ -557,7 +556,7 @@ const UserInputImpl = forwardRef<UserInputHandle, UserInputProps>(function UserI
557556
)
558557
if (accepted === false) return
559558
clearComposer()
560-
}, [stopSendWithoutChatPlan, onSubmit, clearComposer])
559+
}, [chatPlanRequired, onSubmit, clearComposer])
561560

562561
/**
563562
* Enter policy for the editor: mirror canSubmit's uploading guard (Enter

‎apps/sim/app/workspace/[workspaceId]/home/home.tsx‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import { useMarkMothershipChatRead } from '@/hooks/queries/mothership-chats'
4141
import { useOAuthReturnRouter } from '@/hooks/use-oauth-return'
4242
import type { ChatContext } from '@/stores/panel'
4343
import {
44+
ChatPlanNotice,
4445
ChatSurfaceProvider,
4546
CreditsChip,
4647
MothershipChat,
@@ -442,6 +443,7 @@ function HomeContent({ chatId, userName, userId }: HomeProps) {
442443
What should we get done{firstName ? `, ${firstName}` : ''}?
443444
</h1>
444445
<div ref={initialViewInputRef} className='relative w-full max-w-chat'>
446+
<ChatPlanNotice />
445447
<ChatSurfaceProvider
446448
userId={userId}
447449
onContextAdd={handleContextAdd}

‎apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat-plan-gate.ts‎

Lines changed: 0 additions & 43 deletions
This file was deleted.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
'use client'
2+
3+
import { useOptionalWorkspaceHostContext } from '@/app/workspace/[workspaceId]/providers/workspace-host-provider'
4+
5+
/**
6+
* Whether this workspace's payer may not use Chat, so the composer shows the upgrade card and
7+
* holds sends that run admission would refuse.
8+
*
9+
* The verdict is the host context's `features.chat`, which the server resolves with the same
10+
* check admission runs. A surface without a workspace host (an organization conversation), or a
11+
* context that predates the flag, is not held here; admission still answers it with the card.
12+
*/
13+
export function useChatPlanRequired(): boolean {
14+
return useOptionalWorkspaceHostContext()?.features?.chat === false
15+
}

0 commit comments

Comments
 (0)