feat(03-01): add loading spinners to Login, Register, and BudgetSetup submit buttons
- LoginPage: Spinner replaces button text during loading, min-w-[120px] prevents layout shift - RegisterPage: Spinner replaces button text during loading, min-w-[120px] prevents layout shift - BudgetSetup: Spinner replaces create button text during saving, min-w-[120px] prevents layout shift - All buttons remain disabled during loading/saving to prevent double-submit
This commit is contained in:
@@ -4,6 +4,7 @@ import { Card, CardContent, CardHeader, CardTitle, CardFooter } from '@/componen
|
|||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||||
|
import { Spinner } from '@/components/ui/spinner'
|
||||||
import { budgets as budgetsApi, type Budget } from '@/lib/api'
|
import { budgets as budgetsApi, type Budget } from '@/lib/api'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -89,8 +90,8 @@ export function BudgetSetup({ existingBudgets, onCreated, onCancel }: Props) {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
<CardFooter className="flex gap-2 justify-end">
|
<CardFooter className="flex gap-2 justify-end">
|
||||||
<Button variant="outline" onClick={onCancel}>{t('common.cancel')}</Button>
|
<Button variant="outline" onClick={onCancel}>{t('common.cancel')}</Button>
|
||||||
<Button onClick={handleCreate} disabled={saving || !name || !startDate || !endDate}>
|
<Button onClick={handleCreate} disabled={saving || !name || !startDate || !endDate} className="min-w-[120px]">
|
||||||
{t('common.create')}
|
{saving ? <Spinner /> : t('common.create')}
|
||||||
</Button>
|
</Button>
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { Button } from '@/components/ui/button'
|
|||||||
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
|
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
|
||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
import { Alert, AlertDescription } from '@/components/ui/alert'
|
import { Alert, AlertDescription } from '@/components/ui/alert'
|
||||||
|
import { Spinner } from '@/components/ui/spinner'
|
||||||
import { palette } from '@/lib/palette'
|
import { palette } from '@/lib/palette'
|
||||||
import type { AuthContext } from '@/hooks/useAuth'
|
import type { AuthContext } from '@/hooks/useAuth'
|
||||||
|
|
||||||
@@ -78,8 +79,8 @@ export function LoginPage({ auth: { login }, onToggle }: Props) {
|
|||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<Button type="submit" disabled={loading} className="w-full">
|
<Button type="submit" disabled={loading} className="w-full min-w-[120px]">
|
||||||
{t('auth.login')}
|
{loading ? <Spinner /> : t('auth.login')}
|
||||||
</Button>
|
</Button>
|
||||||
<a href="/api/auth/oidc" className="block">
|
<a href="/api/auth/oidc" className="block">
|
||||||
<Button type="button" variant="outline" className="w-full">
|
<Button type="button" variant="outline" className="w-full">
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { Button } from '@/components/ui/button'
|
|||||||
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
|
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
|
||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
import { Alert, AlertDescription } from '@/components/ui/alert'
|
import { Alert, AlertDescription } from '@/components/ui/alert'
|
||||||
|
import { Spinner } from '@/components/ui/spinner'
|
||||||
import { palette } from '@/lib/palette'
|
import { palette } from '@/lib/palette'
|
||||||
import type { AuthContext } from '@/hooks/useAuth'
|
import type { AuthContext } from '@/hooks/useAuth'
|
||||||
|
|
||||||
@@ -86,8 +87,8 @@ export function RegisterPage({ auth: { register }, onToggle }: Props) {
|
|||||||
required
|
required
|
||||||
minLength={8}
|
minLength={8}
|
||||||
/>
|
/>
|
||||||
<Button type="submit" disabled={loading} className="w-full">
|
<Button type="submit" disabled={loading} className="w-full min-w-[120px]">
|
||||||
{t('auth.register')}
|
{loading ? <Spinner /> : t('auth.register')}
|
||||||
</Button>
|
</Button>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
Reference in New Issue
Block a user