Compare commits

...

1 Commits

Author SHA1 Message Date
Jarrod Flesch
8256b3adfe chore: WIP, improved but still broken changes 2024-05-29 08:29:12 -04:00
7 changed files with 191 additions and 30 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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) => {

View File

@@ -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>

View File

@@ -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],

View File

@@ -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 && (

View File

@@ -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,