From 1c4c70728a8d10489d848e665eac5d19952ee214 Mon Sep 17 00:00:00 2001 From: Julian Gomez Date: Fri, 21 Nov 2025 12:14:01 -0600 Subject: [PATCH 1/3] Pro status tracker widget for home page --- .claude/settings.local.json | 3 +- app/components/home/HomeKit.tsx | 4 + .../home/SubscriptionStatusWidget.tsx | 94 +++++++++++++++++++ 3 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 app/components/home/SubscriptionStatusWidget.tsx 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 c364bceb..79f28889 100644 --- a/app/components/home/HomeKit.tsx +++ b/app/components/home/HomeKit.tsx @@ -19,6 +19,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() @@ -246,6 +247,9 @@ export default function HomeKit() {
{renderContent()}
+ + {/* Subscription Status Widget */} + ) } diff --git a/app/components/home/SubscriptionStatusWidget.tsx b/app/components/home/SubscriptionStatusWidget.tsx new file mode 100644 index 00000000..d959b061 --- /dev/null +++ b/app/components/home/SubscriptionStatusWidget.tsx @@ -0,0 +1,94 @@ +import useBillingState from '@/app/hooks/useBillingState' +import { useMainStore } from '@/app/store/useMainStore' + +interface SubscriptionStatusWidgetProps { + wordsUsed?: number +} + +export function SubscriptionStatusWidget({ + wordsUsed = 1000, +}: SubscriptionStatusWidgetProps) { + const billingState = useBillingState() + const { setCurrentPage, setSettingsPage } = useMainStore() + + const handleUpgradeClick = () => { + setCurrentPage('settings') + setSettingsPage('pricing-billing') + } + + // Hide widget for active Pro subscribers (not on trial) + if (billingState.proStatus === 'active_pro' && !billingState.isTrialActive) { + return null + } + + // Show trial status if user is on trial + if (billingState.isTrialActive && billingState.daysLeft > 0) { + return ( +
+
+ {/* Header with pink dot indicator */} +
+
+
Pro Trial Active
+
+ + {/* Days remaining */} +
+ {billingState.daysLeft} day{billingState.daysLeft !== 1 ? 's' : ''}{' '} + left on Ito Pro +
+ + {/* Upgrade button */} + +
+
+ ) + } + + // Show free tier status (Ito Starter) + const totalWords = 5000 + 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 */} + +
+
+ ) +} From 65b3ce5d40273970c292d9c0820a57e507cb25e4 Mon Sep 17 00:00:00 2001 From: Julian Gomez Date: Mon, 24 Nov 2025 14:01:56 -0600 Subject: [PATCH 2/3] finished --- app/components/home/HomeKit.tsx | 6 +- .../home/SubscriptionStatusWidget.tsx | 149 +++++++++++------- 2 files changed, 98 insertions(+), 57 deletions(-) diff --git a/app/components/home/HomeKit.tsx b/app/components/home/HomeKit.tsx index 74b20e06..a5adbf4c 100644 --- a/app/components/home/HomeKit.tsx +++ b/app/components/home/HomeKit.tsx @@ -232,15 +232,15 @@ export default function HomeKit() { />
+ + {/* Subscription Status Widget */} + {/* Main Content */}
{renderContent()}
- - {/* Subscription Status Widget */} - ) } diff --git a/app/components/home/SubscriptionStatusWidget.tsx b/app/components/home/SubscriptionStatusWidget.tsx index d959b061..de5b9e9c 100644 --- a/app/components/home/SubscriptionStatusWidget.tsx +++ b/app/components/home/SubscriptionStatusWidget.tsx @@ -1,14 +1,33 @@ -import useBillingState from '@/app/hooks/useBillingState' +import useBillingState, { + BillingState, + ProStatus, +} from '@/app/hooks/useBillingState' import { useMainStore } from '@/app/store/useMainStore' interface SubscriptionStatusWidgetProps { wordsUsed?: number + navExpanded?: boolean +} + +const mockBillingState: BillingState = { + proStatus: ProStatus.FREE_TRIAL, + subscriptionStartAt: null, + subscriptionEndAt: null, + isScheduledForCancellation: false, + trialDays: 14, + trialStartAt: null, + daysLeft: 1, + isTrialActive: true, + hasCompletedTrial: false, } export function SubscriptionStatusWidget({ wordsUsed = 1000, + navExpanded = true, }: SubscriptionStatusWidgetProps) { - const billingState = useBillingState() + // const billingState = useBillingState() + const billingState = mockBillingState as BillingState // Use mock for testing + console.log({ billingState }) const { setCurrentPage, setSettingsPage } = useMainStore() const handleUpgradeClick = () => { @@ -16,79 +35,101 @@ export function SubscriptionStatusWidget({ setSettingsPage('pricing-billing') } - // Hide widget for active Pro subscribers (not on trial) - if (billingState.proStatus === 'active_pro' && !billingState.isTrialActive) { + // 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 } - // Show trial status if user is on trial - if (billingState.isTrialActive && billingState.daysLeft > 0) { - return ( -
-
- {/* Header with pink dot indicator */} -
-
-
Pro Trial Active
-
- - {/* Days remaining */} -
- {billingState.daysLeft} day{billingState.daysLeft !== 1 ? 's' : ''}{' '} - left on Ito Pro -
- - {/* Upgrade button */} - -
-
- ) + // Hide widget for active Pro subscribers + if (billingState.proStatus === ProStatus.ACTIVE_PRO) { + return null } - // Show free tier status (Ito Starter) - const totalWords = 5000 - const usagePercentage = Math.min(100, (wordsUsed / totalWords) * 100) + // Show trial status if user is on free trial + if (billingState.proStatus === ProStatus.FREE_TRIAL) { + const daysUsed = billingState.trialDays - billingState.daysLeft + const trialPercentage = Math.min( + 100, + (daysUsed / billingState.trialDays) * 100, + ) - return ( -
-
+ return ( +
{/* Header */} -
Your plan
-
Ito Starter
+
Pro Trial Active
{/* Progress bar */} -
+
- {/* Usage text */} -
- You've used{' '} - - {wordsUsed.toLocaleString()} of {totalWords.toLocaleString()} - {' '} - words this week + {/* Days remaining */} +
+ {billingState.daysLeft} day{billingState.daysLeft !== 1 ? 's' : ''}{' '} + left on Ito Pro
{/* Upgrade button */}
+ ) + } + + // Show free tier status (Ito Starter) + const totalWords = 5000 + 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 */} +
) } From 67d07dddfc855349ab307c59a43d77314f788e7c Mon Sep 17 00:00:00 2001 From: Julian Gomez Date: Mon, 24 Nov 2025 15:19:50 -0600 Subject: [PATCH 3/3] cleanup --- app/components/home/HomeKit.tsx | 1 - .../home/SubscriptionStatusWidget.tsx | 24 ++++--------------- 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/app/components/home/HomeKit.tsx b/app/components/home/HomeKit.tsx index a5adbf4c..679f64dc 100644 --- a/app/components/home/HomeKit.tsx +++ b/app/components/home/HomeKit.tsx @@ -233,7 +233,6 @@ export default function HomeKit() {
- {/* Subscription Status Widget */}
diff --git a/app/components/home/SubscriptionStatusWidget.tsx b/app/components/home/SubscriptionStatusWidget.tsx index de5b9e9c..5589325f 100644 --- a/app/components/home/SubscriptionStatusWidget.tsx +++ b/app/components/home/SubscriptionStatusWidget.tsx @@ -9,25 +9,13 @@ interface SubscriptionStatusWidgetProps { navExpanded?: boolean } -const mockBillingState: BillingState = { - proStatus: ProStatus.FREE_TRIAL, - subscriptionStartAt: null, - subscriptionEndAt: null, - isScheduledForCancellation: false, - trialDays: 14, - trialStartAt: null, - daysLeft: 1, - isTrialActive: true, - hasCompletedTrial: false, -} +const FREE_TIER_WORD_LIMIT = 5000 export function SubscriptionStatusWidget({ wordsUsed = 1000, navExpanded = true, }: SubscriptionStatusWidgetProps) { - // const billingState = useBillingState() - const billingState = mockBillingState as BillingState // Use mock for testing - console.log({ billingState }) + const billingState = useBillingState() const { setCurrentPage, setSettingsPage } = useMainStore() const handleUpgradeClick = () => { @@ -58,10 +46,8 @@ export function SubscriptionStatusWidget({ // Show trial status if user is on free trial if (billingState.proStatus === ProStatus.FREE_TRIAL) { const daysUsed = billingState.trialDays - billingState.daysLeft - const trialPercentage = Math.min( - 100, - (daysUsed / billingState.trialDays) * 100, - ) + const trialDays = billingState.trialDays || 1 + const trialPercentage = Math.min(100, (daysUsed / trialDays) * 100) return (
@@ -94,7 +80,7 @@ export function SubscriptionStatusWidget({ } // Show free tier status (Ito Starter) - const totalWords = 5000 + const totalWords = FREE_TIER_WORD_LIMIT const usagePercentage = Math.min(100, (wordsUsed / totalWords) * 100) return (