Skip to content

1492 profile page #1539

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 28 commits into from
Jun 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
172c0d6
[User Management] Profile Page #1492
sholzer May 23, 2025
783a6d8
[User Management] Profile Page #1492
sholzer May 27, 2025
daacd49
[User Management] Profile Page #1492
sholzer May 27, 2025
d3219d1
[User Management] Profile Page #1492
sholzer May 27, 2025
a9530b6
[User Management] Profile Page #1492
sholzer May 27, 2025
fb7a3f5
[User Management] Profile Page #1492
sholzer May 27, 2025
41a433d
[User Management] Profile Page #1492
sholzer May 27, 2025
c0b81f8
Merge remote-tracking branch 'origin/1.x' into 1492-profile-page
sholzer May 28, 2025
80ee708
[User Management] Profile Page #1492
sholzer May 28, 2025
1b84eb7
[User Management] Profile Page #1492
sholzer Jun 10, 2025
0e05ac0
Merge remote-tracking branch 'origin/1.x' into 1492-profile-page
sholzer Jun 10, 2025
d92594c
[User Management] Profile Page #1492
sholzer Jun 17, 2025
30274d2
[User Management] Profile Page #1492
sholzer Jun 17, 2025
984ae4a
[User Management] Profile Page #1492
sholzer Jun 18, 2025
17979f9
[User Management] Profile Page #1492
sholzer Jun 18, 2025
60f14dc
[User Management] Profile Page #1492
sholzer Jun 18, 2025
0cc022c
[User Management] Profile Page #1492
sholzer Jun 18, 2025
2146d0d
[User Management] Profile Page #1492
sholzer Jun 18, 2025
bd63212
[User Management] Profile Page #1492
sholzer Jun 18, 2025
2c6de43
[User Management] Profile Page #1492
sholzer Jun 18, 2025
d754b83
[User Management] Profile Page #1492
sholzer Jun 18, 2025
a6db85b
[User Management] Profile Page #1492
sholzer Jun 18, 2025
7913d95
[User Management] Profile Page #1492
sholzer Jun 23, 2025
1cac4b4
[User Management] Profile Page #1492
sholzer Jun 23, 2025
46c4584
[User Management] Profile Page #1492
sholzer Jun 23, 2025
ed19dfc
Merge remote-tracking branch 'origin/1.x' into 1492-profile-page
sholzer Jun 23, 2025
8bbef26
[User Management] Profile Page #1492
sholzer Jun 23, 2025
b4fe3f0
Automatic frontend build
sholzer Jun 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ export const useStyles = createStyles(({ token, css }) => {
}

.ant-collapse-header {
padding: ${token.paddingXXS}px ${token.paddingSM}px;
padding: ${token.paddingXS}px ${token.paddingSM}px;
}
}
}
Expand Down
71 changes: 71 additions & 0 deletions assets/js/src/core/modules/auth/hooks/use-trackable-changes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* 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 type { PayloadAction } from '@reduxjs/toolkit'
import { useAppDispatch } from '@sdk/app'

export type ModifiedCell = Record<string, any>

export interface ModifiedCellsAction {
modifiedCells: ModifiedCell
}

export interface TrackableChangesDraft {
modified: boolean
changes: Record<string, boolean>
modifiedCells: ModifiedCell
}

interface UseTrackableChangesReturn {
resetChanges: (state: TrackableChangesDraft, action: PayloadAction<void>) => void
setModifiedCells: (state: TrackableChangesDraft, action: PayloadAction<ModifiedCellsAction>) => void
}

export const useTrackableChangesReducers = (): UseTrackableChangesReturn => {
const resetChanges = (state: TrackableChangesDraft): void => {
state.changes = {}
state.modifiedCells = {}
state.modified = false
}

const setModifiedCells = (state: TrackableChangesDraft, action: PayloadAction<ModifiedCellsAction>): void => {
state.modifiedCells = {
...state.modifiedCells,
...action.payload.modifiedCells
}
state.modified = true
}

return {
resetChanges,
setModifiedCells
}
}

export interface UseTrackableChangesDraftReturn {
removeTrackedChanges: () => void
setModifiedCells: (modifiedCells: ModifiedCell) => void
}

export const useTrackableChangesDraft = (
resetChangesAction: () => PayloadAction<void>,
setModifiedCellsAction: (action: ModifiedCellsAction) => PayloadAction<ModifiedCellsAction>
): UseTrackableChangesDraftReturn => {
const dispatch = useAppDispatch()

return {
removeTrackedChanges: (): void => {
dispatch(resetChangesAction())
},
setModifiedCells: (modifiedCells: ModifiedCell): void => {
dispatch(setModifiedCellsAction({ modifiedCells }))
}
}
}
53 changes: 53 additions & 0 deletions assets/js/src/core/modules/auth/hooks/use-user-draft.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* 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 { useAppSelector } from '@sdk/app'
import {
resetChanges, setModifiedCells, selectCurrentUser
} from '../user/user-slice'
import { useEffect, useState } from 'react'
import {
useTrackableChangesDraft,
type UseTrackableChangesDraftReturn
} from '@Pimcore/modules/auth/hooks/use-trackable-changes'
import { type UserInformation } from '@Pimcore/modules/auth/user/user-api-slice-enhanced'

interface IExtendsUserInformation extends UserInformation {
modified?: boolean
modifiedCells?: Record<string, boolean>
}
export interface UseUserDraftReturn extends UseTrackableChangesDraftReturn {
isLoading: boolean
user: IExtendsUserInformation
}

export const useUserDraft = (): UseUserDraftReturn => {
const user = useAppSelector(state => selectCurrentUser(state))
const [isLoading, setIsLoading] = useState<boolean>(true)

useEffect(() => {
if (user === undefined) {
setIsLoading(true)
} else {
setIsLoading(false)
}
}, [user])

const trackableChangesActions = useTrackableChangesDraft(
() => resetChanges(),
(action) => setModifiedCells(action)
)

return {
isLoading,
user,
...trackableChangesActions
}
}
83 changes: 83 additions & 0 deletions assets/js/src/core/modules/auth/hooks/use-user-helper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/**
* 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 { useAppDispatch } from '@sdk/app'
import {
api,
type Error, type UserUpdateProfileApiResponse
} from '@Pimcore/modules/auth/user/user-api-slice-enhanced'
import { useNotification } from '@Pimcore/components/notification/useNotification'
import { useTranslation } from 'react-i18next'
import { userProfileUpdated } from '@Pimcore/modules/auth/user/user-slice'
import { type KeyBindingForAUser } from '@Pimcore/modules/auth/user/user-api-slice.gen'

interface UseUserReturn {
updateUserProfile: (user) => Promise<{ data: UserUpdateProfileApiResponse, error: any }>
}

export const useUserHelper = (): UseUserReturn => {
const { t } = useTranslation()
const dispatch = useAppDispatch()
const [notificationApi] = useNotification()

const handleNotification = (successMessage, error): void => {
if (error !== undefined) {
notificationApi.open({
type: 'error',
message: error.data?.message ?? t('user-management.save-user.error')
})
} else {
notificationApi.open({
type: 'success',
message: successMessage
})
}
}

async function updateUserProfile (user): Promise<{ data: UserUpdateProfileApiResponse, error: Error }> {
if (user.modifiedCells !== undefined) {
const mergedKeyBindings = Array.from(([...(user.keyBindings ?? []), ...(user.modifiedCells.keyBindings ?? [])].reduce(
(map, item: KeyBindingForAUser) => map.set(item.action, item), new Map<string, KeyBindingForAUser>()) as Map<string, KeyBindingForAUser>
).values()
)

const { keyBindings, ...restModifiedCells } = user.modifiedCells

user = {
...user,
...restModifiedCells,
keyBindings: mergedKeyBindings
}
}

const { data, error }: any = await dispatch(api.endpoints.userUpdateProfile.initiate({
updateUserProfile: {
firstname: user.firstname,
lastname: user.lastname,
email: user.email,
language: user.language,
dateTimeLocale: user.dateTimeLocale,
welcomeScreen: user.welcomeScreen,
memorizeTabs: user.memorizeTabs,
contentLanguages: user.contentLanguages,
keyBindings: user.keyBindings
}
}))

handleNotification(t('user-management.save-user.success'), error)

dispatch(userProfileUpdated(data))
return data
}

return {
updateUserProfile
}
}
7 changes: 6 additions & 1 deletion assets/js/src/core/modules/auth/hooks/use-user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ import { useSelector } from 'react-redux'
import { selectCurrentUser } from '@Pimcore/modules/auth/user/user-slice'
import { type UserInformation } from '../user/user-api-slice.gen'

export const useUser = (): UserInformation => {
interface IExtendsUserInformation extends UserInformation {
modified?: boolean
modifiedCells?: Record<string, boolean>
}

export const useUser = (): IExtendsUserInformation => {
const user = useSelector(selectCurrentUser)

return useMemo(() => (user), [user])
Expand Down
53 changes: 53 additions & 0 deletions assets/js/src/core/modules/auth/profile/profile-container.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* 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 React from 'react'
import type { WidgetManagerTabConfig } from '@Pimcore/modules/widget-manager/widget-manager-slice'
import { ContentLayout } from '@Pimcore/components/content-layout/content-layout'
import { Content } from '@Pimcore/components/content/content'
import { useUser } from '@Pimcore/modules/auth/hooks/use-user'
import { useUserDraft } from '@Pimcore/modules/auth/hooks/use-user-draft'
import { Toolbar } from '@Pimcore/modules/auth/profile/toolbar/toolbar'
import { ProfileDetail } from '@Pimcore/modules/auth/profile/profile-detail'

export const USERPROFILE: WidgetManagerTabConfig = {
component: 'user-profile',
name: 'user-profile',
id: 'user-profile',
config: {
translationKey: 'user-profile.label',
icon: {
type: 'name',
value: 'user'
}
}
}

const ProfileContainer = (): React.JSX.Element => {
const user = useUser()
const { isLoading } = useUserDraft()

return (
<ContentLayout
renderToolbar={
<Toolbar id={ user.id } />
}
>
<Content
loading={ isLoading }
padded
>
<ProfileDetail id={ user.id } />
</Content>
</ContentLayout>
)
}

export { ProfileContainer }
Loading