diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 0a57fb17..845b5ffa 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -19,7 +19,8 @@ "Bash(cargo clean:*)", "Bash(awk:*)", "WebFetch(domain:connectrpc.com)", - "Bash(git log:*)" + "Bash(git log:*)", + "Bash(bunx tsc:*)" ], "deny": [], "ask": [] diff --git a/app/components/home/HomeKit.tsx b/app/components/home/HomeKit.tsx index 300e8bda..679f64dc 100644 --- a/app/components/home/HomeKit.tsx +++ b/app/components/home/HomeKit.tsx @@ -17,6 +17,7 @@ import DictionaryContent from './contents/DictionaryContent' import NotesContent from './contents/NotesContent' import SettingsContent from './contents/SettingsContent' import AboutContent from './contents/AboutContent' +import { SubscriptionStatusWidget } from './SubscriptionStatusWidget' export default function HomeKit() { const { navExpanded, currentPage, setCurrentPage } = useMainStore() @@ -231,6 +232,8 @@ export default function HomeKit() { /> + + {/* Main Content */} diff --git a/app/components/home/SubscriptionStatusWidget.tsx b/app/components/home/SubscriptionStatusWidget.tsx new file mode 100644 index 00000000..5589325f --- /dev/null +++ b/app/components/home/SubscriptionStatusWidget.tsx @@ -0,0 +1,121 @@ +import useBillingState, { + BillingState, + ProStatus, +} from '@/app/hooks/useBillingState' +import { useMainStore } from '@/app/store/useMainStore' + +interface SubscriptionStatusWidgetProps { + wordsUsed?: number + navExpanded?: boolean +} + +const FREE_TIER_WORD_LIMIT = 5000 + +export function SubscriptionStatusWidget({ + wordsUsed = 1000, + navExpanded = true, +}: SubscriptionStatusWidgetProps) { + const billingState = useBillingState() + const { setCurrentPage, setSettingsPage } = useMainStore() + + const handleUpgradeClick = () => { + setCurrentPage('settings') + setSettingsPage('pricing-billing') + } + + // Common styles + const cardClassName = + 'w-full bg-white rounded-2xl border-2 border-neutral-100 shadow-sm p-2 space-y-1.5' + const progressBarContainerClassName = + 'w-full h-1.5 bg-neutral-200 rounded-full overflow-hidden' + const progressBarFillClassName = + 'h-full transition-all duration-300 bg-gradient-to-r' + const buttonBaseClassName = + 'w-full text-white px-4 py-2.5 rounded-md text-sm font-semibold hover:bg-gray-800 cursor-pointer transition-colors mt-4' + + // Hide widget when sidebar is collapsed + if (!navExpanded) { + return null + } + + // Hide widget for active Pro subscribers + if (billingState.proStatus === ProStatus.ACTIVE_PRO) { + return null + } + + // Show trial status if user is on free trial + if (billingState.proStatus === ProStatus.FREE_TRIAL) { + const daysUsed = billingState.trialDays - billingState.daysLeft + const trialDays = billingState.trialDays || 1 + const trialPercentage = Math.min(100, (daysUsed / trialDays) * 100) + + return ( +
+ {/* Header */} +
Pro Trial Active
+ + {/* Progress bar */} +
+
+
+ + {/* Days remaining */} +
+ {billingState.daysLeft} day{billingState.daysLeft !== 1 ? 's' : ''}{' '} + left on Ito Pro +
+ + {/* Upgrade button */} + +
+ ) + } + + // Show free tier status (Ito Starter) + const totalWords = FREE_TIER_WORD_LIMIT + const usagePercentage = Math.min(100, (wordsUsed / totalWords) * 100) + + return ( +
+ {/* Header */} +
Your plan
+
Ito Starter
+ + {/* Progress bar */} +
+
+
+ + {/* Usage text */} +
+ You've used{' '} + + {wordsUsed.toLocaleString()} of {totalWords.toLocaleString()} + {' '} + words this week +
+ + {/* Upgrade button */} + +
+ ) +}