Skip to content

1042 relation data path formatter #1447

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

Draft
wants to merge 5 commits into
base: 1.x
Choose a base branch
from
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,39 @@
* @license https://github.com/pimcore/studio-ui-bundle/blob/1.x/LICENSE.md POCL and PCL
*/

import { useRef } from 'react'
import { invalidatingTags } from '@Pimcore/app/api/pimcore/tags'
import { store, useAppDispatch } from '@Pimcore/app/store'
import { setNodeLoadingInAllTree, setNodePublished } from '@Pimcore/components/element-tree/element-tree-slice'
import { type IconProps } from '@Pimcore/components/icon/icon'
import trackError, { ApiError, GeneralError } from '@Pimcore/modules/app/error-handler'
import { type ElementIcon } from '@Pimcore/modules/asset/asset-api-slice.gen'
import { api } from '@Pimcore/modules/data-object/data-object-api-slice-enhanced'
import { api, type Error } from '@Pimcore/modules/data-object/data-object-api-slice-enhanced'
import { getElementIcon } from '@Pimcore/modules/element/element-helper'
import { checkElementPermission } from '@Pimcore/modules/element/permissions/permission-helper'
import { useWidgetManager } from '@Pimcore/modules/widget-manager/hooks/use-widget-manager'
import { getWidgetId } from '@Pimcore/modules/widget-manager/utils/tools'
import { SaveTaskType } from '../actions/save/use-save'
import { useDataObjectUpdateByIdMutation } from '../data-object-api-slice.gen'
import { useDataObjectUpdateByIdMutation, type DataObjectFormatPathApiResponse } from '../data-object-api-slice.gen'
import { publishDraft, unpublishDraft } from '../data-object-draft-slice'
import { type EditorContainerProps } from '../editor/editor-container'
import { useDataObjectDraftFetcher } from './use-data-object-draft-fetcher'
import type { ApiErrorData } from '@Pimcore/modules/app/error-handler/classes/api-error'

interface OpenDataObjectWidgetProps {
config: EditorContainerProps
}

interface IFormatPathItems {
id: number
type: string
fullPath: string
}

interface UseDataObjectReturn {
openDataObject: (props: OpenDataObjectWidgetProps) => Promise<void>
executeDataObjectTask: (id: number, task: SaveTaskType, onFinish?: () => void) => Promise<void>
formatPath: (items: IFormatPathItems[], fieldName: string, dataObjectId: number) => Promise<DataObjectFormatPathApiResponse | undefined>
}

export const useDataObjectHelper = (): UseDataObjectReturn => {
Expand Down Expand Up @@ -121,5 +130,46 @@ export const useDataObjectHelper = (): UseDataObjectReturn => {
}
}

return { openDataObject, executeDataObjectTask }
const formatPathCache = useRef(new Map<string, DataObjectFormatPathApiResponse | undefined>())
const formatPath = async (items: IFormatPathItems[], fieldName: string, dataObjectId: number): Promise<DataObjectFormatPathApiResponse | undefined> => {
const cacheKey = `dataObjectId_${dataObjectId}_${items.length}_fieldName_${fieldName}`

if (formatPathCache.current.has(cacheKey)) {
return formatPathCache.current.get(cacheKey)
}

const targets = items.reduce((acc, item) => {
acc[`object_${item.id}`] = {
id: item.id,
type: item.type,
label: item.fullPath,
path: item.fullPath,
nicePathKey: `object_${item.id}`
}
return acc
}, {})

const { data, error } = await store.dispatch(api.endpoints.dataObjectFormatPath.initiate({
body: {
objectId: dataObjectId,
targets,
context: {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be a bit tricky but we need to somehow find a way to respect the different possible context cases:

https://github.com/pimcore/studio-backend-bundle/blob/75ac539df3485d772261d8645d50765710ac0647/src/DataObject/Legacy/PathFormatterHelper.php#L46

containerType: 'object',
fieldname: fieldName,
objectId: dataObjectId,
layoutId: '0'
}
}
}))

if (data === undefined) {
trackError(new ApiError(error as unknown as ApiErrorData))
}

formatPathCache.current.set(cacheKey, data)

return data
}

