feat: add whether to enable self registration toggle (#760)

* 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)
This commit is contained in:
Lawrence Liu
2025-11-13 01:37:24 +08:00
committed by tangmengqiu
parent f8d1cbb670
commit e497898ef1
13 changed files with 437 additions and 750 deletions

View File

@@ -4,6 +4,7 @@ import { motion } from 'framer-motion'
import { Menu, X, ChevronDown } from 'lucide-react'
import { t, type Language } from '../i18n/translations'
import { Container } from './Container'
import { useSystemConfig } from '../hooks/useSystemConfig'
interface HeaderBarProps {
onLoginClick?: () => void
@@ -33,6 +34,8 @@ export default function HeaderBar({
const [userDropdownOpen, setUserDropdownOpen] = useState(false)
const dropdownRef = useRef<HTMLDivElement>(null)
const userDropdownRef = useRef<HTMLDivElement>(null)
const { config: systemConfig } = useSystemConfig()
const registrationEnabled = systemConfig?.registration_enabled !== false
// Close dropdown when clicking outside
useEffect(() => {
@@ -464,16 +467,18 @@ export default function HeaderBar({
>
{t('signIn', language)}
</a>
<a
href="/register"
className="px-4 py-2 rounded font-semibold text-sm transition-colors hover:opacity-90"
style={{
background: 'var(--brand-yellow)',
color: 'var(--brand-black)',
}}
>
{t('signUp', language)}
</a>
{registrationEnabled && (
<a
href="/register"
className="px-4 py-2 rounded font-semibold text-sm transition-colors hover:opacity-90"
style={{
background: 'var(--brand-yellow)',
color: 'var(--brand-black)',
}}
>
{t('signUp', language)}
</a>
)}
</div>
)
)}
@@ -901,17 +906,19 @@ export default function HeaderBar({
>
{t('signIn', language)}
</a>
<a
href="/register"
className="block w-full px-4 py-2 rounded font-semibold text-sm text-center transition-colors"
style={{
background: 'var(--brand-yellow)',
color: 'var(--brand-black)',
}}
onClick={() => setMobileMenuOpen(false)}
>
{t('signUp', language)}
</a>
{registrationEnabled && (
<a
href="/register"
className="block w-full px-4 py-2 rounded font-semibold text-sm text-center transition-colors"
style={{
background: 'var(--brand-yellow)',
color: 'var(--brand-black)',
}}
onClick={() => setMobileMenuOpen(false)}
>
{t('signUp', language)}
</a>
)}
</div>
)}
</div>