Skip to content

Commit 5d9a36c

Browse files
KevenWMarkhamclaude
andcommitted
feat: comprehensive API key management system
Added complete API key management with 4 major features: 1. **Payment Integration** - Created PaymentModal component with credit packages - Bonus credits for larger purchases (5-35% savings) - Estimated usage calculations - Simulated payment flow (ready for Stripe integration) 2. **Real API Key Validation** - Enhanced validation with actual Gemini API test calls - Validates API key format and connectivity - Handles quota/rate limit errors gracefully - Provides clear error messages 3. **Balance Alerts** - BalanceAlert component shows low/critical balance warnings - Thresholds: $5 (low) and $1 (critical) - Animated critical alerts - Dismissable with auto-reset on config change 4. **Dual Mode Support** - Own API Key: Users provide their own Gemini key (free) - Paid Service: Users pay for API usage through our service - Seamless mode switching - Real-time balance tracking Components Added: - src/components/ApiKeySettings.tsx (enhanced) - src/components/PaymentModal.tsx - src/components/BalanceAlert.tsx Updated Files: - src/App.tsx - integrated all modals and alerts - src/services/geminiClient.ts - auto-loads from localStorage Business Features: - Transparent pricing display - Cost tracking already integrated - Ready for Stripe/PayPal integration - Professional payment UI 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent ed2e334 commit 5d9a36c

File tree

6 files changed

+935
-60
lines changed

6 files changed

+935
-60
lines changed

.claude/settings.local.json