return { openDataObject, executeDataObjectTask, formatPath }
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import type {
import { type ColumnDef, createColumnHelper } from '@tanstack/react-table'
import { useTranslation } from 'react-i18next'
import { getElementCellConfig } from '../many-to-many-relation/grid'
import { Flex } from 'antd'
import { SanitizeHtml } from '@Pimcore/components/sanitize-html/sanitize-html'

export interface AdvancedManyToManyRelationClassDefinitionProps {
allowToClearRelation: boolean
Expand Down Expand Up @@ -88,7 +90,17 @@ export const AdvancedManyToManyRelation = (props: AdvancedManyToManyRelationProp
editable: false,
config: getElementCellConfig(props.inherited === true || props.disabled === true)
},
size: 200
size: 200,
cell: (info) => {
return (
<Flex
align={ 'center' }
className={ 'p-mini' }
>
<SanitizeHtml html={ info.getValue() ?? '' } />
</Flex>
)
}
}),
...columnDefinition,
columnHelper.accessor('type', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
type ManyToManyRelationValueItem
} from '@Pimcore/modules/element/dynamic-types/definitions/objects/data-related/components/many-to-many-relation/hooks/use-value'
import { IconButton } from '@Pimcore/components/icon-button/icon-button'
import { Tooltip } from 'antd'
import { Tooltip, Flex } from 'antd'
import { Trans, useTranslation } from 'react-i18next'
import { useElementHelper } from '@Pimcore/modules/element/hooks/use-element-helper'
import { ButtonGroup } from '@Pimcore/components/button-group/button-group'
Expand All @@ -36,6 +36,7 @@ import { type OnUpdateCellDataEvent } from '@Pimcore/types/components/types'
import { type ElementCellConfig, type ElementInfo } from '../../../../grid-cell/components/element-cell/element-cell'
import { type DefaultCellProps } from '@Pimcore/components/grid/columns/default-cell'
import { mapToElementType } from '@Pimcore/modules/element/utils/element-type'
import { SanitizeHtml } from '@Pimcore/components/sanitize-html/sanitize-html'

