Skip to content
Merged
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
4 changes: 0 additions & 4 deletions .github/workflows/deploy-preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
10 changes: 0 additions & 10 deletions .husky/pre-commit
100644 → 100755
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion apps/sensenet/src/components/dialogs/dialogs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion apps/sensenet/src/components/dialogs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
41 changes: 41 additions & 0 deletions apps/sensenet/src/components/dialogs/operations/influenceField.tsx
Original file line number Diff line number Diff line change
@@ -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<TInfluenceField> = ({ name, description, radioOptions }) => {
return (
<div className="input-container influence-field">
<h3>{name}</h3>
<p>{description}</p>

{radioOptions.map((option) => {
const { optionTitle, value, influencedFieldId } = option

return (
<>
<input
type="radio"
id={optionTitle}
name={name}
value={optionTitle}
onClick={() => {
const element = document.getElementById(influencedFieldId) as HTMLInputElement

if (!element) {
return
}

element.value = value
}}
/>
<label htmlFor={optionTitle}>{optionTitle}</label>
</>
)
})}
</div>
)
}
Original file line number Diff line number Diff line change
@@ -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<HTMLInputElement>
}>
}

type simpleInputField = {
name?: string
description?: string
inputProps: React.HTMLProps<HTMLInputElement>
}

type UIDescription =
| baseDescriptionFields & {
elements: Array<simpleInputField | TInfluenceField>
}

type OperationResult = {
ToastMessage?: string
}
Expand Down Expand Up @@ -61,6 +74,7 @@ export function OperationsDialog(props: OperationsDialogProps) {
const globalClasses = useGlobalStyles()

const [UIDescription, setUIDescription] = useState<UIDescription>()
const [isOperationSubmiting, setIsOperationSubmiting] = useState(false)

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

const submitAction = async (e: React.FormEvent<HTMLFormElement>) => {
setIsOperationSubmiting(true)
e.preventDefault()
if (!formRef.current) return

Expand All @@ -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)
}
}

Expand Down Expand Up @@ -132,6 +149,10 @@ export function OperationsDialog(props: OperationsDialogProps) {
submitAction(e)
}}>
{UIDescription?.elements?.map((field, index) => {
if ('radioOptions' in field) {
return <InfluenceField {...field} key={index} />
}

const { inputProps, description, name } = field

return (
Expand Down Expand Up @@ -160,6 +181,8 @@ export function OperationsDialog(props: OperationsDialogProps) {
color="primary"
variant="contained"
type="submit"
disabled={isOperationSubmiting}
endIcon={isOperationSubmiting && <CircularProgress size={20} />}
autoFocus={true}>
{UIDescription?.submitTitle || localization.submit}
</Button>
Expand Down
Original file line number Diff line number Diff line change
@@ -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'

Expand All @@ -14,6 +15,7 @@ export const ReferenceGrid: React.FC<ReactClientFieldSetting> = (props) => {
<Icon item={item} style={{ width: 'auto', height: 'auto', marginTop: 1, paddingTop: 1 }} />
)}
pickerClasses={{ cancelButton: globalClasses.cancelButton }}
paths={PATHS}
/>
)
}
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,7 @@
"eslint --ext .jsx,.js --cache --fix",
"prettier --write"
]
}
},
"dependencies": {},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
/* eslint-disable require-jsdoc */
import {
Avatar,
createStyles,
Icon,
IconButton,
ListItem,
ListItemAvatar,
ListItemIcon,
ListItemSecondaryAction,
ListItemText,
Table,
TableBody,
TableCell,
makeStyles,
} from '@material-ui/core'
import { InsertDriveFile } from '@material-ui/icons'
import { Repository } from '@sensenet/client-core'
Expand All @@ -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<string, PathConfig>

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
Expand All @@ -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<DefaultItemTemplateProps> = (props) => {
const { content, repository } = props
const { content, repository, paths } = props

const classes = useStyles()

const renderIcon = (item: GenericContent | User | Image) => {
if (repository?.schemas.isContentFromType<User>(item, 'User')) {
Expand Down Expand Up @@ -111,26 +163,24 @@ export const DefaultItemTemplate: React.FC<DefaultItemTemplateProps> = (props) =
<ListItem style={props.actionName === 'browse' ? { padding: 0 } : undefined} key={content.Id} button={false}>
{content.Type ? renderIcon(content) : null}
<ListItemText
primary={
content.Path?.trim() === '' ? (
content.DisplayName
) : (
<Table>
<TableBody>
<TableCell component="th" title={content.Path} scope="row">
{content.Path}
</TableCell>
<TableCell component="th" scope="row">
{content.DisplayName}
</TableCell>
<TableCell component="th" scope="row">
{content.Type}
</TableCell>
</TableBody>
</Table>
)
}
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 ? (
<ListItemSecondaryAction>
Expand Down
Loading
Loading