Lines changed: 15 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,25 @@
11
{
22
"permissions": {
33
"allow": [
4-
"Bash(npm create:*)",
5-
"Bash(npm init:*)",
6-
"Bash(npm install:*)",
7-
"Bash(npx husky init)",
8-
"Bash(git init:*)",
4+
"Bash(powershell:*)",
5+
"Bash(npx asar:*)",
6+
"Bash(taskkill:*)",
7+
"Bash(npm run electron:dev:*)",
8+
"Bash(node -p:*)",
9+
"Bash(npx electron .)",
910
"Bash(cat:*)",
10-
"Bash(echo \"npx --no -- commitlint --edit $1\")",
11-
"Bash(.husky/commit-msg)",
12-
"Bash(gh:*)",
13-
"Bash(git remote add:*)",
11+
"Bash(./node_modules/electron/dist/electron.exe:*)",
1412
"Bash(git add:*)",
1513
"Bash(git commit:*)",
16-
"Bash(git push:*)",
17-
"Bash(npx tailwindcss:*)",
18-
"Bash(npm test:*)",
19-
"Bash(test -f:*)",
14+
"Bash(git push)",
15+
"Bash(gh run list:*)",
16+
"Bash(gh run watch:*)",
17+
"Bash(gh run download:*)",
18+
"Bash(explorer.exe \"Transcript Parser Installer\")",
19+
"Bash(wait)",
20+
"Bash(explorer.exe \"Transcript Parser v1.0.0 - Production Build\")",
2021
"Bash(npm run dev:*)",
21-
"Bash(timeout /t 5)",
22-
"Bash(npm test:coverage:*)",
23-
"Bash(npm run test:coverage:*)",
24-
"Bash(git reset:*)",
25-
"Bash(npm run test:*)",
26-
"Bash(npm run lint)",
27-
"Bash(npx playwright install:*)",
28-
"Bash(npx playwright test:*)",
29-
"Bash(npx msw:*)",
30-
"Bash(npx tsc:*)",
31-
"Bash(npm run build:*)",
32-
"Bash(npm run test:e2e:*)",
33-
"Bash(test:*)",
34-
"Bash(ffmpeg:*)",
35-
"Bash(git mv:*)",
36-
"Bash(npm uninstall:*)",
37-
"Bash(docker:*)",
38-
"Bash(docker-compose up:*)",
39-
"Bash(findstr:*)",
40-
"Bash(npm run db:generate:*)",
41-
"Bash(curl:*)",
42-
"Bash(netstat:*)",
43-
"Bash(npm run format:*)",
44-
"Bash(npx vite build)",
45-
"Bash(npx playwright:*)",
46-
"Bash(nul)",
47-
"Bash(unzip:*)",
48-
"Bash(copy:*)",
49-
"Bash(mkdir:*)",
50-
"Bash(echo:*)",
51-
"Bash(c:codetranscript-parserdocker-compose.yml)",
52-
"Bash(\"c:\\code\\transcript-parser\\docker-compose.yml\")",
53-
"Bash(.gitkeep)",
54-
"Bash(dir:*)",
55-
"Bash(ls:*)",
56-
"Bash(npm run preview:*)",
57-
"Bash(npx vercel --version)",
58-
"Bash(npx vercel:*)"
22+
"Bash($null)"
5923
],
6024
"deny": [],
6125
"ask": []

src/App.tsx

Lines changed: 66 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ import { Register } from '@/components/Register'
1010
import { TranscriptLibrary } from '@/components/TranscriptLibrary'
1111
import { CostSummaryModal } from '@/components/CostSummaryModal'
1212
import { VideoPlayerModal } from '@/components/VideoPlayerModal'
13+
import {
14+
ApiKeySettings,
15+
loadApiConfig,
16+
type ApiKeyConfig,
17+
} from '@/components/ApiKeySettings'
18+
import { BalanceAlert, shouldShowBalanceAlert } from '@/components/BalanceAlert'
1319
import { useTranscription } from '@/hooks/useTranscription'
1420
import { apiClient } from '@/services/apiClient'
1521
import { usageTracker } from '@/services/usageTracker'
@@ -31,6 +37,11 @@ function App() {
3137
const [authMode, setAuthMode] = useState<'login' | 'register'>('login')
3238
const [showLibrary, setShowLibrary] = useState(false)
3339
const [showCostSummary, setShowCostSummary] = useState(false)
40+
const [showApiSettings, setShowApiSettings] = useState(false)
41+
const [apiConfig, setApiConfig] = useState<ApiKeyConfig | null>(
42+
loadApiConfig()
43+
)
44+
const [balanceAlertDismissed, setBalanceAlertDismissed] = useState(false)
3445

3546
// Video upload state
3647
const [videoFile, setVideoFile] = useState<File | null>(null)
@@ -79,7 +90,7 @@ function App() {
7990
inputTokens: 5234,
8091
outputTokens: 3421,
8192
totalTokens: 8655,
82-
metadata: { duration: '3:45', fileSize: '15MB' }
93+
metadata: { duration: '3:45', fileSize: '15MB' },
8394
})
8495

8596
usageTracker.track({
@@ -89,7 +100,7 @@ function App() {
89100
inputTokens: 2735,
90101
outputTokens: 0,
91102
totalTokens: 2735,
92-
metadata: { speakers: 3 }
103+
metadata: { speakers: 3 },
93104
})
94105
}
95106
}
@@ -174,7 +185,10 @@ function App() {
174185
// Clear any existing video/transcription
175186
handleRemoveVideo()
176187
setShowLibrary(false)
177-
console.log('✅ Loaded transcript from library:', loadedTranscript.metadata.fileName)
188+
console.log(
189+
'✅ Loaded transcript from library:',
190+
loadedTranscript.metadata.fileName
191+
)
178192
}
179193

180194
// Show auth modal
@@ -183,7 +197,8 @@ function App() {
183197
<div
184198
className="min-h-screen flex items-center justify-center p-4"
185199
style={{
186-
background: 'linear-gradient(135deg, #f8fafc 0%, rgba(239, 246, 255, 0.5) 50%, rgba(250, 245, 255, 0.5) 100%)'
200+
background:
201+
'linear-gradient(135deg, #f8fafc 0%, rgba(239, 246, 255, 0.5) 50%, rgba(250, 245, 255, 0.5) 100%)',
187202
}}
188203
>
189204
<div className="w-full max-w-md">
@@ -210,7 +225,8 @@ function App() {
210225
<div
211226
className="min-h-screen p-4 sm:p-6 lg:p-8"
212227
style={{
213-
background: 'linear-gradient(135deg, #f8fafc 0%, rgba(239, 246, 255, 0.5) 50%, rgba(250, 245, 255, 0.5) 100%)'
228+
background:
229+
'linear-gradient(135deg, #f8fafc 0%, rgba(239, 246, 255, 0.5) 50%, rgba(250, 245, 255, 0.5) 100%)',
214230
}}
215231
>
216232
<div className="max-w-7xl mx-auto">
@@ -232,7 +248,8 @@ function App() {
232248
<div
233249
className="min-h-screen p-4 sm:p-6 lg:p-8"
234250
style={{
235-
background: 'linear-gradient(135deg, #f8fafc 0%, rgba(239, 246, 255, 0.5) 50%, rgba(250, 245, 255, 0.5) 100%)'
251+
background:
252+
'linear-gradient(135deg, #f8fafc 0%, rgba(239, 246, 255, 0.5) 50%, rgba(250, 245, 255, 0.5) 100%)',
236253
}}
237254
>
238255
<div className="max-w-7xl mx-auto">
@@ -261,6 +278,17 @@ function App() {
261278
)}
262279
</div>
263280
<div className="flex gap-2">
281+
<Button
282+
variant="outline"
283+
onClick={() => setShowApiSettings(true)}
284+
className={
285+
apiConfig
286+
? 'border-emerald-500 text-emerald-600 hover:bg-emerald-50'
287+
: 'border-red-500 text-red-600 hover:bg-red-50'
288+
}
289+
>
290+
{apiConfig ? '🔑 API Configured' : '⚠️ Configure API'}
291+
</Button>
264292
{!isAuthenticated ? (
265293
<Button onClick={() => setShowAuth(true)}>
266294
Login / Register
@@ -270,7 +298,10 @@ function App() {
270298
<Button variant="outline" onClick={() => setShowLibrary(true)}>
271299
My Transcripts
272300
</Button>
273-
<Button variant="outline" onClick={() => setShowCostSummary(true)}>
301+
<Button
302+
variant="outline"
303+
onClick={() => setShowCostSummary(true)}
304+
>
274305
Summary
275306
</Button>
276307
<Button variant="outline" onClick={handleLogout}>
@@ -320,7 +351,9 @@ function App() {
320351
<CostSummaryModal
321352
isOpen={showCostSummary}
322353
onClose={() => setShowCostSummary(false)}
323-
stats={usageTracker.getUserUsage(apiClient.getCurrentUser()?.id || 1)}
354+
stats={usageTracker.getUserUsage(
355+
apiClient.getCurrentUser()?.id || 1
356+
)}
324357
/>
325358
)}
326359

@@ -334,6 +367,31 @@ function App() {
334367
fileName={videoFile.name}
335368
/>
336369
)}
370+
371+
{/* API Key Settings Modal */}
372+
<ApiKeySettings
373+
isOpen={showApiSettings}
374+
onClose={() => setShowApiSettings(false)}
375+
onSave={config => {
376+
setApiConfig(config)
377+
// Reset dismissed flag when config changes
378+
setBalanceAlertDismissed(false)
379+
}}
380+
currentConfig={apiConfig || undefined}
381+
/>
382+
383+
{/* Balance Alert for Paid Mode */}
384+
{apiConfig?.mode === 'paid' &&
385+
shouldShowBalanceAlert(
386+
apiConfig.paidBalance || 0,
387+
balanceAlertDismissed
388+
) && (
389+
<BalanceAlert
390+
balance={apiConfig.paidBalance || 0}
391+
onAddCredit={() => setShowApiSettings(true)}
392+
onDismiss={() => setBalanceAlertDismissed(true)}
393+
/>
394+
)}
337395
</div>
338396
</div>
339397
)

0 commit comments

Comments
 (0)