LoginPage.tsx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. import { useState } from 'react';
  2. import { useNavigate } from 'react-router-dom';
  3. import { useMutation } from '@tanstack/react-query';
  4. import { useAuth } from '../contexts/AuthContext';
  5. import { useToast } from '../contexts/ToastContext';
  6. import { useTheme } from '../contexts/ThemeContext';
  7. export function LoginPage() {
  8. const navigate = useNavigate();
  9. const { login } = useAuth();
  10. const { showToast } = useToast();
  11. const { mode } = useTheme();
  12. const [username, setUsername] = useState('');
  13. const [password, setPassword] = useState('');
  14. const loginMutation = useMutation({
  15. mutationFn: () => login(username, password),
  16. onSuccess: () => {
  17. showToast('Logged in successfully');
  18. navigate('/');
  19. },
  20. onError: (error: Error) => {
  21. showToast(error.message || 'Login failed', 'error');
  22. },
  23. });
  24. const handleSubmit = (e: React.FormEvent) => {
  25. e.preventDefault();
  26. if (!username || !password) {
  27. showToast('Please enter username and password', 'error');
  28. return;
  29. }
  30. loginMutation.mutate();
  31. };
  32. return (
  33. <div className="min-h-screen flex items-center justify-center bg-bambu-dark p-4">
  34. <div className="max-w-md w-full space-y-8 p-8 bg-gradient-to-br from-bambu-card to-bambu-dark-secondary rounded-xl border border-bambu-dark-tertiary shadow-lg">
  35. <div className="text-center">
  36. <div className="flex items-center justify-center mb-6">
  37. <img
  38. src={mode === 'dark' ? '/img/bambuddy_logo_dark_transparent.png' : '/img/bambuddy_logo_light.png'}
  39. alt="Bambuddy"
  40. className="h-16"
  41. />
  42. </div>
  43. <h2 className="text-3xl font-bold text-white">
  44. Bambuddy Login
  45. </h2>
  46. <p className="mt-2 text-sm text-bambu-gray">
  47. Sign in to your account
  48. </p>
  49. </div>
  50. <form className="mt-8 space-y-6" onSubmit={handleSubmit}>
  51. <div className="space-y-4">
  52. <div>
  53. <label htmlFor="username" className="block text-sm font-medium text-white mb-2">
  54. Username
  55. </label>
  56. <input
  57. id="username"
  58. type="text"
  59. required
  60. value={username}
  61. onChange={(e) => setUsername(e.target.value)}
  62. className="block w-full px-4 py-3 bg-bambu-dark-secondary border border-bambu-dark-tertiary rounded-lg text-white placeholder-bambu-gray focus:outline-none focus:ring-2 focus:ring-bambu-green/50 focus:border-bambu-green transition-colors"
  63. placeholder="Enter your username"
  64. autoComplete="username"
  65. />
  66. </div>
  67. <div>
  68. <label htmlFor="password" className="block text-sm font-medium text-white mb-2">
  69. Password
  70. </label>
  71. <input
  72. id="password"
  73. type="password"
  74. required
  75. value={password}
  76. onChange={(e) => setPassword(e.target.value)}
  77. className="block w-full px-4 py-3 bg-bambu-dark-secondary border border-bambu-dark-tertiary rounded-lg text-white placeholder-bambu-gray focus:outline-none focus:ring-2 focus:ring-bambu-green/50 focus:border-bambu-green transition-colors"
  78. placeholder="Enter your password"
  79. autoComplete="current-password"
  80. />
  81. </div>
  82. </div>
  83. <div>
  84. <button
  85. type="submit"
  86. disabled={loginMutation.isPending}
  87. className="w-full flex justify-center py-3 px-4 bg-bambu-green hover:bg-bambu-green-light text-white font-medium rounded-lg shadow-lg shadow-bambu-green/20 hover:shadow-bambu-green/30 focus:outline-none focus:ring-2 focus:ring-bambu-green/50 focus:ring-offset-2 focus:ring-offset-bambu-dark-secondary transition-all disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-bambu-green"
  88. >
  89. {loginMutation.isPending ? 'Logging in...' : 'Sign in'}
  90. </button>
  91. </div>
  92. </form>
  93. </div>
  94. </div>
  95. );
  96. }