Skip to content

Commit 50675fc

Browse files
VargaJoehassanad94
andauthored
Merge/develop into sn auth extraimprovements (#1649)
* feature: schema selector field #2217 * feature: add extra class * refactor: revert package manager * remove package mamnager * fix: undefined on no message * refactor: modify comment * change pre-comit hook * refacotr builds * revert ci cahnges * remove wake up notifyier * remove story book ci * fature: add link to refences * fix: remove console log * feature: loading state to operation * 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 --------- Co-authored-by: Hassan Adam <hassana@sensenet.com>
1 parent 8efe9b2 commit 50675fc

10 files changed

Lines changed: 258 additions & 105 deletions

File tree

.github/workflows/deploy-preview.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,6 @@ jobs:
2727
run: yarn snapp build
2828
env:
2929
RELATIVE_CI_KEY: ${{ secrets.RELATIVE_CI_KEY }}
30-
31-
- name: wake up deploy notifier
32-
run: yarn wait-on https://sensenet-sn-deploy-notifier.glitch.me/ -l -t 300000 -i 10000
33-
3430
- name: Publish
3531
run: npx netlify-cli@v2.41.0 deploy --dir=./apps/sensenet/build --message ${{ github.event.pull_request.number }}
3632
env:

.husky/pre-commit

100644100755
Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,4 @@
11
#!/bin/sh
22
. "$(dirname "$0")/_/husky.sh"
33

4-
# Detect /dev/tty using readlink and fd/2
5-
tty=$(readlink /proc/$$/fd/2)
6-
7-
# Use the detected tty for redirection
8-
if [[ -n "$tty" ]]; then
9-
exec >"$tty" 2>&1
10-
else
11-
echo "Could not detect /dev/tty"
12-
fi
13-
144
yarn lint-staged

apps/sensenet/src/components/dialogs/dialogs.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const ChangePasswordDialog = lazy(() => import('./change-password'))
2424
const DateRangePicker = lazy(() => import('./date-range-picker'))
2525
const AddDeleteUserGroups = lazy(() => import('./add-delete-user-groups'))
2626
const ColumnSettings = lazy(() => import('./column-settings'))
27-
const Operations = lazy(() => import('./operations'))
27+
const Operations = lazy(() => import('./operations/operations'))
2828

2929
function dialogRenderer(dialog: DialogWithProps) {
3030
switch (dialog.name) {

apps/sensenet/src/components/dialogs/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,4 @@ export * from './restore'
1919
export * from './save-query'
2020
export * from './add-delete-user-groups'
2121
export * from './column-settings'
22-
export * from './operations'
22+
export * from './operations/operations'
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import React from 'react'
2+
3+
export type TInfluenceField = {
4+
name?: string
5+
description?: string
6+
radioOptions: Array<{ optionTitle: string; influencedFieldId: string; value: string }>
7+
}
8+
9+
export const InfluenceField: React.FC<TInfluenceField> = ({ name, description, radioOptions }) => {
10+
return (
11+
<div className="input-container influence-field">
12+
<h3>{name}</h3>
13+
<p>{description}</p>
14+
15+
{radioOptions.map((option) => {
16+
const { optionTitle, value, influencedFieldId } = option
17+
18+
return (
19+
<>
20+
<input
21+
type="radio"
22+
id={optionTitle}
23+
name={name}
24+
value={optionTitle}
25+
onClick={() => {
26+
const element = document.getElementById(influencedFieldId) as HTMLInputElement
27+
28+
if (!element) {
29+
return
30+
}
31+
32+
element.value = value
33+
}}
34+
/>
35+
<label htmlFor={optionTitle}>{optionTitle}</label>
36+
</>
37+
)
38+
})}
39+
</div>
40+
)
41+
}

apps/sensenet/src/components/dialogs/operations.tsx renamed to apps/sensenet/src/components/dialogs/operations/operations.tsx

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,43 @@
1-
import { Button, createStyles, DialogActions, DialogContent, makeStyles, TextField } from '@material-ui/core'
1+
import {
2+
Button,
3+
CircularProgress,
4+
createStyles,
5+
DialogActions,
6+
DialogContent,
7+
makeStyles,
8+
TextField,
9+
} from '@material-ui/core'
210
import { GenericContent } from '@sensenet/default-content-types'
311
import { useLogger, useRepository, useSession } from '@sensenet/hooks-react'
412
import React, { useEffect, useRef, useState } from 'react'
5-
import { useCurrentUser } from '../../context'
6-
import { useGlobalStyles } from '../../globalStyles'
7-
import { useLocalization } from '../../hooks'
8-
import { Icon } from '../Icon'
9-
import { DialogTitle, useDialog } from '.'
10-
13+
import { DialogTitle, useDialog } from '..'
14+
import { useCurrentUser } from '../../../context'
15+
import { useGlobalStyles } from '../../../globalStyles'
16+
import { useLocalization } from '../../../hooks'
17+
import { Icon } from '../../Icon'
18+
import { InfluenceField, TInfluenceField } from './influenceField'
1119
export interface OperationsDialogProps {
1220
content: GenericContent
1321
OperationName: string
1422
}
15-
/*Ezt itt jól ki kell dolgozni!!! nem végleges csak demora van egyszerűsítve
16-
Valószínüleg nem is itt lesz a végleges helye hanem ott ahol a GenericContent van
17-
*/
18-
type UIDescription = {
23+
/*Ezt itt jól ki kell dolgozni!!! nem végleges csak demora van egyszerűsítve*/
24+
25+
type baseDescriptionFields = {
1926
title?: string
2027
submitTitle?: string
21-
elements: Array<{
22-
name?: string
23-
description?: string
24-
inputProps: React.HTMLProps<HTMLInputElement>
25-
}>
2628
}
2729

30+
type simpleInputField = {
31+
name?: string
32+
description?: string
33+
inputProps: React.HTMLProps<HTMLInputElement>
34+
}
35+
36+
type UIDescription =
37+
| baseDescriptionFields & {
38+
elements: Array<simpleInputField | TInfluenceField>
39+
}
40+
2841
type OperationResult = {
2942
ToastMessage?: string
3043
}
@@ -61,6 +74,7 @@ export function OperationsDialog(props: OperationsDialogProps) {
6174
const globalClasses = useGlobalStyles()
6275

6376
const [UIDescription, setUIDescription] = useState<UIDescription>()
77+
const [isOperationSubmiting, setIsOperationSubmiting] = useState(false)
6478

6579
useEffect(() => {
6680
const loadOperation = async () => {
@@ -80,6 +94,7 @@ export function OperationsDialog(props: OperationsDialogProps) {
8094
}, [logger, props.OperationName, props.content.Path, repository])
8195

8296
const submitAction = async (e: React.FormEvent<HTMLFormElement>) => {
97+
setIsOperationSubmiting(true)
8398
e.preventDefault()
8499
if (!formRef.current) return
85100

@@ -98,13 +113,15 @@ export function OperationsDialog(props: OperationsDialogProps) {
98113
body: formJson,
99114
})
100115

101-
const success = `: ${result?.ToastMessage}` || ''
116+
const success = result.ToastMessage ? `: ${result?.ToastMessage}` : ''
102117

103118
logger.information({ message: `${localization.success}${success}` })
104119

105120
closeLastDialog()
106121
} catch (error) {
107122
logger.error({ message: error.message })
123+
} finally {
124+
setIsOperationSubmiting(false)
108125
}
109126
}
110127

@@ -132,6 +149,10 @@ export function OperationsDialog(props: OperationsDialogProps) {
132149
submitAction(e)
133150
}}>
134151
{UIDescription?.elements?.map((field, index) => {
152+
if ('radioOptions' in field) {
153+
return <InfluenceField {...field} key={index} />
154+
}
155+
135156
const { inputProps, description, name } = field
136157

137158
return (
@@ -160,6 +181,8 @@ export function OperationsDialog(props: OperationsDialogProps) {
160181
color="primary"
161182
variant="contained"
162183
type="submit"
184+
disabled={isOperationSubmiting}
185+
endIcon={isOperationSubmiting && <CircularProgress size={20} />}
163186
autoFocus={true}>
164187
{UIDescription?.submitTitle || localization.submit}
165188
</Button>

apps/sensenet/src/components/field-controls/reference-grid.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { ReactClientFieldSetting, ReferenceGrid as SnReferenceGrid } from '@sensenet/controls-react'
22
import { clsx } from 'clsx'
33
import React from 'react'
4+
import { PATHS } from '../../application-paths'
45
import { useGlobalStyles } from '../../globalStyles'
56
import { Icon } from '../Icon'
67

@@ -14,6 +15,7 @@ export const ReferenceGrid: React.FC<ReactClientFieldSetting> = (props) => {
1415
<Icon item={item} style={{ width: 'auto', height: 'auto', marginTop: 1, paddingTop: 1 }} />
1516
)}
1617
pickerClasses={{ cancelButton: globalClasses.cancelButton }}
18+
paths={PATHS}
1719
/>
1820
)
1921
}

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,5 +77,7 @@
7777
"eslint --ext .jsx,.js --cache --fix",
7878
"prettier --write"
7979
]
80-
}
80+
},
81+
"dependencies": {},
82+
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
8183
}

packages/sn-controls-react/src/fieldcontrols/reference-grid/default-item-template.tsx

Lines changed: 74 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1+
/* eslint-disable require-jsdoc */
12
import {
23
Avatar,
4+
createStyles,
35
Icon,
46
IconButton,
57
ListItem,
68
ListItemAvatar,
79
ListItemIcon,
810
ListItemSecondaryAction,
911
ListItemText,
10-
Table,
11-
TableBody,
12-
TableCell,
12+
makeStyles,
1313
} from '@material-ui/core'
1414
import { InsertDriveFile } from '@material-ui/icons'
1515
import { Repository } from '@sensenet/client-core'
@@ -18,6 +18,42 @@ import { GenericContent, Image, User } from '@sensenet/default-content-types'
1818
import React from 'react'
1919
import { renderIconDefault } from '../icon'
2020

21+
export type PathConfig = {
22+
appPath: string
23+
snPath?: string
24+
}
25+
26+
export type Paths = Record<string, PathConfig>
27+
28+
export function getAppPathAndContent(PATHS: Paths, targetPath: string) {
29+
const matches = Object.entries(PATHS)
30+
.filter(([, config]) => config.snPath && targetPath.startsWith(config.snPath))
31+
.sort((a, b) => b[1].snPath!.length - a[1].snPath!.length)
32+
33+
if (!matches[0]) return undefined
34+
35+
const [, config] = matches[0]
36+
const contentePath = targetPath.substring(config.snPath!.length)
37+
38+
return {
39+
appPath: config.appPath,
40+
contentePath,
41+
}
42+
}
43+
44+
export function buildCustomPath(path: string, action: string | undefined, contentePath: string) {
45+
const customPath = path
46+
.replace(':browseType', 'explorer')
47+
.replace('/:path', '') // Remove the path parameter
48+
.replace(':action?', action || 'default')
49+
50+
const url = new URL(window.location.origin)
51+
url.pathname = customPath
52+
url.searchParams.set('content', contentePath)
53+
54+
return url.toString()
55+
}
56+
2157
interface DefaultItemTemplateProps {
2258
content: GenericContent
2359
remove?: (id: number) => void
@@ -27,13 +63,29 @@ interface DefaultItemTemplateProps {
2763
repository?: Repository
2864
multiple: boolean
2965
renderIcon?: (name: string) => JSX.Element
66+
paths: Paths
3067
}
3168

69+
const useStyles = makeStyles(() =>
70+
createStyles({
71+
referenceItemText: {
72+
textAlign: 'left',
73+
paddingRight: 15,
74+
cursor: 'pointer',
75+
'&[data-clickable="true"]:hover': {
76+
textDecoration: 'underline',
77+
},
78+
},
79+
}),
80+
)
81+
3282
/**
3383
* Represents a default renderer for reference grid row
3484
*/
3585
export const DefaultItemTemplate: React.FC<DefaultItemTemplateProps> = (props) => {
36-
const { content, repository } = props
86+
const { content, repository, paths } = props
87+
88+
const classes = useStyles()
3789

3890
const renderIcon = (item: GenericContent | User | Image) => {
3991
if (repository?.schemas.isContentFromType<User>(item, 'User')) {
@@ -111,26 +163,24 @@ export const DefaultItemTemplate: React.FC<DefaultItemTemplateProps> = (props) =
111163
<ListItem style={props.actionName === 'browse' ? { padding: 0 } : undefined} key={content.Id} button={false}>
112164
{content.Type ? renderIcon(content) : null}
113165
<ListItemText
114-
primary={
115-
content.Path?.trim() === '' ? (
116-
content.DisplayName
117-
) : (
118-
<Table>
119-
<TableBody>
120-
<TableCell component="th" title={content.Path} scope="row">
121-
{content.Path}
122-
</TableCell>
123-
<TableCell component="th" scope="row">
124-
{content.DisplayName}
125-
</TableCell>
126-
<TableCell component="th" scope="row">
127-
{content.Type}
128-
</TableCell>
129-
</TableBody>
130-
</Table>
131-
)
132-
}
133-
style={{ textAlign: 'left', paddingRight: 15 }}
166+
onClick={() => {
167+
if (content.Id === -1 || !paths) {
168+
return
169+
}
170+
const referencedItemPaths = getAppPathAndContent(paths, content.Path)
171+
172+
if (!referencedItemPaths) {
173+
return
174+
}
175+
176+
const { appPath, contentePath } = referencedItemPaths
177+
178+
const fullUrl = buildCustomPath(appPath, props.actionName, contentePath)
179+
window.location.href = fullUrl
180+
}}
181+
primary={content.DisplayName}
182+
className={classes.referenceItemText}
183+
data-clickable={content.Id !== -1}
134184
/>
135185
{props.actionName && props.actionName !== 'browse' && !props.readOnly ? (
136186
<ListItemSecondaryAction>

0 commit comments

Comments
 (0)