Studio UI Development - Compare Objects#73
Conversation
|
There was a problem hiding this comment.
Pull request overview
Adds an Object Merger / “Compare Objects” screen to the Studio UI bundle, wiring it into navigation + widget registry and providing the client-side compare/apply/reset/save behavior for Pimcore data objects.
Changes:
- Introduces a new
object-mergermodule (page, toolbar, form, versions view, context + data hook) and supporting helpers/types. - Adds Studio translations for the compare/merge UI.
- Updates Rsbuild module federation remote wiring and bumps
@pimcore/studio-ui-bundlecanary; replaces the committed studio build output.
Reviewed changes
Copilot reviewed 33 out of 34 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Resources/translations/studio.en.yaml | Adds translation keys for the compare objects UI. |
| src/Resources/public/studio/build/7cd2589e-1c49-4de1-9674-b88c2473c540/manifest.json | New build manifest for the studio bundle output. |
| src/Resources/public/studio/build/7cd2589e-1c49-4de1-9674-b88c2473c540/exposeRemote.js | New remote exposure script for module federation. |
| src/Resources/public/studio/build/7cd2589e-1c49-4de1-9674-b88c2473c540/entrypoints.json | New entrypoints used by Symfony to load studio assets. |
| src/Resources/public/studio/build/774b960f-178a-4c0b-b61a-a1560ed6e8a4/static/js/remoteEntry.js | Removes previous build artifact. |
| src/Resources/public/studio/build/774b960f-178a-4c0b-b61a-a1560ed6e8a4/static/js/main.69be1148.js | Removes previous build artifact. |
| src/Resources/public/studio/build/774b960f-178a-4c0b-b61a-a1560ed6e8a4/static/js/async/__federation_expose_default_export.9c01be30.js | Removes previous build artifact. |
| src/Resources/public/studio/build/774b960f-178a-4c0b-b61a-a1560ed6e8a4/mf-stats.json | Removes previous build artifact. |
| src/Resources/public/studio/build/774b960f-178a-4c0b-b61a-a1560ed6e8a4/mf-manifest.json | Removes previous build artifact. |
| src/Resources/public/studio/build/774b960f-178a-4c0b-b61a-a1560ed6e8a4/manifest.json | Removes previous build artifact. |
| src/Resources/public/studio/build/774b960f-178a-4c0b-b61a-a1560ed6e8a4/main.html | Removes previous build artifact. |
| src/Resources/public/studio/build/774b960f-178a-4c0b-b61a-a1560ed6e8a4/exposeRemote.js | Removes previous build artifact. |
| src/Resources/public/studio/build/774b960f-178a-4c0b-b61a-a1560ed6e8a4/entrypoints.json | Removes previous build artifact. |
| assets/studio/rsbuild.config.ts | Switches remote definition to createDynamicRemote and updates imports. |
| assets/studio/package.json | Bumps @pimcore/studio-ui-bundle canary and reorders deps. |
| assets/studio/package-lock.json | Locks updated @pimcore/studio-ui-bundle version. |
| assets/studio/js/src/plugins.ts | Updates module import path for ObjectMerger module entry. |
| assets/studio/js/src/modules/object-merger/types.ts | Adds shared types for merger data structures. |
| assets/studio/js/src/modules/object-merger/object-merger-page/object-merger-page.tsx | Adds main page layout composing form/toolbar/view. |
| assets/studio/js/src/modules/object-merger/object-merger-page/object-merger-page-wrapper.tsx | Wraps the page with the merger context provider. |
| assets/studio/js/src/modules/object-merger/object-merger-page/components/refetch/refetch.tsx | Adds refetch button/spinner component for toolbar. |
| assets/studio/js/src/modules/object-merger/object-merger-page/components/object-merger-view/types.ts | Adds types for selected objects (A/B). |
| assets/studio/js/src/modules/object-merger/object-merger-page/components/object-merger-view/object-merger-view.tsx | Implements compare screen content and header actions. |
| assets/studio/js/src/modules/object-merger/object-merger-page/components/object-merger-view/object-merger-view.styles.ts | Adds styles for sticky header and compare layout. |
| assets/studio/js/src/modules/object-merger/object-merger-page/components/object-merger-view/helpers.ts | Adds breadcrumb/category building helpers. |
| assets/studio/js/src/modules/object-merger/object-merger-page/components/object-merger-view/constants.tsx | Adds category enum and merge source list. |
| assets/studio/js/src/modules/object-merger/object-merger-page/components/object-merger-view/components/object-merger-versions/object-merger-versions.tsx | Renders side-by-side version fields and merge/reset actions. |
| assets/studio/js/src/modules/object-merger/object-merger-page/components/object-merger-toolbar/object-merger-toolbar.tsx | Adds toolbar actions (mirror/apply/reset/save). |
| assets/studio/js/src/modules/object-merger/object-merger-page/components/object-merger-form/object-merger-form.tsx | Adds object selection form and compare trigger. |
| assets/studio/js/src/modules/object-merger/object-merger-page/components/object-merger-form/object-merger-form.styles.ts | Adds form layout styles. |
| assets/studio/js/src/modules/object-merger/index.tsx | Registers nav item and widget for the new page. |
| assets/studio/js/src/modules/object-merger/hooks/use-object-merger-data.tsx | Adds the data fetching/merge/apply/reset/save logic. |
| assets/studio/js/src/modules/object-merger/helpers/details-functions.ts | Adds layout processing + merger field creation helpers. |
| assets/studio/js/src/modules/object-merger/context/object-merger-context.tsx | Provides context wiring for module state/actions. |
Files not reviewed (1)
- assets/studio/package-lock.json: Language not supported
Comments suppressed due to low confidence (5)
assets/studio/js/src/modules/object-merger/object-merger-page/components/object-merger-view/object-merger-view.styles.ts:29
- The sticky header uses an extremely large
z-index: 999999999and a hard-codedbackground-color: #fff. This can cause stacking issues with other overlays/modals and breaks theming (dark mode). Prefer using design tokens for background and a project-standard z-index value/token so the header participates correctly in the global stacking context.
headerContainer: css`
position: sticky;
top: 0;
width: 100%;
z-index: 999999999;
&::before {
content: '';
position: absolute;
top: -15px;
bottom: 0;
width: 100%;
height: 20px;
background-color: #fff;
z-index: -1;
assets/studio/js/src/modules/object-merger/object-merger-page/components/refetch/refetch.tsx:32
onClickis declaredasyncbut doesn'tawaitanything (andrefetchis sync). This creates an unnecessary promise and can confuse error handling. UseonClick={refetch}oronClick={() => refetch()}instead.
<IconButton
icon={ { value: 'refresh' } }
onClick={ async () => { refetch() } }
/>
assets/studio/js/src/modules/object-merger/helpers/details-functions.ts:189
fieldCollectionModifiedListis computed fromitemA/itemBregardless of the currentroles. After using “Mirror view”,mainValue/targetValueswap, but this diff logic still compares the original A/B inputs, so highlighting can become incorrect. Compute the differences frommainValuevstargetValue(or frommainItemvstargetItem) so it stays consistent with the current roles.
if (field.Field.fieldtype === 'fieldcollections') {
const mainLength = mainValue?.length ?? 0
const targetLength = targetValue?.length ?? 0
const mainList = targetLength > mainLength ? itemB : itemA
const targetList = mainLength < targetLength ? itemA : itemB
const differences = differenceWith(
mainList?.fieldValue as IFieldCollectionValue[] ?? [],
targetList?.fieldValue as IFieldCollectionValue[] ?? [],
(item1, item2) => {
return item1?.type === item2?.type && isEqual(item1?.data, item2?.data)
}
)
field.fieldCollectionModifiedList = differences.map(item => item.type)
}
assets/studio/js/src/modules/object-merger/hooks/use-object-merger-data.tsx:181
applyAlldoesArray.find(...)on bothformattedDataMainandformattedDataTargetinside a loop overformattedFullData, which is quadratic. For objects with many fields this can become noticeably slow. Consider pre-indexingformattedDataMain/TargetbyfieldPath(e.g.Map<string, IFormattedFieldData>) before the loop.
formattedFullData.forEach((fieldItem) => {
const mainItem = formattedDataMain.find(item => item.fieldPath === fieldItem.fieldPath)
const targetItem = formattedDataTarget.find(item => item.fieldPath === fieldItem.fieldPath)
assets/studio/rsbuild.config.ts:8
- This file mixes semicolon-terminated imports with non-semicolon imports. To keep formatting consistent (and avoid lint/prettier churn), align the new imports with the existing style used in this repo/file (either all semicolons or none).
import { defineConfig } from '@rsbuild/core'
import { pluginReact } from '@rsbuild/plugin-react'
import { pluginModuleFederation } from '@module-federation/rsbuild-plugin';
import { pluginGenerateEntrypoints } from '@pimcore/studio-ui-bundle/rsbuild/plugins'
import { createDynamicRemote } from '@pimcore/studio-ui-bundle/rsbuild/utils';
import path from 'node:path'
import fs from 'node:fs';
import { v4 } from 'uuid';
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.



Task: #63