From 1a1a187c26be23e78e0a682f5782688d50b7f7cf Mon Sep 17 00:00:00 2001 From: Hassan Adam Date: Thu, 14 Aug 2025 11:57:11 +0200 Subject: [PATCH 1/2] fature: add link to refences --- apps/sensenet/src/application-paths.ts | 34 ++++---- .../src/components/content/CustomContent.tsx | 1 + .../field-controls/reference-grid.tsx | 2 + .../reference-grid/default-item-template.tsx | 78 ++++++++++++++++++- .../reference-grid/reference-grid.tsx | 12 ++- 5 files changed, 106 insertions(+), 21 deletions(-) diff --git a/apps/sensenet/src/application-paths.ts b/apps/sensenet/src/application-paths.ts index 0d640e2ba..079593a5f 100644 --- a/apps/sensenet/src/application-paths.ts +++ b/apps/sensenet/src/application-paths.ts @@ -30,28 +30,28 @@ type RoutesWithContentBrowser = keyof Pick< type RoutesWithActionParam = keyof Pick type Options = - | { path: (typeof PATHS)['events']['appPath']; params?: { eventGuid: string;[index: string]: string } } + | { path: (typeof PATHS)['events']['appPath']; params?: { eventGuid: string; [index: string]: string } } | { - path: (typeof PATHS)[RoutesWithContentBrowser]['appPath'] - params: { browseType: (typeof BrowseType)[number]; action?: string;[index: string]: string | undefined } - } + path: (typeof PATHS)[RoutesWithContentBrowser]['appPath'] + params: { browseType: (typeof BrowseType)[number]; action?: string; [index: string]: string | undefined } + } | { - path: (typeof PATHS)['custom']['appPath'] - params: { - browseType: (typeof BrowseType)[number] - path: string - action?: string - [index: string]: string | undefined + path: (typeof PATHS)['custom']['appPath'] + params: { + browseType: (typeof BrowseType)[number] + path: string + action?: string + [index: string]: string | undefined + } } - } | { - path: (typeof PATHS)[RoutesWithActionParam]['appPath'] - params?: { action: string;[index: string]: string } - } + path: (typeof PATHS)[RoutesWithActionParam]['appPath'] + params?: { action: string; [index: string]: string } + } | { - path: (typeof PATHS)['settings']['appPath'] - params?: { submenu: SettingsItemType;[index: string]: string | SettingsItemType } - } + path: (typeof PATHS)['settings']['appPath'] + params?: { submenu: SettingsItemType; [index: string]: string | SettingsItemType } + } export const resolvePathParams = ({ path, params }: Options) => { let currentPath: string = path diff --git a/apps/sensenet/src/components/content/CustomContent.tsx b/apps/sensenet/src/components/content/CustomContent.tsx index a9103d4ea..f5aedcdb5 100644 --- a/apps/sensenet/src/components/content/CustomContent.tsx +++ b/apps/sensenet/src/components/content/CustomContent.tsx @@ -14,6 +14,7 @@ export const CustomContent: FunctionComponent = () => { const customDrawer = settings.drawer.items.find((item) => item.settings?.appPath === match.params.path) const path = customDrawer?.settings.root || settings.content.root || ConstantContent.PORTAL_ROOT.Path const { currentPath, onNavigate } = useTreeNavigation(path) + console.log(path, currentPath, match.params) switch (match.params.browseType) { default: diff --git a/apps/sensenet/src/components/field-controls/reference-grid.tsx b/apps/sensenet/src/components/field-controls/reference-grid.tsx index f0329bc67..251ce1dbe 100644 --- a/apps/sensenet/src/components/field-controls/reference-grid.tsx +++ b/apps/sensenet/src/components/field-controls/reference-grid.tsx @@ -1,6 +1,7 @@ import { ReactClientFieldSetting, ReferenceGrid as SnReferenceGrid } from '@sensenet/controls-react' import { clsx } from 'clsx' import React from 'react' +import { PATHS } from '../../application-paths' import { useGlobalStyles } from '../../globalStyles' import { DialogTitle } from '../dialogs/dialog-title' import { Icon } from '../Icon' @@ -14,6 +15,7 @@ export const ReferenceGrid: React.FC = (props) => { dialogTitleComponent={DialogTitle} renderPickerIcon={(item) => } pickerClasses={{ cancelButton: globalClasses.cancelButton }} + paths={PATHS} /> ) } diff --git a/packages/sn-controls-react/src/fieldcontrols/reference-grid/default-item-template.tsx b/packages/sn-controls-react/src/fieldcontrols/reference-grid/default-item-template.tsx index 170bff9f5..1bead2df1 100644 --- a/packages/sn-controls-react/src/fieldcontrols/reference-grid/default-item-template.tsx +++ b/packages/sn-controls-react/src/fieldcontrols/reference-grid/default-item-template.tsx @@ -1,5 +1,7 @@ +/* eslint-disable require-jsdoc */ import { Avatar, + createStyles, Icon, IconButton, ListItem, @@ -7,6 +9,7 @@ import { ListItemIcon, ListItemSecondaryAction, ListItemText, + makeStyles, } from '@material-ui/core' import { InsertDriveFile } from '@material-ui/icons' import { Repository } from '@sensenet/client-core' @@ -15,6 +18,42 @@ import { GenericContent, Image, User } from '@sensenet/default-content-types' import React from 'react' import { renderIconDefault } from '../icon' +export type PathConfig = { + appPath: string + snPath?: string +} + +export type Paths = Record + +function getAppPathAndContent(PATHS: Paths, targetPath: string) { + const matches = Object.entries(PATHS) + .filter(([, config]) => config.snPath && targetPath.startsWith(config.snPath)) + .sort((a, b) => b[1].snPath!.length - a[1].snPath!.length) + + if (!matches[0]) return undefined + + const [, config] = matches[0] + const contentePath = targetPath.substring(config.snPath!.length) + + return { + appPath: config.appPath, + contentePath, + } +} + +function buildCustomPath(path: string, action: string | undefined, contentePath: string) { + const customPath = path + .replace(':browseType', 'explorer') + .replace('/:path', '') // Remove the path parameter + .replace(':action?', action || 'default') + + const url = new URL(window.location.origin) + url.pathname = customPath + url.searchParams.set('content', contentePath) + + return url.toString() +} + interface DefaultItemTemplateProps { content: GenericContent remove?: (id: number) => void @@ -24,13 +63,29 @@ interface DefaultItemTemplateProps { repository?: Repository multiple: boolean renderIcon?: (name: string) => JSX.Element + paths: Paths } +const useStyles = makeStyles(() => + createStyles({ + referenceItemText: { + textAlign: 'left', + paddingRight: 15, + cursor: 'pointer', + '&[data-clickable="true"]:hover': { + textDecoration: 'underline', + }, + }, + }), +) + /** * Represents a default renderer for reference grid row */ export const DefaultItemTemplate: React.FC = (props) => { - const { content, repository } = props + const { content, repository, paths } = props + + const classes = useStyles() const renderIcon = (item: GenericContent | User | Image) => { if (repository?.schemas.isContentFromType(item, 'User')) { @@ -107,7 +162,26 @@ export const DefaultItemTemplate: React.FC = (props) = return ( {content.Type ? renderIcon(content) : null} - + { + if (content.Id === -1 || !paths) { + return + } + const referencedItemPaths = getAppPathAndContent(paths, content.Path) + + if (!referencedItemPaths) { + return + } + + const { appPath, contentePath } = referencedItemPaths + + const fullUrl = buildCustomPath(appPath, props.actionName, contentePath) + window.location.href = fullUrl + }} + primary={content.DisplayName} + className={classes.referenceItemText} + data-clickable={content.Id !== -1} + /> {props.actionName && props.actionName !== 'browse' && !props.readOnly ? ( {content.Id > 0 ? ( diff --git a/packages/sn-controls-react/src/fieldcontrols/reference-grid/reference-grid.tsx b/packages/sn-controls-react/src/fieldcontrols/reference-grid/reference-grid.tsx index 3259efc0c..dd07af012 100644 --- a/packages/sn-controls-react/src/fieldcontrols/reference-grid/reference-grid.tsx +++ b/packages/sn-controls-react/src/fieldcontrols/reference-grid/reference-grid.tsx @@ -16,7 +16,7 @@ import { PickerClassKey } from '@sensenet/pickers-react' import React, { ElementType, useCallback, useEffect, useMemo, useState } from 'react' import { ReactClientFieldSetting } from '../client-field-setting' import { defaultLocalization } from '../localization' -import { DefaultItemTemplate } from './default-item-template' +import { DefaultItemTemplate, Paths } from './default-item-template' import { ReferencePicker } from './reference-picker' const styles = { @@ -38,6 +38,7 @@ interface ReferenceGridProps extends ReactClientFieldSetting JSX.Element pickerClasses?: PickerClassKey + paths: Paths } export const ReferenceGrid: React.FC = (props) => { @@ -185,7 +186,11 @@ export const ReferenceGrid: React.FC = (props) => { case 'new': case 'edit': return ( - + = (props) => { repository={props.repository} multiple={props.settings.AllowMultiple ? props.settings.AllowMultiple : false} renderIcon={props.renderIcon} + paths={props.paths} /> ))} {!props.settings.ReadOnly ? ( @@ -221,6 +227,7 @@ export const ReferenceGrid: React.FC = (props) => { repository={props.repository} multiple={props.settings.AllowMultiple ? props.settings.AllowMultiple : false} renderIcon={props.renderIcon} + paths={props.paths} /> ) : null} @@ -271,6 +278,7 @@ export const ReferenceGrid: React.FC = (props) => { repository={props.repository} multiple={props.settings.AllowMultiple ? props.settings.AllowMultiple : false} renderIcon={props.renderIcon} + paths={props.paths} /> )) ) : ( From 66a700622d6cf99ef5facfd2e897720ad9554477 Mon Sep 17 00:00:00 2001 From: Hassan Adam Date: Thu, 14 Aug 2025 12:27:06 +0200 Subject: [PATCH 2/2] fix: remove console log --- apps/sensenet/src/components/content/CustomContent.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/sensenet/src/components/content/CustomContent.tsx b/apps/sensenet/src/components/content/CustomContent.tsx index f5aedcdb5..a9103d4ea 100644 --- a/apps/sensenet/src/components/content/CustomContent.tsx +++ b/apps/sensenet/src/components/content/CustomContent.tsx @@ -14,7 +14,6 @@ export const CustomContent: FunctionComponent = () => { const customDrawer = settings.drawer.items.find((item) => item.settings?.appPath === match.params.path) const path = customDrawer?.settings.root || settings.content.root || ConstantContent.PORTAL_ROOT.Path const { currentPath, onNavigate } = useTreeNavigation(path) - console.log(path, currentPath, match.params) switch (match.params.browseType) { default: