Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Advanced search #1180

Merged
merged 8 commits into from
Feb 17, 2021
Merged
Show file tree
Hide file tree
Changes from 7 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
1 change: 1 addition & 0 deletions apps/sensenet/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
"monaco-editor": "0.21.3",
"react": "^16.13.0",
"react-autosuggest": "^10.1.0",
"react-day-picker": "^7.4.8",
"react-dom": "^16.13.0",
"react-markdown": "^5.0.3",
"react-monaco-editor": "0.41.2",
Expand Down
13 changes: 8 additions & 5 deletions apps/sensenet/src/components/dialogs/content-picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ import { Icon } from '../Icon'
import { DialogTitle, useDialog } from '.'

export interface ContentPickerDialogProps {
defaultValue?: GenericContentWithIsParent
currentPath: string
selectionRoot?: string
content: GenericContent
handleSubmit?: (path: string) => void
required?: boolean
content?: GenericContent
handleSubmit?: (content: GenericContentWithIsParent) => void
}

export const ContentPickerDialog: React.FunctionComponent<ContentPickerDialogProps> = (props) => {
Expand All @@ -32,21 +34,22 @@ export const ContentPickerDialog: React.FunctionComponent<ContentPickerDialogPro
const itemsODataOptions = useMemo(() => ({ filter: '' }), [])

const handleSubmit = async (selection: GenericContentWithIsParent[]) => {
props.handleSubmit?.(selection[0].Path)
props.handleSubmit?.(selection[0])
closeLastDialog()
}

return (
<>
<DialogTitle>
<div className={globalClasses.centeredVertical}>
<Icon item={props.content} style={{ marginRight: '1em' }} />
{props.content && <Icon item={props.content} style={{ marginRight: '1em' }} />}
{localization.title}
</div>
</DialogTitle>

<Picker
repository={repository}
defaultValue={props.defaultValue ? [props.defaultValue] : undefined}
currentPath={props.currentPath}
selectionRoots={selectionRoots}
itemsODataOptions={itemsODataOptions}
Expand All @@ -56,7 +59,7 @@ export const ContentPickerDialog: React.FunctionComponent<ContentPickerDialogPro
actionsContainer={DialogActions}
handleCancel={closeLastDialog}
handleSubmit={handleSubmit}
required={1}
required={props.required ? 1 : 0}
localization={{ cancelButton: localization.cancelButton, submitButton: localization.selectButton }}
classes={{ cancelButton: globalClasses.cancelButton }}
/>
Expand Down
130 changes: 130 additions & 0 deletions apps/sensenet/src/components/dialogs/date-range-picker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import { Button, createStyles, DialogActions, DialogContent, makeStyles, Theme } from '@material-ui/core'
import React, { useState } from 'react'
import DayPicker, { DateUtils, Modifier } from 'react-day-picker'
import MomentLocaleUtils from 'react-day-picker/moment'
import { useGlobalStyles } from '../../globalStyles'
import { useLocalization, usePersonalSettings } from '../../hooks'
import { useDialog } from '.'

import 'react-day-picker/lib/style.css'

const useStyles = makeStyles((theme: Theme) => {
return createStyles({
root: {
'& .DayPicker-wrapper, & .DayPicker-NavButton': {
outline: 0,
},
'& .DayPicker-Day': {
borderRadius: '0 !important',
},
'& .DayPicker-Day--selected:not(.DayPicker-Day--disabled):not(.DayPicker-Day--outside)': {
backgroundColor: theme.palette.primary.main,
},
},
})
})

export interface DateRangePickerProps {
defaultValue?: {
from: Date
to: Date
}
handleSubmit: (range: { from?: Date; to?: Date }) => void
}

export const DateRangePicker: React.FunctionComponent<DateRangePickerProps> = (props) => {
const { closeLastDialog } = useDialog()
const globalClasses = useGlobalStyles()
const classes = useStyles()
const localization = useLocalization().dateRangePicker
const personalSettings = usePersonalSettings()
const langCode = personalSettings.language === 'hungarian' ? 'hu' : 'en'

const [from, setFrom] = useState<Date | undefined>(props.defaultValue?.from)
const [to, setTo] = useState<Date | undefined>(props.defaultValue?.to)
const [enteredTo, setEnteredTo] = useState<Date | undefined>(props.defaultValue?.to)

const isSelectingFirstDay = (day: Date) => {
const isBeforeFirstDay = from && DateUtils.isDayBefore(day, from)
const isRangeSelected = from && to
return !from || isBeforeFirstDay || isRangeSelected
}

const handleDayClick = (day: Date) => {
if (from && to && day >= from && day <= to) {
handleResetClick()
return
}
if (isSelectingFirstDay(day)) {
setFrom(day)
setTo(undefined)
setEnteredTo(undefined)
} else {
setTo(day)
setEnteredTo(day)
}
}

const handleDayMouseEnter = (day: Date) => {
if (!isSelectingFirstDay(day)) {
setEnteredTo(day)
}
}

const handleResetClick = () => {
setFrom(undefined)
setTo(undefined)
setEnteredTo(undefined)
}

const modifiers = { start: from, end: enteredTo }
const disabledDays: Modifier = from ? { before: from } : undefined
const selectedDays: Modifier[] = from && enteredTo ? [from, { from, to: enteredTo }] : from ? [from] : []

return (
<>
<DialogContent>
<DayPicker
className={classes.root}
firstDayOfWeek={1}
fixedWeeks
numberOfMonths={2}
fromMonth={from}
toMonth={new Date()}
selectedDays={selectedDays}
disabledDays={disabledDays}
modifiers={modifiers}
onDayClick={handleDayClick}
onDayMouseEnter={handleDayMouseEnter}
localeUtils={MomentLocaleUtils}
locale={langCode}
/>
</DialogContent>
<DialogActions>
<Button
aria-label={localization.resetButton}
disabled={!from || !to}
color="secondary"
variant="contained"
onClick={handleResetClick}>
{localization.resetButton}
</Button>
<Button
aria-label={localization.submitButton}
color="primary"
variant="contained"
onClick={() => props.handleSubmit({ from, to })}>
{localization.submitButton}
</Button>
<Button
aria-label={localization.cancelButton}
className={globalClasses.cancelButton}
onClick={() => closeLastDialog()}>
{localization.cancelButton}
</Button>
</DialogActions>
</>
)
}

export default DateRangePicker
2 changes: 2 additions & 0 deletions apps/sensenet/src/components/dialogs/dialog-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
ContentPickerDialogProps,
CopyMoveDialogProps,
CustomActionResultDialogProps,
DateRangePickerProps,
DeleteContentDialogProps,
ExecuteActionDialogProps,
PermissionEditorDialogProps,
Expand Down Expand Up @@ -37,6 +38,7 @@ export type DialogWithProps = (
| { name: 'content-picker'; props: ContentPickerDialogProps }
| { name: 'feedback' }
| { name: 'change-password' }
| { name: 'date-range-picker'; props: DateRangePickerProps }
) & { dialogProps?: Partial<DialogProps> }

type Action = { type: 'PUSH_DIALOG'; dialog: DialogWithProps } | { type: 'POP_DIALOG' } | { type: 'CLOSE_ALL_DIALOGS' }
Expand Down
3 changes: 3 additions & 0 deletions apps/sensenet/src/components/dialogs/dialogs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const Restore = React.lazy(() => import('./restore'))
const ContentPicker = React.lazy(() => import('./content-picker'))
const Feedback = React.lazy(() => import('./feedback'))
const ChangePasswordDialog = React.lazy(() => import('./change-password'))
const DateRangePicker = React.lazy(() => import('./date-range-picker'))

function dialogRenderer(dialog: DialogWithProps) {
switch (dialog.name) {
Expand Down Expand Up @@ -60,6 +61,8 @@ function dialogRenderer(dialog: DialogWithProps) {
return <Feedback />
case 'change-password':
return <ChangePasswordDialog />
case 'date-range-picker':
return <DateRangePicker {...dialog.props} />
default:
return null
}
Expand Down
1 change: 1 addition & 0 deletions apps/sensenet/src/components/dialogs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export * from './check-in'
export * from './content-picker'
export * from './copy-move'
export * from './custom-action-result'
export * from './date-range-picker'
export * from './delete'
export * from './dialog-provider'
export * from './dialog-title'
Expand Down
5 changes: 3 additions & 2 deletions apps/sensenet/src/components/dialogs/restore.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PathHelper } from '@sensenet/client-utils'
import { TrashBag } from '@sensenet/default-content-types'
import { GenericContent, TrashBag } from '@sensenet/default-content-types'
import { useLogger, useRepository } from '@sensenet/hooks-react'
import { Button, DialogActions, DialogContent, InputAdornment, TextField } from '@material-ui/core'
import RestoreIcon from '@material-ui/icons/RestoreFromTrash'
Expand Down Expand Up @@ -95,7 +95,8 @@ export function Restore(props: RestoreProps) {
content: props.content,
currentPath: props.content.OriginalPath || '/Root',
selectionRoot: rootPath,
handleSubmit: (path: string) => setDestination(path),
handleSubmit: (content: GenericContent) => setDestination(content.Path),
required: true,
},
dialogProps: { disableBackdropClick: true, open: true, classes: { paper: globalClasses.pickerDialog } },
})
Expand Down
9 changes: 6 additions & 3 deletions apps/sensenet/src/components/dialogs/save-query.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import { Button, DialogActions, DialogContent, TextField } from '@material-ui/co
import React, { useState } from 'react'
import { useGlobalStyles } from '../../globalStyles'
import { useLocalization } from '../../hooks'
import { createSearchQuery } from '../../services/search-query-builder'
import { SearchFilters } from '../search'
import { DialogTitle, useDialog } from '.'

export type SaveQueryProps = {
saveName?: string
query: string
filters: SearchFilters
}

export function SaveQuery(props: SaveQueryProps) {
Expand All @@ -30,9 +32,10 @@ export function SaveQuery(props: SaveQueryProps) {
select: ['DisplayName', 'Query'],
},
body: {
query: props.query,
query: createSearchQuery(props.filters).toString(),
displayName: saveName,
queryType: 'Public',
uiFilters: JSON.stringify(props.filters),
},
})
logger.information({
Expand All @@ -52,7 +55,7 @@ export function SaveQuery(props: SaveQueryProps) {
<DialogContent style={{ minWidth: 450 }}>
<TextField
fullWidth={true}
defaultValue={localization.saveInputPlaceholder(props.query)}
defaultValue={localization.saveInputPlaceholder(props.filters.term)}
onChange={(ev) => setSaveName(ev.currentTarget.value)}
/>
</DialogContent>
Expand Down
Loading