Updates Apr3: new pages, components, and widespread UI/API improvements

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
belviskhoremk
2026-04-03 09:15:25 +00:00
parent d07111a4f2
commit 56ce9717aa
34 changed files with 6810 additions and 2291 deletions

View File

@@ -4,13 +4,16 @@ import { useNavigate, useLocation, Link } from 'react-router-dom'
import { billingAPI, authAPI } from '@/services/api'
import { useAuthStore } from '@/store/authStore'
import { Button, Card, Input } from '@/components/ui'
import { useToast } from '@/contexts/ToastContext'
import { useThemeStore } from '@/store/themeStore'
import { getPlanColor, formatDate } from '@/lib/utils'
import { CreditCard, User, ExternalLink, AlertTriangle } from 'lucide-react'
import { CreditCard, User, ExternalLink, AlertTriangle, Moon, Sun } from 'lucide-react'
export const SettingsPage: React.FC = () => {
const navigate = useNavigate()
const location = useLocation()
const [toast, setToast] = useState('')
const { success: showToast, error: showError } = useToast()
const { isDark, toggle: toggleTheme } = useThemeStore()
const tab: 'profile' | 'billing' = location.pathname === '/settings/billing' ? 'billing' : 'profile'
@@ -21,14 +24,19 @@ export const SettingsPage: React.FC = () => {
}
}, [navigate, location.pathname])
const showToast = (msg: string) => {
setToast(msg)
setTimeout(() => setToast(''), 3500)
}
return (
<div className="p-6 max-w-3xl mx-auto">
<h1 className="text-2xl font-bold text-gray-900 mb-6">Settings</h1>
<div className="flex items-center justify-between mb-6">
<h1 className="text-2xl font-bold text-gray-900">Settings</h1>
<button
onClick={toggleTheme}
className="flex items-center gap-2 px-3 py-2 rounded-lg border border-gray-200 hover:bg-gray-100 text-sm text-gray-600 transition-colors"
aria-label="Toggle dark mode"
>
{isDark ? <Sun className="w-4 h-4" /> : <Moon className="w-4 h-4" />}
{isDark ? 'Light mode' : 'Dark mode'}
</button>
</div>
{/* Tabs */}
<div className="flex gap-1 mb-6 bg-gray-100 p-1 rounded-xl w-fit">
@@ -51,20 +59,13 @@ export const SettingsPage: React.FC = () => {
))}
</div>
{tab === 'profile' && <ProfileSettings onToast={showToast} />}
{tab === 'billing' && <BillingSettings onToast={showToast} />}
{toast && (
<div className="fixed bottom-4 right-4 bg-gray-900 text-white px-4 py-2 rounded-lg text-sm shadow-lg z-50 animate-fade-in-up">
{toast}
<button onClick={() => setToast('')} className="ml-3 opacity-60 hover:opacity-100">&times;</button>
</div>
)}
{tab === 'profile' && <ProfileSettings onToast={showToast} onError={showError} />}
{tab === 'billing' && <BillingSettings onToast={showToast} onError={showError} />}
</div>
)
}
const ProfileSettings: React.FC<{ onToast: (msg: string) => void }> = ({ onToast }) => {
const ProfileSettings: React.FC<{ onToast: (msg: string) => void; onError: (msg: string) => void }> = ({ onToast, onError }) => {
const { user, setAuth, token, logout } = useAuthStore()
const navigate = useNavigate()
const [companyName, setCompanyName] = useState(user?.company_name || '')
@@ -95,7 +96,7 @@ const ProfileSettings: React.FC<{ onToast: (msg: string) => void }> = ({ onToast
onToast('Profile updated successfully')
} catch (err) {
const e = err as { response?: { data?: { detail?: string } } }
onToast(e.response?.data?.detail || 'Failed to update profile')
onError(e.response?.data?.detail || 'Failed to update profile')
} finally {
setSaving(false)
}
@@ -110,7 +111,7 @@ const ProfileSettings: React.FC<{ onToast: (msg: string) => void }> = ({ onToast
navigate('/')
} catch (err) {
const e = err as { response?: { data?: { detail?: string } } }
onToast(e.response?.data?.detail || 'Failed to delete account')
onError(e.response?.data?.detail || 'Failed to delete account')
setDeleting(false)
}
}
@@ -211,7 +212,7 @@ const ProfileSettings: React.FC<{ onToast: (msg: string) => void }> = ({ onToast
)
}
const BillingSettings: React.FC<{ onToast: (msg: string) => void }> = ({ onToast }) => {
const BillingSettings: React.FC<{ onToast: (msg: string) => void; onError: (msg: string) => void }> = ({ onError }) => {
const navigate = useNavigate()
const [loading, setLoading] = useState(false)
@@ -227,7 +228,7 @@ const BillingSettings: React.FC<{ onToast: (msg: string) => void }> = ({ onToast
window.location.href = url
} catch (err) {
const e = err as { response?: { data?: { detail?: string } } }
onToast(e.response?.data?.detail || 'Failed to open billing portal')
onError(e.response?.data?.detail || 'Failed to open billing portal')
} finally {
setLoading(false)
}