mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2025-12-06 13:54:41 +08:00
* refactor(frontend): extract RegistrationDisabled as reusable component - Create RegistrationDisabled component with i18n support - Add registrationClosed and registrationClosedMessage translations - Replace inline JSX in App.tsx with new component - Improve code maintainability and reusability - Add hover effect to back button for better UX * fix(frontend): add registration toggle to LoginModal component - Add useSystemConfig hook to LoginModal - Conditionally render registration button based on registration_enabled config - Ensures consistency with HeaderBar and LoginPage registration controls - Completes registration toggle feature implementation across all entry points * feat(frontend): add registration toggle UI support - Add registration disabled page in App.tsx when registration is closed - Hide registration link in LoginPage when registration is disabled - Add registration_enabled field to SystemConfig interface - Frontend conditionally shows/hides registration UI based on backend config * feat: add registration toggle feature Add system-level registration enable/disable control: - Add registration_enabled config to system_config table (default: true) - Add registration check in handleRegister API endpoint - Expose registration_enabled status in /api/config endpoint - Frontend can use this config to conditionally show/hide registration UI This allows administrators to control user registration without code changes. * fix(frontend): add registration toggle to HeaderBar and RegisterPage - Add useSystemConfig hook and registrationEnabled check to HeaderBar - Conditionally show/hide signup buttons in both desktop and mobile views - Add registration check to RegisterPage to show RegistrationDisabled component - This completes the registration toggle feature across all UI components * test(frontend): add comprehensive unit tests for registration toggle feature - Add RegistrationDisabled component tests (rendering, navigation, styling) - Add registrationToggle logic tests (config handling, edge cases, multi-location consistency) - Configure Vitest with jsdom environment for React component testing - All 80 tests passing (9 new tests for RegistrationDisabled + 21 for toggle logic)
97 lines
3.2 KiB
TypeScript
97 lines
3.2 KiB
TypeScript
import { motion } from 'framer-motion'
|
|
import { X } from 'lucide-react'
|
|
import { t, Language } from '../../i18n/translations'
|
|
import { useSystemConfig } from '../../hooks/useSystemConfig'
|
|
|
|
interface LoginModalProps {
|
|
onClose: () => void
|
|
language: Language
|
|
}
|
|
|
|
export default function LoginModal({ onClose, language }: LoginModalProps) {
|
|
const { config: systemConfig } = useSystemConfig()
|
|
const registrationEnabled = systemConfig?.registration_enabled !== false
|
|
|
|
return (
|
|
<motion.div
|
|
className="fixed inset-0 z-50 flex items-center justify-center p-4"
|
|
style={{ background: 'rgba(0, 0, 0, 0.8)' }}
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
exit={{ opacity: 0 }}
|
|
onClick={onClose}
|
|
>
|
|
<motion.div
|
|
className="relative max-w-md w-full rounded-2xl p-8"
|
|
style={{
|
|
background: 'var(--brand-dark-gray)',
|
|
border: '1px solid rgba(240, 185, 11, 0.2)',
|
|
}}
|
|
initial={{ scale: 0.9, y: 50 }}
|
|
animate={{ scale: 1, y: 0 }}
|
|
exit={{ scale: 0.9, y: 50 }}
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
<motion.button
|
|
onClick={onClose}
|
|
className="absolute top-4 right-4"
|
|
style={{ color: 'var(--text-secondary)' }}
|
|
whileHover={{ scale: 1.1, rotate: 90 }}
|
|
whileTap={{ scale: 0.9 }}
|
|
>
|
|
<X className="w-6 h-6" />
|
|
</motion.button>
|
|
<h2
|
|
className="text-2xl font-bold mb-6"
|
|
style={{ color: 'var(--brand-light-gray)' }}
|
|
>
|
|
{t('accessNofxPlatform', language)}
|
|
</h2>
|
|
<p className="text-sm mb-6" style={{ color: 'var(--text-secondary)' }}>
|
|
{t('loginRegisterPrompt', language)}
|
|
</p>
|
|
<div className="space-y-3">
|
|
<motion.button
|
|
onClick={() => {
|
|
window.history.pushState({}, '', '/login')
|
|
window.dispatchEvent(new PopStateEvent('popstate'))
|
|
onClose()
|
|
}}
|
|
className="block w-full px-6 py-3 rounded-lg font-semibold text-center"
|
|
style={{
|
|
background: 'var(--brand-yellow)',
|
|
color: 'var(--brand-black)',
|
|
}}
|
|
whileHover={{
|
|
scale: 1.05,
|
|
boxShadow: '0 10px 30px rgba(240, 185, 11, 0.4)',
|
|
}}
|
|
whileTap={{ scale: 0.95 }}
|
|
>
|
|
{t('signIn', language)}
|
|
</motion.button>
|
|
{registrationEnabled && (
|
|
<motion.button
|
|
onClick={() => {
|
|
window.history.pushState({}, '', '/register')
|
|
window.dispatchEvent(new PopStateEvent('popstate'))
|
|
onClose()
|
|
}}
|
|
className="block w-full px-6 py-3 rounded-lg font-semibold text-center"
|
|
style={{
|
|
background: 'var(--brand-dark-gray)',
|
|
color: 'var(--brand-light-gray)',
|
|
border: '1px solid rgba(240, 185, 11, 0.2)',
|
|
}}
|
|
whileHover={{ scale: 1.05, borderColor: 'var(--brand-yellow)' }}
|
|
whileTap={{ scale: 0.95 }}
|
|
>
|
|
{t('registerNewAccount', language)}
|
|
</motion.button>
|
|
)}
|
|
</div>
|
|
</motion.div>
|
|
</motion.div>
|
|
)
|
|
}
|