SetupPage.tsx 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. import { useState } from 'react';
  2. import { useNavigate } from 'react-router-dom';
  3. import { useMutation } from '@tanstack/react-query';
  4. import { api } from '../api/client';
  5. import { useToast } from '../contexts/ToastContext';
  6. import { useTheme } from '../contexts/ThemeContext';
  7. export function SetupPage() {
  8. const navigate = useNavigate();
  9. const { showToast } = useToast();
  10. const { mode } = useTheme();
  11. const [authEnabled, setAuthEnabled] = useState(false);
  12. const [adminUsername, setAdminUsername] = useState('');
  13. const [adminPassword, setAdminPassword] = useState('');
  14. const [confirmPassword, setConfirmPassword] = useState('');
  15. const setupMutation = useMutation({
  16. mutationFn: () =>
  17. api.setupAuth({
  18. auth_enabled: authEnabled,
  19. admin_username: authEnabled ? adminUsername : undefined,
  20. admin_password: authEnabled ? adminPassword : undefined,
  21. }),
  22. onSuccess: (data) => {
  23. if (data.auth_enabled && data.admin_created) {
  24. showToast('Authentication enabled and admin user created');
  25. navigate('/login');
  26. } else {
  27. showToast('Setup completed');
  28. navigate('/');
  29. }
  30. },
  31. onError: (error: Error) => {
  32. showToast(error.message, 'error');
  33. },
  34. });
  35. const handleSubmit = (e: React.FormEvent) => {
  36. e.preventDefault();
  37. if (authEnabled) {
  38. if (!adminUsername || !adminPassword) {
  39. showToast('Please enter admin username and password', 'error');
  40. return;
  41. }
  42. if (adminPassword !== confirmPassword) {
  43. showToast('Passwords do not match', 'error');
  44. return;
  45. }
  46. if (adminPassword.length < 6) {
  47. showToast('Password must be at least 6 characters', 'error');
  48. return;
  49. }
  50. }
  51. setupMutation.mutate();
  52. };
  53. return (
  54. <div className="min-h-screen flex items-center justify-center bg-bambu-dark p-4">
  55. <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">
  56. <div className="text-center">
  57. <div className="flex items-center justify-center mb-6">
  58. <img
  59. src={mode === 'dark' ? '/img/bambuddy_logo_dark_transparent.png' : '/img/bambuddy_logo_light.png'}
  60. alt="Bambuddy"
  61. className="h-16"
  62. />
  63. </div>
  64. <h2 className="text-3xl font-bold text-white">
  65. Bambuddy Setup
  66. </h2>
  67. <p className="mt-2 text-sm text-bambu-gray">
  68. Configure authentication for your Bambuddy instance
  69. </p>
  70. </div>
  71. <form className="mt-8 space-y-6" onSubmit={handleSubmit}>
  72. <div className="space-y-4">
  73. <div className="flex items-center p-4 bg-bambu-dark-secondary/50 rounded-lg border border-bambu-dark-tertiary">
  74. <input
  75. id="auth-enabled"
  76. type="checkbox"
  77. checked={authEnabled}
  78. onChange={(e) => setAuthEnabled(e.target.checked)}
  79. className="h-4 w-4 text-bambu-green focus:ring-bambu-green border-bambu-dark-tertiary rounded bg-bambu-dark-secondary"
  80. />
  81. <label htmlFor="auth-enabled" className="ml-3 block text-sm font-medium text-white">
  82. Enable Authentication
  83. </label>
  84. </div>
  85. {authEnabled && (
  86. <div className="space-y-4 mt-4">
  87. <div>
  88. <label htmlFor="admin-username" className="block text-sm font-medium text-white mb-2">
  89. Admin Username
  90. </label>
  91. <input
  92. id="admin-username"
  93. type="text"
  94. required
  95. value={adminUsername}
  96. onChange={(e) => setAdminUsername(e.target.value)}
  97. 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"
  98. placeholder="Enter admin username"
  99. autoComplete="username"
  100. />
  101. </div>
  102. <div>
  103. <label htmlFor="admin-password" className="block text-sm font-medium text-white mb-2">
  104. Admin Password
  105. </label>
  106. <input
  107. id="admin-password"
  108. type="password"
  109. required
  110. value={adminPassword}
  111. onChange={(e) => setAdminPassword(e.target.value)}
  112. 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"
  113. placeholder="Enter admin password"
  114. minLength={6}
  115. autoComplete="new-password"
  116. />
  117. </div>
  118. <div>
  119. <label htmlFor="confirm-password" className="block text-sm font-medium text-white mb-2">
  120. Confirm Password
  121. </label>
  122. <input
  123. id="confirm-password"
  124. type="password"
  125. required
  126. value={confirmPassword}
  127. onChange={(e) => setConfirmPassword(e.target.value)}
  128. 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"
  129. placeholder="Confirm admin password"
  130. minLength={6}
  131. autoComplete="new-password"
  132. />
  133. </div>
  134. </div>
  135. )}
  136. </div>
  137. <div>
  138. <button
  139. type="submit"
  140. disabled={setupMutation.isPending}
  141. 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"
  142. >
  143. {setupMutation.isPending ? 'Setting up...' : 'Complete Setup'}
  144. </button>
  145. </div>
  146. </form>
  147. </div>
  148. </div>
  149. );
  150. }