From 4ad6f5db42452c83cd9bdcbd28066faeb713422d Mon Sep 17 00:00:00 2001 From: Hassan Adam Date: Wed, 13 Aug 2025 15:19:00 +0200 Subject: [PATCH 01/15] feature: schema selector field #2217 --- .../src/components/dialogs/dialogs.tsx | 2 +- apps/sensenet/src/components/dialogs/index.ts | 2 +- .../dialogs/operations/influenceField.tsx | 41 +++++++++++++++++++ .../dialogs/{ => operations}/operations.tsx | 34 ++++++++++----- package.json | 5 ++- 5 files changed, 69 insertions(+), 15 deletions(-) create mode 100644 apps/sensenet/src/components/dialogs/operations/influenceField.tsx rename apps/sensenet/src/components/dialogs/{ => operations}/operations.tsx (86%) diff --git a/apps/sensenet/src/components/dialogs/dialogs.tsx b/apps/sensenet/src/components/dialogs/dialogs.tsx index 9f388c3e5..81e6fad2d 100644 --- a/apps/sensenet/src/components/dialogs/dialogs.tsx +++ b/apps/sensenet/src/components/dialogs/dialogs.tsx @@ -24,7 +24,7 @@ const ChangePasswordDialog = lazy(() => import('./change-password')) const DateRangePicker = lazy(() => import('./date-range-picker')) const AddDeleteUserGroups = lazy(() => import('./add-delete-user-groups')) const ColumnSettings = lazy(() => import('./column-settings')) -const Operations = lazy(() => import('./operations')) +const Operations = lazy(() => import('./operations/operations')) function dialogRenderer(dialog: DialogWithProps) { switch (dialog.name) { diff --git a/apps/sensenet/src/components/dialogs/index.ts b/apps/sensenet/src/components/dialogs/index.ts index c4e89b06a..4180c1449 100644 --- a/apps/sensenet/src/components/dialogs/index.ts +++ b/apps/sensenet/src/components/dialogs/index.ts @@ -19,4 +19,4 @@ export * from './restore' export * from './save-query' export * from './add-delete-user-groups' export * from './column-settings' -export * from './operations' +export * from './operations/operations' diff --git a/apps/sensenet/src/components/dialogs/operations/influenceField.tsx b/apps/sensenet/src/components/dialogs/operations/influenceField.tsx new file mode 100644 index 000000000..fdeb84fd8 --- /dev/null +++ b/apps/sensenet/src/components/dialogs/operations/influenceField.tsx @@ -0,0 +1,41 @@ +import React from 'react' + +export type TInfluenceField = { + name?: string + description?: string + radioOptions: Array<{ optionTitle: string; influencedFieldId: string; value: string }> +} + +export const InfluenceField: React.FC = ({ name, description, radioOptions }) => { + return ( +
+

{name}

+

{description}