interface ManyToManyRelationGridProps {
value?: ManyToManyRelationValue | null
Expand Down Expand Up @@ -94,7 +95,17 @@ export const ManyToManyRelationGrid = forwardRef(function ManyToManyRelationGrid
editable: false,
config: getElementCellConfig(props.inherited === true || props.disabled === true)
},
size: 200
size: 200,
cell: (info) => {
return (
<Flex
align={ 'center' }
className={ 'p-mini' }
>
<SanitizeHtml html={ info.getValue() ?? '' } />
</Flex>
)
}
}),
columnHelper.accessor('type', {
header: t('relations.type'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import type { DragAndDropInfo } from '@Pimcore/components/drag-and-drop/context-
import { useAlertModal } from '@Pimcore/components/modal/alert-modal/hooks/use-alert-modal'
import { useTranslation } from 'react-i18next'
import { type Asset } from '@Pimcore/modules/asset/asset-api-slice-enhanced'
import { useEffect } from 'react'
import { useDataObjectHelper } from '@Pimcore/modules/data-object/hooks/use-data-object-helper'
import { useDataObject } from '@Pimcore/modules/data-object/hooks/use-data-object'

export interface ManyToManyRelationValueItem {
id: number
Expand All @@ -33,22 +36,51 @@ interface UseValueReturn {
addItems: (items: ManyToManyRelationValueItem[]) => void
addAssets: (assets: Asset[]) => Promise<void>
maxRemainingItems?: number
pathFormatterClass?: string | null
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently we have the loading spinner + the triggered format-path request also when no path formatter is configured. We need to find a way to skip the path formatter logic when no pathFormatterClass is defined.

}

export const useValue = (
value: ManyToManyRelationValue | null,
setValue: (value: ManyToManyRelationValue | null) => void,
displayedValue: ManyToManyRelationValue | null,
setDisplayedValue: (value: ManyToManyRelationValue | null) => void,
setIsLoading: (isLoading: boolean) => void,
maxItems: number | null,
allowMultipleAssignments?: boolean
id: string | undefined,
allowMultipleAssignments?: boolean,
pathFormatterClass?: string | null
): UseValueReturn => {
const { id: dataObjectId } = useDataObject()
const { formatPath } = useDataObjectHelper()
const modal = useAlertModal()

const { t } = useTranslation()
const itemIsInValue = (id: number, type: string): boolean => {
return value?.some(item => item.id === id && item.type === type) ?? false
}

useEffect(() => {
console.log(pathFormatterClass, value, dataObjectId, id)
if (pathFormatterClass !== null && value !== null && dataObjectId !== undefined && id !== undefined) {
setIsLoading(true)

formatPath(value, id, dataObjectId).then((data) => {
if (data === undefined) {
return
}

const newValue = value.map((item) => ({
...item,
fullPath: data.items.find(i => i.objectReference === `object_${item.id}`)?.formatedPath ?? item.fullPath
}))

console.log('newValue', newValue)
setDisplayedValue(newValue)
setIsLoading(false)
}).catch(error => { console.error(error) })
}
}, [value])

const addItems = (items: ManyToManyRelationValueItem[]): void => {
const newItems = allowMultipleAssignments !== true
? items.filter(item => !itemIsInValue(item.id, item.type))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export interface ManyToManyRelationClassDefinitionProps {
}

export interface ManyToManyRelationProps extends IRelationAllowedTypesDataComponent, ManyToManyRelationClassDefinitionProps {
id?: string
disabled?: boolean
inherited?: boolean
value?: ManyToManyRelationValue | null
Expand All @@ -64,7 +65,8 @@ export interface ManyToManyRelationProps extends IRelationAllowedTypesDataCompon
export const ManyToManyRelation = (props: ManyToManyRelationProps): React.JSX.Element => {
const [value, setValue] = useState<ManyToManyRelationValue | null>(props.value ?? null)
const [displayedValue, setDisplayedValue] = useState<ManyToManyRelationValue | null>(props.value ?? null)
const { onDrop, deleteItem, onSearch, addAssets, addItems, maxRemainingItems } = useValue(value, setValue, displayedValue, setDisplayedValue, props.maxItems, props.allowMultipleAssignments)
const [isLoading, setIsLoading] = useState<boolean>(props.pathFormatterClass !== null)
const { onDrop, deleteItem, onSearch, addAssets, addItems, maxRemainingItems } = useValue(value, setValue, displayedValue, setDisplayedValue, setIsLoading, props.maxItems, props.id, props.allowMultipleAssignments, props.pathFormatterClass)

useEffect(() => {
if (!isEqual(value, props.value ?? null)) {
Expand All @@ -79,7 +81,7 @@ export const ManyToManyRelation = (props: ManyToManyRelationProps): React.JSX.El
}
}, [JSON.stringify(props.value)])

if (props.isLoading === true) {
if (isLoading || props.isLoading === true) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The loading state here seemed to be a good idea, but it's a bit annoying that the full data type has a loading state - especially when a new item is added via the tree. The grid data type itself also provides a loading prop. Maybe it's better to just set the grid itself to loading instead of everything? Not sure how easy it's possible but in a perfect world we would see the loading spinner directly on row level for newly added items (and only for the initial load we use the global one).

return (
<Content
loading
Expand Down
12 changes: 0 additions & 12 deletions public/build/4d5d7b08-d256-4f86-bc41-7f8e0f1b8089/entrypoints.json

This file was deleted.

14 changes: 0 additions & 14 deletions public/build/4d5d7b08-d256-4f86-bc41-7f8e0f1b8089/manifest.json

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"entrypoints": {
"vendor": {
"js": [
"/bundles/pimcorestudioui/build/6cba8b67-a6fe-4ac6-aa10-d3d8a0f49ea9/vendor.js"
]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"bundles/pimcorestudioui/build/6cba8b67-a6fe-4ac6-aa10-d3d8a0f49ea9/vendor.js": "/bundles/pimcorestudioui/build/6cba8b67-a6fe-4ac6-aa10-d3d8a0f49ea9/vendor.js"
}

This file was deleted.

This file was deleted.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions public/build/a561e8d7-38c1-4bcf-976a-dfd64956d437/entrypoints.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"entrypoints": {
"core-dll": {
"css": [
"/bundles/pimcorestudioui/build/a561e8d7-38c1-4bcf-976a-dfd64956d437/core-dll.css"
],
"js": [
"/bundles/pimcorestudioui/build/a561e8d7-38c1-4bcf-976a-dfd64956d437/core-dll.js"
]
}
}
}
14 changes: 14 additions & 0 deletions public/build/a561e8d7-38c1-4bcf-976a-dfd64956d437/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"bundles/pimcorestudioui/a561e8d7-38c1-4bcf-976a-dfd64956d437/core-dll.css": "/bundles/pimcorestudioui/build/a561e8d7-38c1-4bcf-976a-dfd64956d437/core-dll.css",
"bundles/pimcorestudioui/a561e8d7-38c1-4bcf-976a-dfd64956d437/core-dll.js": "/bundles/pimcorestudioui/build/a561e8d7-38c1-4bcf-976a-dfd64956d437/core-dll.js",
"bundles/pimcorestudioui/a561e8d7-38c1-4bcf-976a-dfd64956d437/105.js": "/bundles/pimcorestudioui/build/a561e8d7-38c1-4bcf-976a-dfd64956d437/105.js",
"bundles/pimcorestudioui/a561e8d7-38c1-4bcf-976a-dfd64956d437/fonts/Lato-Light.ttf": "/bundles/pimcorestudioui/build/a561e8d7-38c1-4bcf-976a-dfd64956d437/fonts/Lato-Light.c7400fca.ttf",
"bundles/pimcorestudioui/a561e8d7-38c1-4bcf-976a-dfd64956d437/fonts/Lato-Regular.ttf": "/bundles/pimcorestudioui/build/a561e8d7-38c1-4bcf-976a-dfd64956d437/fonts/Lato-Regular.9d883d54.ttf",
"bundles/pimcorestudioui/a561e8d7-38c1-4bcf-976a-dfd64956d437/fonts/Lato-Bold.ttf": "/bundles/pimcorestudioui/build/a561e8d7-38c1-4bcf-976a-dfd64956d437/fonts/Lato-Bold.636be8de.ttf",
"bundles/pimcorestudioui/a561e8d7-38c1-4bcf-976a-dfd64956d437/images/spritesheet.svg": "/bundles/pimcorestudioui/build/a561e8d7-38c1-4bcf-976a-dfd64956d437/images/spritesheet.a4e0eb7a.svg",
"bundles/pimcorestudioui/a561e8d7-38c1-4bcf-976a-dfd64956d437/images/spritesheet-2x.png": "/bundles/pimcorestudioui/build/a561e8d7-38c1-4bcf-976a-dfd64956d437/images/spritesheet-2x.7ea3a6d4.png",
"bundles/pimcorestudioui/a561e8d7-38c1-4bcf-976a-dfd64956d437/images/spritesheet.png": "/bundles/pimcorestudioui/build/a561e8d7-38c1-4bcf-976a-dfd64956d437/images/spritesheet.ef32ea2b.png",
"bundles/pimcorestudioui/a561e8d7-38c1-4bcf-976a-dfd64956d437/images/marker-icon.png": "/bundles/pimcorestudioui/build/a561e8d7-38c1-4bcf-976a-dfd64956d437/images/marker-icon.2b3e1faf.png",
"bundles/pimcorestudioui/a561e8d7-38c1-4bcf-976a-dfd64956d437/images/layers-2x.png": "/bundles/pimcorestudioui/build/a561e8d7-38c1-4bcf-976a-dfd64956d437/images/layers-2x.8f2c4d11.png",
"bundles/pimcorestudioui/a561e8d7-38c1-4bcf-976a-dfd64956d437/images/layers.png": "/bundles/pimcorestudioui/build/a561e8d7-38c1-4bcf-976a-dfd64956d437/images/layers.416d9136.png"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"entrypoints": {
"main": {
"js": [
"/bundles/pimcorestudioui/build/f1523252-b7f0-4ed2-9cb3-85d97b02c6ca/main.js"
]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"bundles/pimcorestudioui/build/f1523252-b7f0-4ed2-9cb3-85d97b02c6ca/main.js": "/bundles/pimcorestudioui/build/f1523252-b7f0-4ed2-9cb3-85d97b02c6ca/main.js"
}