Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
11 changes: 10 additions & 1 deletion assets/js/src/core/app/router/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,21 @@ import { LoginPage } from '@Pimcore/modules/auth/login-page'
import React from 'react'
import { createBrowserRouter, Navigate, useLocation } from 'react-router-dom'
import { appConfig } from '../config/app-config'
import { PasswordReset } from '@Pimcore/components/password-reset/password-reset'

export const baseUrl = appConfig.baseUrl.endsWith('/')
? appConfig.baseUrl.slice(0, -1) + '/'
: appConfig.baseUrl

export const LOGIN_URL = `${baseUrl}login/`
export const DEEP_LINK_URL = `${baseUrl}:elementType/:id`
export const PASSWORD_RESET_URL = `${baseUrl}reset-password/`

export const routes = {
root: baseUrl,
login: LOGIN_URL,
deeplinkAsset: DEEP_LINK_URL
deeplinkAsset: DEEP_LINK_URL,
passwordReset: PASSWORD_RESET_URL
}

const AuthenticatedRoute = ({ children }: { children: React.JSX.Element }): React.ReactElement => {
Expand Down Expand Up @@ -62,5 +65,11 @@ export const router = createBrowserRouter([
element: <AuthenticatedRoute>
<DeepLink />
</AuthenticatedRoute>
},
{
path: routes.passwordReset,
element: <AuthenticatedRoute>
<PasswordReset />
</AuthenticatedRoute>
}
])
23 changes: 23 additions & 0 deletions assets/js/src/core/components/password-reset/password-reset.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* This source file is available under the terms of the
* Pimcore Open Core License (POCL)
* Full copyright and license information is available in
* LICENSE.md which is distributed with this source code.
*
* @copyright Copyright (c) Pimcore GmbH (https://www.pimcore.com)
* @license Pimcore Open Core License (POCL)
*/

import { routes } from '@Pimcore/app/router/router'
import React, { useEffect } from 'react'
import { useNavigate } from 'react-router-dom'

export const PasswordReset = (): React.JSX.Element => {
const navigate = useNavigate()

useEffect(() => {
navigate(routes.root, { state: { resetPassword: true } })
}, [])

return <></>
}
18 changes: 16 additions & 2 deletions assets/js/src/core/modules/app/hook/use-handle-deeplink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,19 @@
* @license Pimcore Open Core License (POCL)
*/

import { USERPROFILE } from '@Pimcore/modules/auth/profile/profile-container'
import { useElementHelper } from '@Pimcore/modules/element/hooks/use-element-helper'
import { openMainWidget } from '@Pimcore/modules/widget-manager/widget-manager-slice'
import { type ElementType } from '@Pimcore/types/enums/element/element-type'
import { useAppDispatch } from '@sdk/app'
import { isEmpty } from 'lodash'
import { useLocation } from 'react-router-dom'
import trackError, { GeneralError } from '../error-handler'
import { type ElementType } from '@Pimcore/types/enums/element/element-type'
import { useElementHelper } from '@Pimcore/modules/element/hooks/use-element-helper'

export const useHandleDeepLink = (): void => {
const location = useLocation()
const { openElement } = useElementHelper()
const dispatch = useAppDispatch()

if (location?.state?.isDeeplink === true) {
const id = location?.state?.id
Expand All @@ -33,4 +37,14 @@ export const useHandleDeepLink = (): void => {
trackError(new GeneralError('An Error occured while opening the Element'))
})
}

if (location?.state?.resetPassword === true) {
dispatch(openMainWidget({
...USERPROFILE,
config: {
...USERPROFILE.config,
resetPassword: true
}
}))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/**
* This source file is available under the terms of the
* Pimcore Open Core License (POCL)
* Full copyright and license information is available in
* LICENSE.md which is distributed with this source code.
*
* @copyright Copyright (c) Pimcore GmbH (https://www.pimcore.com)
* @license Pimcore Open Core License (POCL)
*/

import { getPrefix } from '@Pimcore/app/api/pimcore/route'
import { baseUrl } from '@Pimcore/app/router/router'
import { Form } from '@Pimcore/components/form/form'
import { Icon } from '@Pimcore/components/icon/icon'
import { Alert, Button, Flex, FormKit } from '@sdk/components'
import { Input } from 'antd'
import React, { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useAuthentication } from '../../hooks/use-authentication'

interface ForgetPasswordForm {
username: string
}

interface ForgotPasswordFormProps {
onGetBack: () => void
}

export const ForgotPasswordForm = ({ onGetBack }: ForgotPasswordFormProps): React.JSX.Element => {
const { t } = useTranslation()
const [form] = Form.useForm<ForgetPasswordForm>()
const { resetPassword } = useAuthentication()
const [showSuccessMessage, setShowSuccessMessage] = useState<boolean>(false)
const [isLoading, setIsLoading] = useState<boolean>(false)

return (
<Flex vertical>
{showSuccessMessage && (
<Alert
description={ t('forgot-password-form.success-message') }
showIcon
type="success"
/>
)}

{
!showSuccessMessage && (
<FormKit
formProps={ {
form,
onFinish: async (values: ForgetPasswordForm) => {
setIsLoading(true)
void resetPassword(
values.username,
`${baseUrl}${getPrefix()}/reset-password`,
() => {
setIsLoading(false)
},
() => {
form.resetFields()
setShowSuccessMessage(true)
}
)
}
} }
>
<Form.Item
label={ t('forgot-password-form.username') }
name="username"
>
<Input
autoComplete="username"
name={ 'username' }
placeholder={ t('forgot-password-form.username') }
prefix={ <Icon value="user" /> }
/>
</Form.Item>

<Button
className="w-full"
htmlType='submit'
loading={ isLoading }
type='primary'
>
{t('forgot-password-form.reset-password')}
</Button>
</FormKit>
)
}

<Flex justify="flex-end">
<Button
onClick={ onGetBack }
type="link"
>
{t('forgot-password-form.back')}
</Button>
</Flex>
</Flex >
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* This source file is available under the terms of the
* Pimcore Open Core License (POCL)
* Full copyright and license information is available in
* LICENSE.md which is distributed with this source code.
*
* @copyright Copyright (c) Pimcore GmbH (https://www.pimcore.com)
* @license Pimcore Open Core License (POCL)
*/

import { useStyle } from '@Pimcore/modules/auth/components/login-form/login-form-style'
import React, { useState } from 'react'
import { ForgotPasswordForm } from '../forgot-password-form/forgot-password-form'
import { LoginForm } from './login-form'

export interface IAdditionalLogins {
key: string
name: string
link: string
}

interface ILoginFormProps {
additionalLogins?: IAdditionalLogins[]
}

export const LoginFormContainer = ({ additionalLogins }: ILoginFormProps): React.JSX.Element => {
const { styles } = useStyle()
const [showForgotPassword, setShowForgotPassword] = useState<boolean>(false)

return (
<div className={ styles.form }>
{showForgotPassword && (
<ForgotPasswordForm
onGetBack={ () => { setShowForgotPassword(false) } }
/>
)}

{!showForgotPassword && (
<LoginForm
additionalLogins={ additionalLogins }
onPasswordForgotten={ () => { setShowForgotPassword(true) } }
/>
)}
</div>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import { type Meta } from '@storybook/react'
import { LoginForm } from './login-form'

// @todo refactor due to business logic
const config: Meta = {
title: 'Components/__Refactor/Login form',
component: LoginForm,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@
* @license Pimcore Open Core License (POCL)
*/

import { Checkbox, Input } from 'antd'
import { Button } from '@Pimcore/components/button/button'
import React, { useState } from 'react'
import { useStyle } from '@Pimcore/components/login-form/login-form-style'
import { useDispatch } from 'react-redux'
import { useMessage } from '@Pimcore/components/message/useMessage'
import { useTranslation } from 'react-i18next'
import { Icon } from '../icon/icon'
import { type Credentials, useLoginMutation } from '@Pimcore/modules/auth/authorization-api-slice.gen'
import trackError, { ApiError } from '@Pimcore/modules/app/error-handler'
import { setAuthState } from '@Pimcore/modules/auth/auth-slice'
import { type Credentials, useLoginMutation } from '@Pimcore/modules/auth/authorization-api-slice.gen'
import { useStyle } from '@Pimcore/modules/auth/components/login-form/login-form-style'
import { Checkbox, Input } from 'antd'
import React, { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useDispatch } from 'react-redux'
import { Icon } from '../../../../components/icon/icon'

export interface IAdditionalLogins {
key: string
Expand All @@ -28,9 +28,10 @@ export interface IAdditionalLogins {

interface ILoginFormProps {
additionalLogins?: IAdditionalLogins[]
onPasswordForgotten?: () => void
}

export const LoginForm = ({ additionalLogins }: ILoginFormProps): React.JSX.Element => {
export const LoginForm = ({ additionalLogins, onPasswordForgotten }: ILoginFormProps): React.JSX.Element => {
const dispatch = useDispatch()
const { styles } = useStyle()
const messageApi = useMessage()
Expand Down Expand Up @@ -101,7 +102,12 @@ export const LoginForm = ({ additionalLogins }: ILoginFormProps): React.JSX.Elem
>
{t('login-form.remember-me')}
</Checkbox>
<Button type={ 'link' }>{t('login-form.forgot-password')}</Button>
<Button
onClick={ onPasswordForgotten }
type={ 'link' }
>
{t('login-form.forgot-password')}
</Button>
</div>

<Button
Expand Down
50 changes: 50 additions & 0 deletions assets/js/src/core/modules/auth/hooks/use-authentication.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* This source file is available under the terms of the
* Pimcore Open Core License (POCL)
* Full copyright and license information is available in
* LICENSE.md which is distributed with this source code.
*
* @copyright Copyright (c) Pimcore GmbH (https://www.pimcore.com)
* @license Pimcore Open Core License (POCL)
*/

import trackError, { ApiError, GeneralError } from '@Pimcore/modules/app/error-handler'
import { useUserResetPasswordMutation } from '../user/user-api-slice.gen'
import { type ApiErrorData } from '@Pimcore/modules/app/error-handler/classes/api-error'

interface UseAuthenticationReturn {
resetPassword: (username: string, resetPasswordUrl?: string, onFinish?: () => void, onSuccess?: () => void) => Promise<void>
}

export const useAuthentication = (): UseAuthenticationReturn => {
const [resetPasswordMutation] = useUserResetPasswordMutation()

const resetPassword = async (username: string, resetPasswordUrl: string = '', onFinish?: () => void, onSuccess?: () => void): Promise<void> => {
const resetPasswordTask = resetPasswordMutation({
resetPassword: {
username,
resetPasswordUrl
}
})

try {
const response = (await resetPasswordTask) as any

if (response.error !== undefined) {
trackError(new ApiError(response.error as ApiErrorData))
onFinish?.()
return
}

onFinish?.()
onSuccess?.()
} catch {
trackError(new GeneralError('Error resetting password'))
onFinish?.()
}
}

return {
resetPassword
}
}
12 changes: 6 additions & 6 deletions assets/js/src/core/modules/auth/login-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@
* @license Pimcore Open Core License (POCL)
*/

import React, { useEffect } from 'react'
import { routes } from '@Pimcore/app/router/router'
import { LoginForm } from '@Pimcore/components/login-form/login-form'
import { useLocation, useNavigate } from 'react-router-dom'
import { useIsAuthenticated } from './hooks/use-is-authenticated'
import { LoginFormContainer } from '@Pimcore/modules/auth/components/login-form/login-form-container'
import { useUser } from '@Pimcore/modules/auth/hooks/use-user'
import { sendStatistics } from '@Pimcore/modules/auth/services/statisticsService'
import React, { useEffect } from 'react'
import { useLocation, useNavigate } from 'react-router-dom'
import { useIsAuthenticated } from './hooks/use-is-authenticated'
import { useStyle } from './login-page.styles'

export const LoginPage = (): React.JSX.Element => {
Expand All @@ -34,7 +34,7 @@ export const LoginPage = (): React.JSX.Element => {
navigate(redirectPath ?? routes.root)

await sendStatistics(user.isAdmin)
})().catch(() => {})
})().catch(() => { })
}
}, [isAuthenticated])

Expand All @@ -45,7 +45,7 @@ export const LoginPage = (): React.JSX.Element => {
alt={ 'Pimcore Logo' }
src={ '/bundles/pimcorestudioui/img/logo.png' }
/>
<LoginForm />
<LoginFormContainer />
</div>
</div>
)
Expand Down
Loading