+ + {radioOptions.map((option) => { + const { optionTitle, value, influencedFieldId } = option + + return ( + <> + { + const element = document.getElementById(influencedFieldId) as HTMLInputElement + + if (!element) { + return + } + + element.value = value + }} + /> + + + ) + })} +
+ ) +} diff --git a/apps/sensenet/src/components/dialogs/operations.tsx b/apps/sensenet/src/components/dialogs/operations/operations.tsx similarity index 86% rename from apps/sensenet/src/components/dialogs/operations.tsx rename to apps/sensenet/src/components/dialogs/operations/operations.tsx index e187c6603..3c18987e4 100644 --- a/apps/sensenet/src/components/dialogs/operations.tsx +++ b/apps/sensenet/src/components/dialogs/operations/operations.tsx @@ -2,11 +2,12 @@ import { Button, createStyles, DialogActions, DialogContent, makeStyles, TextFie import { GenericContent } from '@sensenet/default-content-types' import { useLogger, useRepository } from '@sensenet/hooks-react' import React, { useEffect, useRef, useState } from 'react' -import { useCurrentUser } from '../../context' -import { useGlobalStyles } from '../../globalStyles' -import { useLocalization } from '../../hooks' -import { Icon } from '../Icon' -import { DialogTitle, useDialog } from '.' +import { DialogTitle, useDialog } from '..' +import { useCurrentUser } from '../../../context' +import { useGlobalStyles } from '../../../globalStyles' +import { useLocalization } from '../../../hooks' +import { Icon } from '../../Icon' +import { InfluenceField, TInfluenceField } from './influenceField' export interface OperationsDialogProps { content: GenericContent @@ -15,16 +16,23 @@ export interface OperationsDialogProps { /*Ezt itt jól ki kell dolgozni!!! nem végleges csak demora van egyszerűsítve Valószínüleg nem is itt lesz a végleges helye hanem ott ahol a GenericContent van */ -type UIDescription = { + +type baseDescriptionFields = { title?: string submitTitle?: string - elements: Array<{ - name?: string - description?: string - inputProps: React.HTMLProps - }> } +type simpleInputField = { + name?: string + description?: string + inputProps: React.HTMLProps +} + +type UIDescription = + | baseDescriptionFields & { + elements: Array + } + type OperationResult = { ToastMessage?: string } @@ -133,6 +141,10 @@ export function OperationsDialog(props: OperationsDialogProps) { submitAction(e) }}> {UIDescription?.elements.map((field, index) => { + if ('radioOptions' in field) { + return + } + const { inputProps, description, name } = field return ( diff --git a/package.json b/package.json index b7868900b..fae111ed8 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ }, "scripts": { "postinstall": "husky install", - "cy:run:dms": "cypress run --project ./examples/sn-dms-demo", + "cy:run:dms": "cypress run --project ./examples/sn-dms-demo", "start:dms:e2e": "cross-env NODE_OPTIONS=--openssl-legacy-provider yarn workspace sn-dms-demo start:e2e", "test:dms:e2e": "cross-env NODE_OPTIONS=--openssl-legacy-provider start-server-and-test start:dms:e2e http-get://localhost:3000 cy:run:dms", "snapp": "yarn workspace @app/sensenet", @@ -78,5 +78,6 @@ "prettier --write" ] }, - "dependencies": {} + "dependencies": {}, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } From ec76acb45019f398f8bebd11f4e010f85a86084e Mon Sep 17 00:00:00 2001 From: Hassan Adam Date: Wed, 13 Aug 2025 15:26:15 +0200 Subject: [PATCH 02/15] feature: add extra class --- .../src/components/dialogs/operations/influenceField.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/sensenet/src/components/dialogs/operations/influenceField.tsx b/apps/sensenet/src/components/dialogs/operations/influenceField.tsx index fdeb84fd8..7ff3203ca 100644 --- a/apps/sensenet/src/components/dialogs/operations/influenceField.tsx +++ b/apps/sensenet/src/components/dialogs/operations/influenceField.tsx @@ -8,7 +8,7 @@ export type TInfluenceField = { export const InfluenceField: React.FC = ({ name, description, radioOptions }) => { return ( -
+

{name}

{description}

From 546b3d51d0fa7cb23685859ade2fb8072f1262e3 Mon Sep 17 00:00:00 2001 From: Hassan Adam Date: Wed, 13 Aug 2025 15:31:21 +0200 Subject: [PATCH 03/15] refactor: revert package manager --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fae111ed8..dc876d492 100644 --- a/package.json +++ b/package.json @@ -79,5 +79,5 @@ ] }, "dependencies": {}, - "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" + "packageManager": "" } From 86ea865c3edb32cb5c202823eb921d1f85780620 Mon Sep 17 00:00:00 2001 From: Hassan Adam Date: Wed, 13 Aug 2025 15:33:13 +0200 Subject: [PATCH 04/15] remove package mamnager --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index dc876d492..0ca6d88fe 100644 --- a/package.json +++ b/package.json @@ -78,6 +78,5 @@ "prettier --write" ] }, - "dependencies": {}, - "packageManager": "" + "dependencies": {} } From 583bbb7b0c667bdcbe0bb63ff7834907dc1d7e67 Mon Sep 17 00:00:00 2001 From: Hassan Adam Date: Wed, 13 Aug 2025 15:56:48 +0200 Subject: [PATCH 05/15] fix: undefined on no message --- apps/sensenet/src/components/dialogs/operations/operations.tsx | 2 +- package.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/sensenet/src/components/dialogs/operations/operations.tsx b/apps/sensenet/src/components/dialogs/operations/operations.tsx index 3c18987e4..f539c2336 100644 --- a/apps/sensenet/src/components/dialogs/operations/operations.tsx +++ b/apps/sensenet/src/components/dialogs/operations/operations.tsx @@ -107,7 +107,7 @@ export function OperationsDialog(props: OperationsDialogProps) { body: formJson, }) - const success = `: ${result?.ToastMessage}` || '' + const success = result.ToastMessage ? `: ${result?.ToastMessage}` : '' logger.information({ message: `${localization.success}${success}` }) diff --git a/package.json b/package.json index 0ca6d88fe..fae111ed8 100644 --- a/package.json +++ b/package.json @@ -78,5 +78,6 @@ "prettier --write" ] }, - "dependencies": {} + "dependencies": {}, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } From 773a8e207f9e97748fc3b024cf1228279c746cd2 Mon Sep 17 00:00:00 2001 From: Hassan Adam Date: Wed, 13 Aug 2025 15:59:54 +0200 Subject: [PATCH 06/15] refactor: modify comment --- .husky/pre-commit | 0 .../sensenet/src/components/dialogs/operations/operations.tsx | 4 +--- 2 files changed, 1 insertion(+), 3 deletions(-) mode change 100644 => 100755 .husky/pre-commit diff --git a/.husky/pre-commit b/.husky/pre-commit old mode 100644 new mode 100755 diff --git a/apps/sensenet/src/components/dialogs/operations/operations.tsx b/apps/sensenet/src/components/dialogs/operations/operations.tsx index f539c2336..f8e291890 100644 --- a/apps/sensenet/src/components/dialogs/operations/operations.tsx +++ b/apps/sensenet/src/components/dialogs/operations/operations.tsx @@ -13,9 +13,7 @@ export interface OperationsDialogProps { content: GenericContent OperationName: string } -/*Ezt itt jól ki kell dolgozni!!! nem végleges csak demora van egyszerűsítve - Valószínüleg nem is itt lesz a végleges helye hanem ott ahol a GenericContent van -*/ +/*Ezt itt jól ki kell dolgozni!!! nem végleges csak demora van egyszerűsítve*/ type baseDescriptionFields = { title?: string From ceb4d98f5e63ba575bdadcb690afb00d77df043e Mon Sep 17 00:00:00 2001 From: Hassan Adam Date: Wed, 13 Aug 2025 16:13:36 +0200 Subject: [PATCH 07/15] change pre-comit hook --- .husky/pre-commit | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.husky/pre-commit b/.husky/pre-commit index 4cb55fda0..d2ae35e84 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,14 +1,4 @@ #!/bin/sh . "$(dirname "$0")/_/husky.sh" -# Detect /dev/tty using readlink and fd/2 -tty=$(readlink /proc/$$/fd/2) - -# Use the detected tty for redirection -if [[ -n "$tty" ]]; then - exec >"$tty" 2>&1 -else - echo "Could not detect /dev/tty" -fi - yarn lint-staged From 372f1d16c0720417777c94ef8a4088d3f94e2d24 Mon Sep 17 00:00:00 2001 From: Hassan Adam Date: Wed, 13 Aug 2025 16:24:13 +0200 Subject: [PATCH 08/15] refacotr builds --- .github/workflows/deploy-preview.yml | 55 ++++++++++++++-------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/.github/workflows/deploy-preview.yml b/.github/workflows/deploy-preview.yml index c8a43d221..d0ba35e01 100644 --- a/.github/workflows/deploy-preview.yml +++ b/.github/workflows/deploy-preview.yml @@ -28,8 +28,8 @@ jobs: env: RELATIVE_CI_KEY: ${{ secrets.RELATIVE_CI_KEY }} - - name: wake up deploy notifier - run: yarn wait-on https://sensenet-sn-deploy-notifier.glitch.me/ -l -t 300000 -i 10000 + # - name: wake up deploy notifier + # run: yarn wait-on https://sensenet-sn-deploy-notifier.glitch.me/ -l -t 300000 -i 10000 - name: Publish run: npx netlify-cli@v2.41.0 deploy --dir=./apps/sensenet/build --message ${{ github.event.pull_request.number }} @@ -37,34 +37,35 @@ jobs: NETLIFY_SITE_ID: 94fb346c-b540-40f7-aaaf-21eee2a9c891 NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} - deploy-storybook: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 + # i have remve storybook implementation we will never us it. + # deploy-storybook: + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v2 - - uses: actions/cache@v4 # Updated from v1 to v4 - with: - path: ~/.cache/yarn - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- + # - uses: actions/cache@v4 # Updated from v1 to v4 + # with: + # path: ~/.cache/yarn + # key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + # restore-keys: | + # ${{ runner.os }}-yarn- - - name: install - run: yarn install - env: - CYPRESS_INSTALL_BINARY: 0 + # - name: install + # run: yarn install + # env: + # CYPRESS_INSTALL_BINARY: 0 - - name: build - run: yarn build + # - name: build + # run: yarn build - - name: build storybook - run: yarn storybook build-storybook + # - name: build storybook + # run: yarn storybook build-storybook - - name: wake up deploy notifier - run: yarn wait-on https://sensenet-sn-deploy-notifier.glitch.me/ -l -t 300000 -i 10000 + # - name: wake up deploy notifier + # run: yarn wait-on https://sensenet-sn-deploy-notifier.glitch.me/ -l -t 300000 -i 10000 - - name: Publish - run: npx netlify-cli@v2.41.0 deploy --dir=./examples/sn-react-component-docs/storybook-static --message ${{ github.event.pull_request.number }} - env: - NETLIFY_SITE_ID: 1747b330-27d8-4ddd-bf74-39469c257010 - NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} + # - name: Publish + # run: npx netlify-cli@v2.41.0 deploy --dir=./examples/sn-react-component-docs/storybook-static --message ${{ github.event.pull_request.number }} + # env: + # NETLIFY_SITE_ID: 1747b330-27d8-4ddd-bf74-39469c257010 + # NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} From 0530f2708c792b3528f7e1e4c4d1a429a9f270c0 Mon Sep 17 00:00:00 2001 From: Hassan Adam Date: Wed, 13 Aug 2025 16:34:48 +0200 Subject: [PATCH 09/15] revert ci cahnges --- .github/workflows/deploy-preview.yml | 55 ++++++++++++++-------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/.github/workflows/deploy-preview.yml b/.github/workflows/deploy-preview.yml index d0ba35e01..c8a43d221 100644 --- a/.github/workflows/deploy-preview.yml +++ b/.github/workflows/deploy-preview.yml @@ -28,8 +28,8 @@ jobs: env: RELATIVE_CI_KEY: ${{ secrets.RELATIVE_CI_KEY }} - # - name: wake up deploy notifier - # run: yarn wait-on https://sensenet-sn-deploy-notifier.glitch.me/ -l -t 300000 -i 10000 + - name: wake up deploy notifier + run: yarn wait-on https://sensenet-sn-deploy-notifier.glitch.me/ -l -t 300000 -i 10000 - name: Publish run: npx netlify-cli@v2.41.0 deploy --dir=./apps/sensenet/build --message ${{ github.event.pull_request.number }} @@ -37,35 +37,34 @@ jobs: NETLIFY_SITE_ID: 94fb346c-b540-40f7-aaaf-21eee2a9c891 NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} - # i have remve storybook implementation we will never us it. - # deploy-storybook: - # runs-on: ubuntu-latest - # steps: - # - uses: actions/checkout@v2 + deploy-storybook: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 - # - uses: actions/cache@v4 # Updated from v1 to v4 - # with: - # path: ~/.cache/yarn - # key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - # restore-keys: | - # ${{ runner.os }}-yarn- + - uses: actions/cache@v4 # Updated from v1 to v4 + with: + path: ~/.cache/yarn + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- - # - name: install - # run: yarn install - # env: - # CYPRESS_INSTALL_BINARY: 0 + - name: install + run: yarn install + env: + CYPRESS_INSTALL_BINARY: 0 - # - name: build - # run: yarn build + - name: build + run: yarn build - # - name: build storybook - # run: yarn storybook build-storybook + - name: build storybook + run: yarn storybook build-storybook - # - name: wake up deploy notifier - # run: yarn wait-on https://sensenet-sn-deploy-notifier.glitch.me/ -l -t 300000 -i 10000 + - name: wake up deploy notifier + run: yarn wait-on https://sensenet-sn-deploy-notifier.glitch.me/ -l -t 300000 -i 10000 - # - name: Publish - # run: npx netlify-cli@v2.41.0 deploy --dir=./examples/sn-react-component-docs/storybook-static --message ${{ github.event.pull_request.number }} - # env: - # NETLIFY_SITE_ID: 1747b330-27d8-4ddd-bf74-39469c257010 - # NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} + - name: Publish + run: npx netlify-cli@v2.41.0 deploy --dir=./examples/sn-react-component-docs/storybook-static --message ${{ github.event.pull_request.number }} + env: + NETLIFY_SITE_ID: 1747b330-27d8-4ddd-bf74-39469c257010 + NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} From edae682a2de6160554ad68bb3ef8aac59ca6b0b9 Mon Sep 17 00:00:00 2001 From: Hassan Adam Date: Wed, 13 Aug 2025 16:52:21 +0200 Subject: [PATCH 10/15] remove wake up notifyier --- .github/workflows/deploy-preview.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/deploy-preview.yml b/.github/workflows/deploy-preview.yml index c8a43d221..eef9f81e4 100644 --- a/.github/workflows/deploy-preview.yml +++ b/.github/workflows/deploy-preview.yml @@ -27,10 +27,6 @@ jobs: run: yarn snapp build env: RELATIVE_CI_KEY: ${{ secrets.RELATIVE_CI_KEY }} - - - name: wake up deploy notifier - run: yarn wait-on https://sensenet-sn-deploy-notifier.glitch.me/ -l -t 300000 -i 10000 - - name: Publish run: npx netlify-cli@v2.41.0 deploy --dir=./apps/sensenet/build --message ${{ github.event.pull_request.number }} env: From 9bd9000cd5baad78cfa91c7f7c328da4ac7dd827 Mon Sep 17 00:00:00 2001 From: Hassan Adam Date: Wed, 13 Aug 2025 17:02:43 +0200 Subject: [PATCH 11/15] remove story book ci --- .github/workflows/deploy-preview.yml | 32 ---------------------------- 1 file changed, 32 deletions(-) diff --git a/.github/workflows/deploy-preview.yml b/.github/workflows/deploy-preview.yml index eef9f81e4..6ef02634c 100644 --- a/.github/workflows/deploy-preview.yml +++ b/.github/workflows/deploy-preview.yml @@ -32,35 +32,3 @@ jobs: env: NETLIFY_SITE_ID: 94fb346c-b540-40f7-aaaf-21eee2a9c891 NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} - - deploy-storybook: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - - uses: actions/cache@v4 # Updated from v1 to v4 - with: - path: ~/.cache/yarn - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - - name: install - run: yarn install - env: - CYPRESS_INSTALL_BINARY: 0 - - - name: build - run: yarn build - - - name: build storybook - run: yarn storybook build-storybook - - - name: wake up deploy notifier - run: yarn wait-on https://sensenet-sn-deploy-notifier.glitch.me/ -l -t 300000 -i 10000 - - - name: Publish - run: npx netlify-cli@v2.41.0 deploy --dir=./examples/sn-react-component-docs/storybook-static --message ${{ github.event.pull_request.number }} - env: - NETLIFY_SITE_ID: 1747b330-27d8-4ddd-bf74-39469c257010 - NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} From 1a1a187c26be23e78e0a682f5782688d50b7f7cf Mon Sep 17 00:00:00 2001 From: Hassan Adam Date: Thu, 14 Aug 2025 11:57:11 +0200 Subject: [PATCH 12/15] 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 13/15] 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: From 384b832b4ecb1f6938f41791f801bfb256bd9f16 Mon Sep 17 00:00:00 2001 From: Hassan Adam Date: Thu, 21 Aug 2025 12:00:41 +0200 Subject: [PATCH 14/15] feature: loading state to operation --- .../dialogs/operations/operations.tsx | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/apps/sensenet/src/components/dialogs/operations/operations.tsx b/apps/sensenet/src/components/dialogs/operations/operations.tsx index f8e291890..edf7c4ade 100644 --- a/apps/sensenet/src/components/dialogs/operations/operations.tsx +++ b/apps/sensenet/src/components/dialogs/operations/operations.tsx @@ -1,4 +1,12 @@ -import { Button, createStyles, DialogActions, DialogContent, makeStyles, TextField } from '@material-ui/core' +import { + Button, + CircularProgress, + createStyles, + DialogActions, + DialogContent, + makeStyles, + TextField, +} from '@material-ui/core' import { GenericContent } from '@sensenet/default-content-types' import { useLogger, useRepository } from '@sensenet/hooks-react' import React, { useEffect, useRef, useState } from 'react' @@ -8,7 +16,6 @@ import { useGlobalStyles } from '../../../globalStyles' import { useLocalization } from '../../../hooks' import { Icon } from '../../Icon' import { InfluenceField, TInfluenceField } from './influenceField' - export interface OperationsDialogProps { content: GenericContent OperationName: string @@ -67,6 +74,7 @@ export function OperationsDialog(props: OperationsDialogProps) { const globalClasses = useGlobalStyles() const [UIDescription, setUIDescription] = useState() + const [isOperationSubmiting, setIsOperationSubmiting] = useState(false) useEffect(() => { console.log(props.OperationName) @@ -87,6 +95,7 @@ export function OperationsDialog(props: OperationsDialogProps) { }, [logger, props.OperationName, props.content.Path, repository]) const submitAction = async (e: React.FormEvent) => { + setIsOperationSubmiting(true) e.preventDefault() if (!formRef.current) return @@ -112,6 +121,8 @@ export function OperationsDialog(props: OperationsDialogProps) { closeLastDialog() } catch (error) { logger.error({ message: error.message }) + } finally { + setIsOperationSubmiting(false) } } @@ -171,6 +182,8 @@ export function OperationsDialog(props: OperationsDialogProps) { color="primary" variant="contained" type="submit" + disabled={isOperationSubmiting} + endIcon={isOperationSubmiting && } autoFocus={true}> {UIDescription?.submitTitle || localization.submit} From 13901122d275ef0f769611281d758c86cd7659f0 Mon Sep 17 00:00:00 2001 From: VargaJoe Date: Mon, 8 Jun 2026 17:30:02 +0200 Subject: [PATCH 15/15] merge: integrate develop into sn-auth-extraimprovements - package.json: add packageManager and empty dependencies from develop - operations dialog: combine null-safe elements?.map with InfluenceField support - default-item-template: take develop version, export getAppPathAndContent/buildCustomPath helpers - reference-grid: keep ag-grid table view, add clickable Path column navigation, accept paths/dialogTitleComponent props from develop --- .github/workflows/deploy-preview.yml | 4 - .husky/pre-commit | 10 -- .../src/components/dialogs/dialogs.tsx | 2 +- apps/sensenet/src/components/dialogs/index.ts | 2 +- .../dialogs/operations/influenceField.tsx | 41 +++++ .../dialogs/{ => operations}/operations.tsx | 57 ++++--- .../field-controls/reference-grid.tsx | 2 + package.json | 4 +- .../reference-grid/default-item-template.tsx | 98 +++++++++--- .../reference-grid/reference-grid.tsx | 143 ++++++++++++------ 10 files changed, 258 insertions(+), 105 deletions(-) mode change 100644 => 100755 .husky/pre-commit create mode 100644 apps/sensenet/src/components/dialogs/operations/influenceField.tsx rename apps/sensenet/src/components/dialogs/{ => operations}/operations.tsx (77%) diff --git a/.github/workflows/deploy-preview.yml b/.github/workflows/deploy-preview.yml index a55f4f6be..3c3ea3828 100644 --- a/.github/workflows/deploy-preview.yml +++ b/.github/workflows/deploy-preview.yml @@ -27,10 +27,6 @@ jobs: run: yarn snapp build env: RELATIVE_CI_KEY: ${{ secrets.RELATIVE_CI_KEY }} - - - name: wake up deploy notifier - run: yarn wait-on https://sensenet-sn-deploy-notifier.glitch.me/ -l -t 300000 -i 10000 - - name: Publish run: npx netlify-cli@v2.41.0 deploy --dir=./apps/sensenet/build --message ${{ github.event.pull_request.number }} env: diff --git a/.husky/pre-commit b/.husky/pre-commit old mode 100644 new mode 100755 index 4cb55fda0..d2ae35e84 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,14 +1,4 @@ #!/bin/sh . "$(dirname "$0")/_/husky.sh" -# Detect /dev/tty using readlink and fd/2 -tty=$(readlink /proc/$$/fd/2) - -# Use the detected tty for redirection -if [[ -n "$tty" ]]; then - exec >"$tty" 2>&1 -else - echo "Could not detect /dev/tty" -fi - yarn lint-staged diff --git a/apps/sensenet/src/components/dialogs/dialogs.tsx b/apps/sensenet/src/components/dialogs/dialogs.tsx index 9f388c3e5..81e6fad2d 100644 --- a/apps/sensenet/src/components/dialogs/dialogs.tsx +++ b/apps/sensenet/src/components/dialogs/dialogs.tsx @@ -24,7 +24,7 @@ const ChangePasswordDialog = lazy(() => import('./change-password')) const DateRangePicker = lazy(() => import('./date-range-picker')) const AddDeleteUserGroups = lazy(() => import('./add-delete-user-groups')) const ColumnSettings = lazy(() => import('./column-settings')) -const Operations = lazy(() => import('./operations')) +const Operations = lazy(() => import('./operations/operations')) function dialogRenderer(dialog: DialogWithProps) { switch (dialog.name) { diff --git a/apps/sensenet/src/components/dialogs/index.ts b/apps/sensenet/src/components/dialogs/index.ts index c4e89b06a..4180c1449 100644 --- a/apps/sensenet/src/components/dialogs/index.ts +++ b/apps/sensenet/src/components/dialogs/index.ts @@ -19,4 +19,4 @@ export * from './restore' export * from './save-query' export * from './add-delete-user-groups' export * from './column-settings' -export * from './operations' +export * from './operations/operations' diff --git a/apps/sensenet/src/components/dialogs/operations/influenceField.tsx b/apps/sensenet/src/components/dialogs/operations/influenceField.tsx new file mode 100644 index 000000000..7ff3203ca --- /dev/null +++ b/apps/sensenet/src/components/dialogs/operations/influenceField.tsx @@ -0,0 +1,41 @@ +import React from 'react' + +export type TInfluenceField = { + name?: string + description?: string + radioOptions: Array<{ optionTitle: string; influencedFieldId: string; value: string }> +} + +export const InfluenceField: React.FC = ({ name, description, radioOptions }) => { + return ( +
+

{name}

+

{description}

+ + {radioOptions.map((option) => { + const { optionTitle, value, influencedFieldId } = option + + return ( + <> + { + const element = document.getElementById(influencedFieldId) as HTMLInputElement + + if (!element) { + return + } + + element.value = value + }} + /> + + + ) + })} +
+ ) +} diff --git a/apps/sensenet/src/components/dialogs/operations.tsx b/apps/sensenet/src/components/dialogs/operations/operations.tsx similarity index 77% rename from apps/sensenet/src/components/dialogs/operations.tsx rename to apps/sensenet/src/components/dialogs/operations/operations.tsx index fd7d5406f..3483dbc3d 100644 --- a/apps/sensenet/src/components/dialogs/operations.tsx +++ b/apps/sensenet/src/components/dialogs/operations/operations.tsx @@ -1,30 +1,43 @@ -import { Button, createStyles, DialogActions, DialogContent, makeStyles, TextField } from '@material-ui/core' +import { + Button, + CircularProgress, + createStyles, + DialogActions, + DialogContent, + makeStyles, + TextField, +} from '@material-ui/core' import { GenericContent } from '@sensenet/default-content-types' import { useLogger, useRepository, useSession } from '@sensenet/hooks-react' import React, { useEffect, useRef, useState } from 'react' -import { useCurrentUser } from '../../context' -import { useGlobalStyles } from '../../globalStyles' -import { useLocalization } from '../../hooks' -import { Icon } from '../Icon' -import { DialogTitle, useDialog } from '.' - +import { DialogTitle, useDialog } from '..' +import { useCurrentUser } from '../../../context' +import { useGlobalStyles } from '../../../globalStyles' +import { useLocalization } from '../../../hooks' +import { Icon } from '../../Icon' +import { InfluenceField, TInfluenceField } from './influenceField' export interface OperationsDialogProps { content: GenericContent OperationName: string } -/*Ezt itt jól ki kell dolgozni!!! nem végleges csak demora van egyszerűsítve - Valószínüleg nem is itt lesz a végleges helye hanem ott ahol a GenericContent van -*/ -type UIDescription = { +/*Ezt itt jól ki kell dolgozni!!! nem végleges csak demora van egyszerűsítve*/ + +type baseDescriptionFields = { title?: string submitTitle?: string - elements: Array<{ - name?: string - description?: string - inputProps: React.HTMLProps - }> } +type simpleInputField = { + name?: string + description?: string + inputProps: React.HTMLProps +} + +type UIDescription = + | baseDescriptionFields & { + elements: Array + } + type OperationResult = { ToastMessage?: string } @@ -61,6 +74,7 @@ export function OperationsDialog(props: OperationsDialogProps) { const globalClasses = useGlobalStyles() const [UIDescription, setUIDescription] = useState() + const [isOperationSubmiting, setIsOperationSubmiting] = useState(false) useEffect(() => { const loadOperation = async () => { @@ -80,6 +94,7 @@ export function OperationsDialog(props: OperationsDialogProps) { }, [logger, props.OperationName, props.content.Path, repository]) const submitAction = async (e: React.FormEvent) => { + setIsOperationSubmiting(true) e.preventDefault() if (!formRef.current) return @@ -98,13 +113,15 @@ export function OperationsDialog(props: OperationsDialogProps) { body: formJson, }) - const success = `: ${result?.ToastMessage}` || '' + const success = result.ToastMessage ? `: ${result?.ToastMessage}` : '' logger.information({ message: `${localization.success}${success}` }) closeLastDialog() } catch (error) { logger.error({ message: error.message }) + } finally { + setIsOperationSubmiting(false) } } @@ -132,6 +149,10 @@ export function OperationsDialog(props: OperationsDialogProps) { submitAction(e) }}> {UIDescription?.elements?.map((field, index) => { + if ('radioOptions' in field) { + return + } + const { inputProps, description, name } = field return ( @@ -160,6 +181,8 @@ export function OperationsDialog(props: OperationsDialogProps) { color="primary" variant="contained" type="submit" + disabled={isOperationSubmiting} + endIcon={isOperationSubmiting && } autoFocus={true}> {UIDescription?.submitTitle || localization.submit} diff --git a/apps/sensenet/src/components/field-controls/reference-grid.tsx b/apps/sensenet/src/components/field-controls/reference-grid.tsx index e269c0be7..2f3e15bc4 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 { Icon } from '../Icon' @@ -14,6 +15,7 @@ export const ReferenceGrid: React.FC = (props) => { )} pickerClasses={{ cancelButton: globalClasses.cancelButton }} + paths={PATHS} /> ) } diff --git a/package.json b/package.json index ac59cc04e..971d70fb4 100644 --- a/package.json +++ b/package.json @@ -77,5 +77,7 @@ "eslint --ext .jsx,.js --cache --fix", "prettier --write" ] - } + }, + "dependencies": {}, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } 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 f6d13cec7..a97e49919 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,9 +9,7 @@ import { ListItemIcon, ListItemSecondaryAction, ListItemText, - Table, - TableBody, - TableCell, + makeStyles, } from '@material-ui/core' import { InsertDriveFile } from '@material-ui/icons' import { Repository } from '@sensenet/client-core' @@ -18,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 + +export 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, + } +} + +export 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 @@ -27,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')) { @@ -111,26 +163,24 @@ export const DefaultItemTemplate: React.FC = (props) = {content.Type ? renderIcon(content) : null} - - - {content.Path} - - - {content.DisplayName} - - - {content.Type} - - - - ) - } - style={{ textAlign: 'left', paddingRight: 15 }} + onClick={() => { + 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 ? ( 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 f71a49f18..8240031b0 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 @@ -3,20 +3,23 @@ import { createStyles, Dialog, DialogProps, + DialogTitle, FormControl, makeStyles, TextField, Typography, useTheme, } from '@material-ui/core' -import { PathHelper } from '@sensenet/client-utils' +import { deepMerge, PathHelper } from '@sensenet/client-utils' import { GenericContent, ReferenceFieldSetting, User } from '@sensenet/default-content-types' import { GenericContentWithIsParent, PickerAdvanced, PickerClassKey } from '@sensenet/pickers-react' import { ColDef } from 'ag-grid-community' import { AgGridReact } from 'ag-grid-react' -import React, { useCallback, useEffect, useState } from 'react' +import React, { ElementType, useCallback, useEffect, useState } from 'react' import { ReactClientFieldSetting } from '../client-field-setting' import CustomLabel from '../label/custom-label' +import { defaultLocalization } from '../localization' +import { buildCustomPath, getAppPathAndContent, Paths } from './default-item-template' const useStyles = makeStyles(() => createStyles({ @@ -54,50 +57,28 @@ const useStyles = makeStyles(() => padding: '8px 8px 0', border: '2px solid grey', }, + pathCell: { + cursor: 'pointer', + '&[data-clickable="true"]:hover': { + textDecoration: 'underline', + }, + }, }), ) -const referemceGridColumns: ColDef[] = [ - { - headerName: 'Path', - field: 'Path', - headerTooltip: 'Path', - flex: 5, - filter: true, - sortable: true, - comparator: (a: string, b: string) => a.toLowerCase().localeCompare(b.toLowerCase()), - resizable: true, - }, - { - headerName: 'Display Name', - field: 'DisplayName', - headerTooltip: 'Display Name', - flex: 1.5, - filter: true, - sortable: true, - comparator: (a: string, b: string) => a.toLowerCase().localeCompare(b.toLowerCase()), - resizable: true, - }, - { - headerName: 'Type', - field: 'Type', - headerTooltip: 'Type', - flex: 1, - filter: true, - sortable: true, - resizable: true, - }, -] - interface ReferenceGridProps extends ReactClientFieldSetting { dialogProps?: Partial + dialogTitleComponent?: ElementType renderPickerIcon: (item: GenericContentWithIsParent | User) => JSX.Element pickerClasses?: PickerClassKey + paths: Paths } export const ReferenceGrid: React.FC = (props) => { const theme = useTheme() const classes = useStyles() + const localization = deepMerge(defaultLocalization.referenceGrid, props.localization?.referenceGrid) + const DialogTitleComponent = props.dialogTitleComponent ?? DialogTitle const [isPickerOpen, setIsPickerOpen] = useState(false) const [fieldValue, setFieldValue] = useState([]) @@ -161,6 +142,18 @@ export const ReferenceGrid: React.FC = (props) => { handleDialogClose() } + const navigateToReference = (content: GenericContent) => { + if (!content?.Path || content.Id === -1 || !props.paths) { + return + } + const referencedItemPaths = getAppPathAndContent(props.paths, content.Path) + if (!referencedItemPaths) { + return + } + const { appPath, contentePath } = referencedItemPaths + window.location.href = buildCustomPath(appPath, props.actionName, contentePath) + } + const getDefaultValue = useCallback(async () => { if (!props.settings.DefaultValue || !props.repository) { return @@ -215,6 +208,51 @@ export const ReferenceGrid: React.FC = (props) => { cellStyle: { padding: 0 }, } + const pathCol: ColDef = { + headerName: 'Path', + field: 'Path', + headerTooltip: 'Path', + flex: 5, + filter: true, + sortable: true, + comparator: (a: string, b: string) => a.toLowerCase().localeCompare(b.toLowerCase()), + resizable: true, + cellRenderer: (x: { data: GenericContent; value: string }) => { + const clickable = Boolean(props.paths && x.data?.Id !== -1) + return ( + clickable && navigateToReference(x.data)}> + {x.value} + + ) + }, + } + + const displayNameCol: ColDef = { + headerName: 'Display Name', + field: 'DisplayName', + headerTooltip: 'Display Name', + flex: 1.5, + filter: true, + sortable: true, + comparator: (a: string, b: string) => a.toLowerCase().localeCompare(b.toLowerCase()), + resizable: true, + } + + const typeCol: ColDef = { + headerName: 'Type', + field: 'Type', + headerTooltip: 'Type', + flex: 1, + filter: true, + sortable: true, + resizable: true, + } + + const dataCols: ColDef[] = [pathCol, displayNameCol, typeCol] + const removeCol: ColDef = { headerName: '', field: '', @@ -243,7 +281,11 @@ export const ReferenceGrid: React.FC = (props) => { case 'new': case 'edit': return ( - + = (props) => {
= (props) => { onClose={handleDialogClose} open={isPickerOpen} {...props.dialogProps}> + {localization.referencePickerTitle} = (props) => { {`${props.settings.DisplayName} (${props.settings.Name})`} -
- -
+ {fieldValue?.length ? ( +
+ +
+ ) : ( + + {localization.noValue} + + )} ) }