Compare commits
1 Commits
v3.0.0-bet
...
fix/beta/w
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8256b3adfe |
@@ -26,6 +26,7 @@ export type CellComponentProps = {
|
||||
className?: string
|
||||
dateDisplayFormat?: DateField['admin']['date']['displayFormat']
|
||||
fieldType?: Field['type']
|
||||
hasMany?: RelationshipField['hasMany']
|
||||
isFieldAffectingData?: boolean
|
||||
label?: Record<string, string> | string
|
||||
labels?: Labels
|
||||
|
||||
@@ -142,6 +142,10 @@ export const buildColumnState = (args: Args): Column[] => {
|
||||
cellProps: {
|
||||
...field.cellComponentProps,
|
||||
...cellProps?.[index],
|
||||
hasMany:
|
||||
field.type === 'relationship' && 'hasMany' in field.fieldComponentProps
|
||||
? field.fieldComponentProps.hasMany
|
||||
: undefined,
|
||||
link: isFirstActiveColumn,
|
||||
relationTo:
|
||||
field.type === 'relationship' && 'relationTo' in field.fieldComponentProps
|
||||
|
||||
@@ -121,7 +121,7 @@ export const RelationshipField: React.FC<Props> = (props) => {
|
||||
options.forEach((opt) => {
|
||||
if (opt.options) {
|
||||
opt.options.some((subOpt) => {
|
||||
if (subOpt?.value === val.value) {
|
||||
if (subOpt?.relationTo === val.relationTo && subOpt?.value === val.value) {
|
||||
matchedOption = subOpt
|
||||
return true
|
||||
}
|
||||
@@ -149,7 +149,10 @@ export const RelationshipField: React.FC<Props> = (props) => {
|
||||
options.forEach((opt) => {
|
||||
if (opt?.options) {
|
||||
opt.options.some((subOpt) => {
|
||||
if (subOpt?.value === valueWithRelation.value) {
|
||||
if (
|
||||
subOpt?.relationTo === valueWithRelation.relationTo &&
|
||||
subOpt?.value === valueWithRelation.value
|
||||
) {
|
||||
matchedOption = subOpt
|
||||
return true
|
||||
}
|
||||
@@ -165,7 +168,7 @@ export const RelationshipField: React.FC<Props> = (props) => {
|
||||
}
|
||||
|
||||
return undefined
|
||||
}, [hasMany, hasMultipleRelations, value, options])
|
||||
}, [hasMany, hasMultipleRelations, options, value])
|
||||
|
||||
const handleInputChange = useCallback(
|
||||
(newSearch) => {
|
||||
@@ -178,14 +181,13 @@ export const RelationshipField: React.FC<Props> = (props) => {
|
||||
|
||||
const addOptionByID = useCallback(
|
||||
async (id, relation) => {
|
||||
if (!errorLoading && id !== 'null' && id && relation) {
|
||||
if (!errorLoading && id !== 'null' && ['number', 'string'].includes(typeof id) && relation) {
|
||||
const response = await fetch(`${serverURL}${api}/${relation}/${id}?depth=0`, {
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Accept-Language': i18n.language,
|
||||
},
|
||||
})
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json()
|
||||
addOptions({ docs: [data] }, relation)
|
||||
@@ -223,7 +225,6 @@ export const RelationshipField: React.FC<Props> = (props) => {
|
||||
if (value && hasLoadedFirstOptions) {
|
||||
if (hasMany) {
|
||||
const matchedOptions = findOptionsByValue()
|
||||
|
||||
;((matchedOptions as Option[]) || []).forEach((option, i) => {
|
||||
if (!option) {
|
||||
if (hasMultipleRelations) {
|
||||
@@ -267,6 +268,12 @@ export const RelationshipField: React.FC<Props> = (props) => {
|
||||
{!errorLoading && (
|
||||
<ReactSelect
|
||||
disabled={disabled}
|
||||
getOptionValue={(option) => {
|
||||
if (!option) return undefined
|
||||
return hasMany && Array.isArray(relationTo)
|
||||
? `${option.relationTo}_${option.value}`
|
||||
: option.value
|
||||
}}
|
||||
isMulti={hasMany}
|
||||
isSortable={isSortable}
|
||||
onChange={(selected) => {
|
||||
|
||||
@@ -27,13 +27,15 @@ export type Props = {
|
||||
fieldName,
|
||||
operator,
|
||||
orIndex,
|
||||
rawQuery,
|
||||
value,
|
||||
}: {
|
||||
andIndex: number
|
||||
fieldName: string
|
||||
fieldName?: string
|
||||
operator: string
|
||||
orIndex: number
|
||||
value: string
|
||||
rawQuery?: Record<string, any>
|
||||
value?: string
|
||||
}) => void
|
||||
}
|
||||
|
||||
@@ -59,6 +61,49 @@ const valueFields: Record<ComponentType, React.FC> = {
|
||||
|
||||
const baseClass = 'condition'
|
||||
|
||||
type BuildRawPolymorphicRelationshipWhereQueryArgs = {
|
||||
fieldName: string
|
||||
operator: 'in' | 'not_in'
|
||||
polyRelationshipValues: {
|
||||
relationTo: string
|
||||
value: number | string
|
||||
}[]
|
||||
}
|
||||
|
||||
function buildRawPolymorphicRelationshipWhereQuery({
|
||||
fieldName,
|
||||
operator,
|
||||
polyRelationshipValues,
|
||||
}: BuildRawPolymorphicRelationshipWhereQueryArgs) {
|
||||
const keys = {
|
||||
constraint: operator === 'in' ? 'and' : 'or',
|
||||
equality: operator === 'in' ? 'equals' : 'not_equals',
|
||||
// in: any constraints must be true
|
||||
// not_in: all constraints must be true
|
||||
inclusion: operator === 'in' ? 'or' : 'and',
|
||||
}
|
||||
|
||||
const relationQueries = []
|
||||
polyRelationshipValues.map((relation, i) => {
|
||||
relationQueries[i] = {
|
||||
[keys.constraint]: [
|
||||
{
|
||||
[`${fieldName}.relationTo`]: {
|
||||
[keys.equality]: relation.relationTo,
|
||||
},
|
||||
},
|
||||
{
|
||||
[`${fieldName}.value`]: {
|
||||
[keys.equality]: relation.value,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
})
|
||||
|
||||
return { [keys.inclusion]: relationQueries }
|
||||
}
|
||||
|
||||
export const Condition: React.FC<Props> = (props) => {
|
||||
const {
|
||||
addCondition,
|
||||
@@ -76,28 +121,46 @@ export const Condition: React.FC<Props> = (props) => {
|
||||
)
|
||||
const [internalOperatorOption, setInternalOperatorOption] = useState(operator)
|
||||
const [internalQueryValue, setInternalQueryValue] = useState<string>(initialValue)
|
||||
|
||||
const debouncedValue = useDebounce(internalQueryValue, 300)
|
||||
|
||||
useEffect(() => {
|
||||
// This is to trigger changes when the debounced value changes
|
||||
if (
|
||||
internalField.value &&
|
||||
internalField?.value &&
|
||||
internalOperatorOption &&
|
||||
![null, undefined].includes(debouncedValue)
|
||||
) {
|
||||
updateCondition({
|
||||
andIndex,
|
||||
fieldName: internalField.value,
|
||||
operator: internalOperatorOption,
|
||||
orIndex,
|
||||
value: debouncedValue,
|
||||
})
|
||||
if (
|
||||
'relationTo' in internalField.props &&
|
||||
Array.isArray(internalField.props.relationTo) &&
|
||||
Array.isArray(debouncedValue)
|
||||
) {
|
||||
updateCondition({
|
||||
andIndex,
|
||||
operator: internalOperatorOption,
|
||||
orIndex,
|
||||
rawQuery: buildRawPolymorphicRelationshipWhereQuery({
|
||||
fieldName: internalField.value,
|
||||
operator:
|
||||
internalOperatorOption as BuildRawPolymorphicRelationshipWhereQueryArgs['operator'],
|
||||
polyRelationshipValues: debouncedValue,
|
||||
}),
|
||||
})
|
||||
} else {
|
||||
updateCondition({
|
||||
andIndex,
|
||||
fieldName: internalField.value,
|
||||
operator: internalOperatorOption,
|
||||
orIndex,
|
||||
value: debouncedValue,
|
||||
})
|
||||
}
|
||||
}
|
||||
}, [
|
||||
debouncedValue,
|
||||
andIndex,
|
||||
internalField?.value,
|
||||
internalField?.props,
|
||||
internalOperatorOption,
|
||||
orIndex,
|
||||
updateCondition,
|
||||
@@ -116,7 +179,7 @@ export const Condition: React.FC<Props> = (props) => {
|
||||
} else if (internalField?.props && 'options' in internalField.props) {
|
||||
valueOptions = internalField.props.options
|
||||
}
|
||||
|
||||
console.log('internalQueryValue', internalQueryValue)
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
<div className={`${baseClass}__wrap`}>
|
||||
@@ -130,12 +193,12 @@ export const Condition: React.FC<Props> = (props) => {
|
||||
setInternalQueryValue(undefined)
|
||||
}}
|
||||
options={fields}
|
||||
value={fields.find((field) => internalField.value === field.value) || fields[0]}
|
||||
value={fields.find((field) => internalField?.value === field.value) || fields[0]}
|
||||
/>
|
||||
</div>
|
||||
<div className={`${baseClass}__operator`}>
|
||||
<ReactSelect
|
||||
disabled={!internalField.value}
|
||||
disabled={!internalField?.value}
|
||||
isClearable={false}
|
||||
onChange={(operator) => {
|
||||
setInternalOperatorOption(operator.value)
|
||||
@@ -165,7 +228,8 @@ export const Condition: React.FC<Props> = (props) => {
|
||||
'relationTo' in internalField.props.cellProps
|
||||
? internalField.props.cellProps?.relationTo
|
||||
: undefined,
|
||||
value: internalQueryValue ?? '',
|
||||
// value: internalQueryValue ?? '',
|
||||
// value: [],
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -9,8 +9,7 @@ const boolean = [
|
||||
},
|
||||
]
|
||||
|
||||
const base = [
|
||||
...boolean,
|
||||
const array = [
|
||||
{
|
||||
label: 'isIn',
|
||||
value: 'in',
|
||||
@@ -19,6 +18,11 @@ const base = [
|
||||
label: 'isNotIn',
|
||||
value: 'not_in',
|
||||
},
|
||||
]
|
||||
|
||||
const base = [
|
||||
...boolean,
|
||||
...array,
|
||||
{
|
||||
label: 'exists',
|
||||
value: 'exists',
|
||||
@@ -114,6 +118,10 @@ const fieldTypeConditions = {
|
||||
component: 'Relationship',
|
||||
operators: [...base],
|
||||
},
|
||||
relationship_polymorphic: {
|
||||
component: 'Relationship',
|
||||
operators: [...array],
|
||||
},
|
||||
richText: {
|
||||
component: 'Text',
|
||||
operators: [...base, like, contains],
|
||||
|
||||
@@ -75,9 +75,10 @@ export const WhereBuilder: React.FC<WhereBuilderProps> = (props) => {
|
||||
|
||||
// Transform the where query to be in the right format. This will transform something simple like [text][equals]=example%20post to the right format
|
||||
const transformedWhere = transformWhereQuery(whereFromSearch)
|
||||
|
||||
if (validateWhereQuery(transformedWhere)) {
|
||||
return transformedWhere.or
|
||||
} else if ('or' in transformedWhere && transformedWhere.or) {
|
||||
return transformedWhere.or
|
||||
}
|
||||
|
||||
console.warn(`Invalid where query in URL: ${JSON.stringify(whereFromSearch)}`)
|
||||
@@ -106,19 +107,28 @@ export const WhereBuilder: React.FC<WhereBuilderProps> = (props) => {
|
||||
}, [])
|
||||
|
||||
const updateCondition = React.useCallback(
|
||||
({ andIndex, fieldName: fieldNameArg, operator: operatorArg, orIndex, value: valueArg }) => {
|
||||
({
|
||||
andIndex,
|
||||
fieldName: fieldNameArg,
|
||||
operator: operatorArg,
|
||||
orIndex,
|
||||
rawQuery,
|
||||
value: valueArg,
|
||||
}) => {
|
||||
setConditions((prevConditions) => {
|
||||
const newConditions = [...prevConditions]
|
||||
if (typeof newConditions[orIndex].and[andIndex] === 'object') {
|
||||
const fieldName = fieldNameArg
|
||||
const operator = operatorArg
|
||||
const value = valueArg ?? (operator ? newConditions[orIndex].and[andIndex][operator] : '')
|
||||
|
||||
if (fieldName && operator && ![null, undefined].includes(value)) {
|
||||
newConditions[orIndex].and[andIndex] = {
|
||||
[fieldName]: operator ? { [operator]: value } : {},
|
||||
}
|
||||
setShouldUpdateQuery(true)
|
||||
} else if (rawQuery) {
|
||||
newConditions[orIndex].and[andIndex] = rawQuery
|
||||
setShouldUpdateQuery(true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,6 +154,7 @@ export const WhereBuilder: React.FC<WhereBuilderProps> = (props) => {
|
||||
React.useEffect(() => {
|
||||
if (shouldUpdateQuery) {
|
||||
handleWhereChange({ or: conditions })
|
||||
console.log('here')
|
||||
setShouldUpdateQuery(false)
|
||||
}
|
||||
}, [conditions, handleWhereChange, shouldUpdateQuery])
|
||||
@@ -164,7 +175,7 @@ export const WhereBuilder: React.FC<WhereBuilderProps> = (props) => {
|
||||
{orIndex !== 0 && <div className={`${baseClass}__label`}>{t('general:or')}</div>}
|
||||
<ul className={`${baseClass}__and-filters`}>
|
||||
{Array.isArray(or?.and) &&
|
||||
or.and.map((_, andIndex) => {
|
||||
or.and.map((andCondition, andIndex) => {
|
||||
const initialFieldName = Object.keys(conditions[orIndex].and[andIndex])[0]
|
||||
const initialOperator =
|
||||
Object.keys(
|
||||
@@ -175,6 +186,68 @@ export const WhereBuilder: React.FC<WhereBuilderProps> = (props) => {
|
||||
initialOperator
|
||||
] || ''
|
||||
|
||||
// if (initialFieldName === 'and' || initialFieldName == 'or') {
|
||||
// initialValue = []
|
||||
// // polymorphic relationships
|
||||
// andCondition[initialFieldName].forEach((polyAndCondition, i) => {
|
||||
// if ('and' in polyAndCondition) {
|
||||
// initialOperator = 'in'
|
||||
// let relationTo = ''
|
||||
// let value = ''
|
||||
// polyAndCondition.and.forEach((whereGroup) => {
|
||||
// for (const [key, whereQuery] of Object.entries(whereGroup)) {
|
||||
// const keyParts = key.split('.')
|
||||
// initialFieldName = keyParts[0] // relationship field name
|
||||
// const relationKey = keyParts[1] // `relationTo` or `value`
|
||||
// relationTo =
|
||||
// relationKey === 'relationTo' &&
|
||||
// typeof whereQuery === 'object' &&
|
||||
// 'equals' in whereQuery
|
||||
// ? String(whereQuery.equals)
|
||||
// : relationTo
|
||||
// value =
|
||||
// relationKey === 'value' &&
|
||||
// typeof whereQuery === 'object' &&
|
||||
// 'equals' in whereQuery
|
||||
// ? String(whereQuery.equals)
|
||||
// : value
|
||||
// }
|
||||
// })
|
||||
// initialValue.push({
|
||||
// relationTo,
|
||||
// value,
|
||||
// })
|
||||
// } else {
|
||||
// initialOperator = 'not_in'
|
||||
// let relationTo = ''
|
||||
// let value = ''
|
||||
// polyAndCondition.or.forEach((whereGroup) => {
|
||||
// for (const [key, whereQuery] of Object.entries(whereGroup)) {
|
||||
// const keyParts = key.split('.')
|
||||
// initialFieldName = keyParts[0] // relationship field name
|
||||
// const relationKey = keyParts[1] // `relationTo` or `value`
|
||||
// relationTo =
|
||||
// relationKey === 'relationTo' &&
|
||||
// typeof whereQuery === 'object' &&
|
||||
// 'equals' in whereQuery
|
||||
// ? String(whereQuery.equals)
|
||||
// : relationTo
|
||||
// value =
|
||||
// relationKey === 'value' &&
|
||||
// typeof whereQuery === 'object' &&
|
||||
// 'equals' in whereQuery
|
||||
// ? String(whereQuery.equals)
|
||||
// : value
|
||||
// }
|
||||
// })
|
||||
// initialValue.push({
|
||||
// relationTo,
|
||||
// value,
|
||||
// })
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
return (
|
||||
<li key={andIndex}>
|
||||
{andIndex !== 0 && (
|
||||
|
||||
@@ -5,10 +5,14 @@ import fieldTypes from './field-types.js'
|
||||
|
||||
export const reduceFieldMap = (fieldMap: Column[], i18n) =>
|
||||
fieldMap.reduce((reduced, field) => {
|
||||
if (typeof fieldTypes[field.type] === 'object') {
|
||||
const fieldType: keyof typeof fieldTypes = Array.isArray(field.cellProps?.relationTo)
|
||||
? `relationship_polymorphic`
|
||||
: (field.type as keyof typeof fieldTypes)
|
||||
|
||||
if (typeof fieldTypes[fieldType] === 'object') {
|
||||
const operatorKeys = new Set()
|
||||
|
||||
const operators = fieldTypes[field.type].operators.reduce((acc, operator) => {
|
||||
const operators = fieldTypes[fieldType].operators.reduce((acc, operator) => {
|
||||
if (!operatorKeys.has(operator.value)) {
|
||||
operatorKeys.add(operator.value)
|
||||
return [
|
||||
@@ -25,7 +29,7 @@ export const reduceFieldMap = (fieldMap: Column[], i18n) =>
|
||||
const formattedField = {
|
||||
label: field.Label,
|
||||
value: field.name,
|
||||
...fieldTypes[field.type],
|
||||
...fieldTypes[fieldType],
|
||||
operators,
|
||||
props: {
|
||||
...field,
|
||||
|
||||
Reference in New Issue
Block a user