Skip to content

Commit 287f9fb

Browse files
committed
fix ui for copilot upgrade limit inline
1 parent 7129b3f commit 287f9fb

File tree

2 files changed

+72
-105
lines changed
  • apps/sim
    • app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/copilot-message/components
    • stores/panel/copilot

2 files changed

+72
-105
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,146 +1,113 @@
11
'use client'
22

33
import { useState } from 'react'
4+
import { ArrowRight, Loader2 } from 'lucide-react'
45
import { useParams, useRouter } from 'next/navigation'
5-
import { ArrowRight, Check, CreditCard, Loader2 } from 'lucide-react'
66
import { Button } from '@/components/emcn'
7-
import { useSubscriptionData, useUpdateUsageLimit } from '@/hooks/queries/subscription'
87
import { canEditUsageLimit } from '@/lib/billing/subscriptions/utils'
8+
import { useSubscriptionData, useUpdateUsageLimit } from '@/hooks/queries/subscription'
9+
import { useCopilotStore } from '@/stores/panel/copilot/store'
910

1011
/**
1112
* Component that displays actionable UI when a user hits their usage limit
12-
* Shows inline input to increase limit or button to upgrade plan
13+
* Shows button options to increase limit or button to upgrade plan
14+
* After updating limit, retries the original user query
1315
*/
1416
export function UsageLimitActions() {
1517
const router = useRouter()
1618
const params = useParams()
1719
const workspaceId = params?.workspaceId as string
1820
const { data: subscriptionData } = useSubscriptionData()
1921
const updateUsageLimitMutation = useUpdateUsageLimit()
20-
21-
const subscription = subscriptionData?.subscription
22+
23+
// The billing API returns { success, context, data }, where data contains plan/status
24+
const subscription = subscriptionData?.data
2225
const canEdit = subscription ? canEditUsageLimit(subscription) : false
23-
const currentLimit = subscriptionData?.data?.usage?.limit || 10
24-
const currentUsage = subscriptionData?.data?.usage?.current || 0
25-
26-
// Suggest an increase of at least $10 or enough to cover current usage + $5
27-
const suggestedIncrease = Math.max(10, Math.ceil(currentUsage - currentLimit + 5))
28-
const suggestedLimit = currentLimit + suggestedIncrease
29-
30-
const [newLimit, setNewLimit] = useState(suggestedLimit.toString())
31-
const [showSuccess, setShowSuccess] = useState(false)
32-
33-
const handleUpdateLimit = async () => {
34-
const limitValue = Number.parseFloat(newLimit)
35-
if (Number.isNaN(limitValue) || limitValue <= currentLimit) {
36-
return
37-
}
3826

27+
const [selectedAmount, setSelectedAmount] = useState<number | null>(null)
28+
const [isHidden, setIsHidden] = useState(false)
29+
30+
// Fixed limit options
31+
const limitOptions = [50, 100, 150]
32+
33+
const handleUpdateLimit = async (newLimit: number) => {
34+
setSelectedAmount(newLimit)
3935
try {
40-
await updateUsageLimitMutation.mutateAsync({ limit: limitValue })
41-
setShowSuccess(true)
42-
setTimeout(() => setShowSuccess(false), 3000)
36+
await updateUsageLimitMutation.mutateAsync({ limit: newLimit })
37+
38+
// Hide the buttons immediately
39+
setIsHidden(true)
40+
41+
// Get the store state and retry the last user message
42+
const { messages, sendMessage } = useCopilotStore.getState()
43+
44+
// Find the last user message (before the error)
45+
const lastUserMessage = [...messages].reverse().find((m) => m.role === 'user')
46+
47+
if (lastUserMessage) {
48+
// Remove the error message (assistant message with usage_limit error)
49+
const filteredMessages = messages.filter(
50+
(m) => !(m.role === 'assistant' && m.errorType === 'usage_limit')
51+
)
52+
53+
// Update messages to remove the error message
54+
useCopilotStore.setState({ messages: filteredMessages })
55+
56+
// Retry the original query by passing the same messageId
57+
// This replaces from that point instead of duplicating
58+
await sendMessage(lastUserMessage.content, {
59+
fileAttachments: lastUserMessage.fileAttachments,
60+
contexts: lastUserMessage.contexts,
61+
messageId: lastUserMessage.id,
62+
})
63+
}
4364
} catch (error) {
4465
// Error is handled by the mutation
66+
setIsHidden(false)
67+
} finally {
68+
setSelectedAmount(null)
4569
}
4670
}
4771

4872
const handleNavigateToUpgrade = () => {
4973
router.push(`/workspace/${workspaceId}/settings?tab=subscription`)
5074
}
5175

76+
// Hide if already processed
77+
if (isHidden) {
78+
return null
79+
}
80+
5281
if (!canEdit) {
5382
// Show upgrade button for users who can't edit (free/enterprise)
5483
return (
55-
<div className='mt-3 flex flex-col gap-2 rounded-lg border border-orange-200 bg-orange-50 p-3 dark:border-orange-800 dark:bg-orange-950'>
56-
<div className='flex items-start gap-2'>
57-
<CreditCard className='mt-0.5 h-4 w-4 flex-shrink-0 text-orange-600 dark:text-orange-400' />
58-
<div className='flex-1'>
59-
<p className='text-sm font-medium text-orange-900 dark:text-orange-100'>
60-
Usage Limit Reached
61-
</p>
62-
<p className='mt-1 text-xs text-orange-700 dark:text-orange-300'>
63-
Upgrade your plan to get higher limits and continue using the copilot.
64-
</p>
65-
</div>
66-
</div>
67-
<Button
68-
onClick={handleNavigateToUpgrade}
69-
className='w-full justify-between text-sm'
70-
variant='default'
71-
>
72-
<span>Upgrade Plan</span>
73-
<ArrowRight className='h-4 w-4' />
84+
<div className='mt-[12px] flex gap-[6px]'>
85+
<Button onClick={handleNavigateToUpgrade} variant='default'>
86+
Upgrade Plan
87+
<ArrowRight className='ml-1 h-3 w-3' />
7488
</Button>
7589
</div>
7690
)
7791
}
7892

79-
// Show inline edit for users who can edit their limit
93+
// Show button options for users who can edit their limit
8094
return (
81-
<div className='mt-3 flex flex-col gap-2 rounded-lg border border-orange-200 bg-orange-50 p-3 dark:border-orange-800 dark:bg-orange-950'>
82-
<div className='flex items-start gap-2'>
83-
<CreditCard className='mt-0.5 h-4 w-4 flex-shrink-0 text-orange-600 dark:text-orange-400' />
84-
<div className='flex-1'>
85-
<p className='text-sm font-medium text-orange-900 dark:text-orange-100'>
86-
Usage Limit Reached
87-
</p>
88-
<p className='mt-1 text-xs text-orange-700 dark:text-orange-300'>
89-
Current limit: ${currentLimit}. Increase your limit to continue.
90-
</p>
91-
</div>
92-
</div>
93-
94-
{showSuccess ? (
95-
<div className='flex items-center gap-2 rounded-md bg-green-100 px-3 py-2 dark:bg-green-900'>
96-
<Check className='h-4 w-4 text-green-700 dark:text-green-300' />
97-
<span className='text-sm text-green-800 dark:text-green-200'>
98-
Limit updated successfully!
99-
</span>
100-
</div>
101-
) : (
102-
<div className='flex gap-2'>
103-
<div className='relative flex-1'>
104-
<span className='pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-sm text-gray-600 dark:text-gray-400'>
105-
$
106-
</span>
107-
<input
108-
type='number'
109-
value={newLimit}
110-
onChange={(e) => setNewLimit(e.target.value)}
111-
min={currentLimit}
112-
step='1'
113-
className='h-9 w-full rounded-md border border-orange-300 bg-white pl-6 pr-3 text-sm text-gray-900 focus:border-orange-500 focus:outline-none focus:ring-1 focus:ring-orange-500 dark:border-orange-700 dark:bg-gray-800 dark:text-gray-100'
114-
placeholder={suggestedLimit.toString()}
115-
/>
116-
</div>
95+
<div className='mt-[12px] flex gap-[6px]'>
96+
{limitOptions.map((limit) => {
97+
const isLoading = updateUsageLimitMutation.isPending && selectedAmount === limit
98+
const isDisabled = updateUsageLimitMutation.isPending
99+
100+
return (
117101
<Button
118-
onClick={handleUpdateLimit}
119-
disabled={
120-
updateUsageLimitMutation.isPending ||
121-
Number.isNaN(Number.parseFloat(newLimit)) ||
122-
Number.parseFloat(newLimit) <= currentLimit
123-
}
124-
className='h-9 px-4 text-sm'
125-
variant='primary'
102+
key={limit}
103+
onClick={() => handleUpdateLimit(limit)}
104+
disabled={isDisabled}
105+
variant='default'
126106
>
127-
{updateUsageLimitMutation.isPending ? (
128-
<>
129-
<Loader2 className='mr-2 h-4 w-4 animate-spin' />
130-
Updating...
131-
</>
132-
) : (
133-
'Update Limit'
134-
)}
107+
{isLoading ? <Loader2 className='mr-1 h-3 w-3 animate-spin' /> : null}${limit}
135108
</Button>
136-
</div>
137-
)}
138-
139-
{updateUsageLimitMutation.isError && (
140-
<p className='text-xs text-red-700 dark:text-red-400'>
141-
{updateUsageLimitMutation.error?.message || 'Failed to update limit'}
142-
</p>
143-
)}
109+
)
110+
})}
144111
</div>
145112
)
146113
}

apps/sim/stores/panel/copilot/store.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2074,14 +2074,14 @@ export const useCopilotStore = create<CopilotStore>()(
20742074
| 'forbidden'
20752075
| 'rate_limit'
20762076
| 'upgrade_required'
2077-
| undefined = undefined
2077+
| undefined
20782078
if (result.status === 401) {
20792079
errorContent =
20802080
'_Unauthorized request. You need a valid API key to use the copilot. You can get one by going to [sim.ai](https://sim.ai) settings and generating one there._'
20812081
errorType = 'unauthorized'
20822082
} else if (result.status === 402) {
20832083
errorContent =
2084-
'_Usage limit exceeded. To continue using this service, upgrade your plan or top up on credits._'
2084+
'_Usage limit exceeded. To continue using this service, upgrade your plan or increase your usage limit to:_'
20852085
errorType = 'usage_limit'
20862086
} else if (result.status === 403) {
20872087
errorContent =

0 commit comments

Comments
 (0)