mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-22 11:15:23 -04:00
feat: merge login, register and intro page
Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
parent
76ef2511e7
commit
56643ffd85
39 changed files with 533 additions and 374 deletions
|
@ -1,11 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import React from 'react'
|
||||
import { BaseAppBar } from '../../../../components/layout/app-bar/base-app-bar'
|
||||
|
||||
export default function AppBar() {
|
||||
return <BaseAppBar>Landing</BaseAppBar>
|
||||
}
|
|
@ -1,11 +1,29 @@
|
|||
'use client'
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import React from 'react'
|
||||
import { BaseAppBar } from '../../../../components/layout/app-bar/base-app-bar'
|
||||
import { Nav, Navbar } from 'react-bootstrap'
|
||||
import styles from '../../../../components/layout/app-bar/navbar.module.scss'
|
||||
import { HelpDropdown } from '../../../../components/layout/app-bar/app-bar-elements/help-dropdown/help-dropdown'
|
||||
import { SettingsButton } from '../../../../components/global-dialogs/settings-dialog/settings-button'
|
||||
import { BrandingElement } from '../../../../components/layout/app-bar/app-bar-elements/branding-element'
|
||||
|
||||
export default function AppBar() {
|
||||
return <BaseAppBar>Login</BaseAppBar>
|
||||
return (
|
||||
<Navbar expand={true} className={`px-2 py-1 align-items-center ${styles.navbar}`}>
|
||||
<Nav className={`align-items-center justify-content-start gap-2 flex-grow-1 ${styles.side}`}>
|
||||
<BrandingElement />
|
||||
</Nav>
|
||||
<Nav className={`align-items-stretch justify-content-end flex-grow-1 ${styles.side} h-100 py-1`}>
|
||||
<div className={'d-flex gap-2'}>
|
||||
<HelpDropdown />
|
||||
<SettingsButton />
|
||||
</div>
|
||||
</Nav>
|
||||
</Navbar>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
'use client'
|
||||
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { CustomBranding } from '../../../components/common/custom-branding/custom-branding'
|
||||
import { HedgeDocLogoVertical } from '../../../components/common/hedge-doc-logo/hedge-doc-logo-vertical'
|
||||
import { LogoSize } from '../../../components/common/hedge-doc-logo/logo-size'
|
||||
import { EditorToRendererCommunicatorContextProvider } from '../../../components/editor-page/render-context/editor-to-renderer-communicator-context-provider'
|
||||
import { IntroCustomContent } from '../../../components/intro-page/intro-custom-content'
|
||||
import { LandingLayout } from '../../../components/landing-layout/landing-layout'
|
||||
import type { NextPage } from 'next'
|
||||
import React from 'react'
|
||||
import { Trans } from 'react-i18next'
|
||||
|
||||
/**
|
||||
* Renders the intro page with the logo and the customizable intro text.
|
||||
*/
|
||||
const IntroPage: NextPage = () => {
|
||||
return (
|
||||
<LandingLayout>
|
||||
<EditorToRendererCommunicatorContextProvider>
|
||||
<div className={'flex-fill mt-3'}>
|
||||
<h1 dir='auto' className={'align-items-center d-flex justify-content-center flex-column'}>
|
||||
<HedgeDocLogoVertical size={LogoSize.BIG} autoTextColor={true} />
|
||||
</h1>
|
||||
<p className='lead'>
|
||||
<Trans i18nKey='app.slogan' />
|
||||
</p>
|
||||
<div className={'mb-5'}>
|
||||
<CustomBranding />
|
||||
</div>
|
||||
<IntroCustomContent />
|
||||
</div>
|
||||
</EditorToRendererCommunicatorContextProvider>
|
||||
</LandingLayout>
|
||||
)
|
||||
}
|
||||
|
||||
export default IntroPage
|
|
@ -5,89 +5,55 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { AuthProviderWithCustomName } from '../../../api/config/types'
|
||||
import { AuthProviderType } from '../../../api/config/types'
|
||||
import { useFrontendConfig } from '../../../components/common/frontend-config-context/use-frontend-config'
|
||||
import { RedirectBack } from '../../../components/common/redirect-back'
|
||||
import { ShowIf } from '../../../components/common/show-if/show-if'
|
||||
import { LandingLayout } from '../../../components/landing-layout/landing-layout'
|
||||
import { filterOneClickProviders } from '../../../components/login-page/auth/utils'
|
||||
import { ViaLdap } from '../../../components/login-page/auth/via-ldap'
|
||||
import { ViaLocal } from '../../../components/login-page/auth/via-local'
|
||||
import { ViaOneClick } from '../../../components/login-page/auth/via-one-click'
|
||||
import { useApplicationState } from '../../../hooks/common/use-application-state'
|
||||
import type { NextPage } from 'next'
|
||||
import React, { useMemo } from 'react'
|
||||
import { Card, Col, Row } from 'react-bootstrap'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
|
||||
/**
|
||||
* Renders the login page with buttons and fields for the enabled auth providers.
|
||||
* Redirects the user to the history page if they are already logged in.
|
||||
*/
|
||||
import type { NextPage } from 'next'
|
||||
import { EditorToRendererCommunicatorContextProvider } from '../../../components/editor-page/render-context/editor-to-renderer-communicator-context-provider'
|
||||
import { HedgeDocLogoVertical } from '../../../components/common/hedge-doc-logo/hedge-doc-logo-vertical'
|
||||
import { LogoSize } from '../../../components/common/hedge-doc-logo/logo-size'
|
||||
import { Trans } from 'react-i18next'
|
||||
import { CustomBranding } from '../../../components/common/custom-branding/custom-branding'
|
||||
import { IntroCustomContent } from '../../../components/intro-page/intro-custom-content'
|
||||
import React from 'react'
|
||||
import { useApplicationState } from '../../../hooks/common/use-application-state'
|
||||
import { RedirectToParamOrHistory } from '../../../components/login-page/redirect-to-param-or-history'
|
||||
import { Col, Container, Row } from 'react-bootstrap'
|
||||
import { LocalLoginCard } from '../../../components/login-page/local-login/local-login-card'
|
||||
import { LdapLoginCards } from '../../../components/login-page/ldap/ldap-login-cards'
|
||||
import { OneClickLoginCard } from '../../../components/login-page/one-click/one-click-login-card'
|
||||
import { GuestCard } from '../../../components/login-page/guest/guest-card'
|
||||
|
||||
const LoginPage: NextPage = () => {
|
||||
useTranslation()
|
||||
const authProviders = useFrontendConfig().authProviders
|
||||
const userLoggedIn = useApplicationState((state) => !!state.user)
|
||||
|
||||
const ldapProviders = useMemo(() => {
|
||||
return authProviders
|
||||
.filter((provider) => provider.type === AuthProviderType.LDAP)
|
||||
.map((provider) => {
|
||||
const ldapProvider = provider as AuthProviderWithCustomName
|
||||
return (
|
||||
<ViaLdap
|
||||
providerName={ldapProvider.providerName}
|
||||
identifier={ldapProvider.identifier}
|
||||
key={ldapProvider.identifier}
|
||||
/>
|
||||
)
|
||||
})
|
||||
}, [authProviders])
|
||||
|
||||
const localLoginEnabled = useMemo(() => {
|
||||
return authProviders.some((provider) => provider.type === AuthProviderType.LOCAL)
|
||||
}, [authProviders])
|
||||
|
||||
const oneClickProviders = useMemo(() => {
|
||||
return authProviders.filter(filterOneClickProviders).map((provider, index) => (
|
||||
<div className={'p-2 d-flex flex-column social-button-container'} key={index}>
|
||||
<ViaOneClick provider={provider} />
|
||||
</div>
|
||||
))
|
||||
}, [authProviders])
|
||||
|
||||
if (userLoggedIn) {
|
||||
return <RedirectBack />
|
||||
return <RedirectToParamOrHistory />
|
||||
}
|
||||
|
||||
return (
|
||||
<LandingLayout>
|
||||
<div className='my-3'>
|
||||
<Row className='h-100 d-flex justify-content-center'>
|
||||
<ShowIf condition={ldapProviders.length > 0 || localLoginEnabled}>
|
||||
<Col xs={12} sm={10} lg={4}>
|
||||
<ShowIf condition={localLoginEnabled}>
|
||||
<ViaLocal />
|
||||
</ShowIf>
|
||||
{ldapProviders}
|
||||
</Col>
|
||||
</ShowIf>
|
||||
<ShowIf condition={oneClickProviders.length > 0}>
|
||||
<Col xs={12} sm={10} lg={4}>
|
||||
<Card className='mb-4'>
|
||||
<Card.Body>
|
||||
<Card.Title>
|
||||
<Trans i18nKey='login.signInVia' values={{ service: '' }} />
|
||||
</Card.Title>
|
||||
{oneClickProviders}
|
||||
</Card.Body>
|
||||
</Card>
|
||||
</Col>
|
||||
</ShowIf>
|
||||
</Row>
|
||||
</div>
|
||||
</LandingLayout>
|
||||
<Container>
|
||||
<Row>
|
||||
<Col xs={8}>
|
||||
<EditorToRendererCommunicatorContextProvider>
|
||||
<div className={'d-flex flex-column align-items-center mt-3'}>
|
||||
<HedgeDocLogoVertical size={LogoSize.BIG} autoTextColor={true} />
|
||||
<h5>
|
||||
<Trans i18nKey='app.slogan' />
|
||||
</h5>
|
||||
<div className={'mb-5'}>
|
||||
<CustomBranding />
|
||||
</div>
|
||||
<IntroCustomContent />
|
||||
</div>
|
||||
</EditorToRendererCommunicatorContextProvider>
|
||||
</Col>
|
||||
<Col xs={4} className={'pt-3 d-flex gap-3 flex-column'}>
|
||||
<GuestCard />
|
||||
<LocalLoginCard />
|
||||
<LdapLoginCards />
|
||||
<OneClickLoginCard />
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,120 +0,0 @@
|
|||
'use client'
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { doLocalRegister } from '../../../api/auth/local'
|
||||
import type { ApiError } from '../../../api/common/api-error'
|
||||
import { DisplayNameField } from '../../../components/common/fields/display-name-field'
|
||||
import { NewPasswordField } from '../../../components/common/fields/new-password-field'
|
||||
import { PasswordAgainField } from '../../../components/common/fields/password-again-field'
|
||||
import { UsernameLabelField } from '../../../components/common/fields/username-label-field'
|
||||
import { useFrontendConfig } from '../../../components/common/frontend-config-context/use-frontend-config'
|
||||
import { Redirect } from '../../../components/common/redirect'
|
||||
import { LandingLayout } from '../../../components/landing-layout/landing-layout'
|
||||
import { fetchAndSetUser } from '../../../components/login-page/auth/utils'
|
||||
import { useUiNotifications } from '../../../components/notifications/ui-notification-boundary'
|
||||
import { RegisterError } from '../../../components/register-page/register-error'
|
||||
import { RegisterInfos } from '../../../components/register-page/register-infos'
|
||||
import { useApplicationState } from '../../../hooks/common/use-application-state'
|
||||
import { useLowercaseOnInputChange } from '../../../hooks/common/use-lowercase-on-input-change'
|
||||
import { useOnInputChange } from '../../../hooks/common/use-on-input-change'
|
||||
import type { NextPage } from 'next'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import type { FormEvent } from 'react'
|
||||
import React, { useCallback, useMemo, useState } from 'react'
|
||||
import { Button, Card, Col, Form, Row } from 'react-bootstrap'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
|
||||
/**
|
||||
* Renders the registration page with fields for username, display name, password, password retype and information about terms and conditions.
|
||||
*/
|
||||
const RegisterPage: NextPage = () => {
|
||||
useTranslation()
|
||||
const router = useRouter()
|
||||
const allowRegister = useFrontendConfig().allowRegister
|
||||
const userExists = useApplicationState((state) => !!state.user)
|
||||
|
||||
const [username, setUsername] = useState('')
|
||||
const [displayName, setDisplayName] = useState('')
|
||||
const [password, setPassword] = useState('')
|
||||
const [passwordAgain, setPasswordAgain] = useState('')
|
||||
const [error, setError] = useState<ApiError>()
|
||||
|
||||
const { dispatchUiNotification } = useUiNotifications()
|
||||
|
||||
const doRegisterSubmit = useCallback(
|
||||
(event: FormEvent) => {
|
||||
doLocalRegister(username, displayName, password)
|
||||
.then(() => fetchAndSetUser())
|
||||
.then(() => dispatchUiNotification('login.register.success.title', 'login.register.success.message', {}))
|
||||
.then(() => router.push('/'))
|
||||
.catch((error: ApiError) => setError(error))
|
||||
event.preventDefault()
|
||||
},
|
||||
[username, displayName, password, dispatchUiNotification, router]
|
||||
)
|
||||
|
||||
const ready = useMemo(() => {
|
||||
return username.trim() !== '' && displayName.trim() !== '' && password.trim() !== '' && password === passwordAgain
|
||||
}, [username, password, displayName, passwordAgain])
|
||||
|
||||
const isWeakPassword = useMemo(() => {
|
||||
return error?.backendErrorName === 'PasswordTooWeakError'
|
||||
}, [error])
|
||||
|
||||
const isValidUsername = useMemo(() => Boolean(username.trim()), [username])
|
||||
|
||||
const onUsernameChange = useLowercaseOnInputChange(setUsername)
|
||||
const onDisplayNameChange = useOnInputChange(setDisplayName)
|
||||
const onPasswordChange = useOnInputChange(setPassword)
|
||||
const onPasswordAgainChange = useOnInputChange(setPasswordAgain)
|
||||
|
||||
if (userExists) {
|
||||
return <Redirect to={'/intro'} />
|
||||
}
|
||||
|
||||
if (!allowRegister) {
|
||||
return <Redirect to={'/login'} />
|
||||
}
|
||||
|
||||
return (
|
||||
<LandingLayout>
|
||||
<div className='my-3'>
|
||||
<h1 className='mb-4'>
|
||||
<Trans i18nKey='login.register.title' />
|
||||
</h1>
|
||||
<Row className='h-100 d-flex justify-content-center'>
|
||||
<Col lg={6}>
|
||||
<Card className='mb-4 text-start'>
|
||||
<Card.Body>
|
||||
<Form onSubmit={doRegisterSubmit} className={'d-flex flex-column gap-3'}>
|
||||
<UsernameLabelField onChange={onUsernameChange} value={username} isValid={isValidUsername} />
|
||||
<DisplayNameField onChange={onDisplayNameChange} value={displayName} />
|
||||
<NewPasswordField onChange={onPasswordChange} value={password} hasError={isWeakPassword} />
|
||||
<PasswordAgainField
|
||||
password={password}
|
||||
onChange={onPasswordAgainChange}
|
||||
value={passwordAgain}
|
||||
hasError={isWeakPassword}
|
||||
/>
|
||||
|
||||
<RegisterInfos />
|
||||
|
||||
<Button variant='primary' type='submit' disabled={!ready}>
|
||||
<Trans i18nKey='login.register.title' />
|
||||
</Button>
|
||||
|
||||
<RegisterError error={error} />
|
||||
</Form>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
</LandingLayout>
|
||||
)
|
||||
}
|
||||
|
||||
export default RegisterPage
|
Loading…
Add table
Add a link
Reference in a new issue