-
Notifications
You must be signed in to change notification settings - Fork 19
Dynamic filter #231
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
Dynamic filter #231
Changes from all commits
82245c8
186ae54
3f67dee
9d2fdda
411e662
7d1e8d1
cc6ae1b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -839,7 +839,7 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI { | |
method: 'POST', | ||
path: '/get_resource_foreign_data', | ||
handler: async ({ body, adminUser, headers, query, cookies, requestUrl }) => { | ||
const { resourceId, column } = body; | ||
const { resourceId, column, search } = body; | ||
if (!this.adminforth.statuses.dbDiscover) { | ||
return { error: 'Database discovery not started' }; | ||
} | ||
|
@@ -910,6 +910,46 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI { | |
throw new Error(`Wrong filter object value: ${JSON.stringify(filters)}`); | ||
} | ||
} | ||
|
||
if (search && search.trim() && columnConfig.foreignResource.searchableFields) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think poly case is not implemented here at all - it should do parallel queries There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. or probably you can do it on frontend (later) - should be possible also, maybe even better |
||
const searchableFields = Array.isArray(columnConfig.foreignResource.searchableFields) | ||
? columnConfig.foreignResource.searchableFields | ||
: [columnConfig.foreignResource.searchableFields]; | ||
|
||
const searchOperator = columnConfig.foreignResource.searchIsCaseSensitive | ||
? AdminForthFilterOperators.LIKE | ||
: AdminForthFilterOperators.ILIKE; | ||
const availableSearchFields = searchableFields.filter((fieldName) => { | ||
const fieldExists = targetResource.columns.some(col => col.name === fieldName); | ||
if (!fieldExists) { | ||
process.env.HEAVY_DEBUG && console.log(`⚠️ Field '${fieldName}' not found in polymorphic target resource '${targetResource.resourceId}', skipping in search filter.`); | ||
} | ||
return fieldExists; | ||
}); | ||
|
||
if (availableSearchFields.length === 0) { | ||
process.env.HEAVY_DEBUG && console.log(`⚠️ No searchable fields available in polymorphic target resource '${targetResource.resourceId}', skipping resource.`); | ||
resolve({ items: [] }); | ||
return; | ||
} | ||
const searchFilters = availableSearchFields.map((fieldName) => { | ||
const filter = { | ||
field: fieldName, | ||
operator: searchOperator, | ||
value: search.trim(), | ||
}; | ||
return filter; | ||
}); | ||
|
||
if (searchFilters.length > 1) { | ||
normalizedFilters.subFilters.push({ | ||
operator: AdminForthFilterOperators.OR, | ||
subFilters: searchFilters, | ||
}); | ||
} else if (searchFilters.length === 1) { | ||
normalizedFilters.subFilters.push(searchFilters[0]); | ||
} | ||
} | ||
const dbDataItems = await this.adminforth.connectors[targetResource.dataSource].getData({ | ||
resource: targetResource, | ||
limit, | ||
|
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -6,7 +6,7 @@ | |||||||||
<input | ||||||||||
ref="inputEl" | ||||||||||
type="text" | ||||||||||
:readonly="readonly" | ||||||||||
:readonly="readonly || searchDisabled" | ||||||||||
v-model="search" | ||||||||||
@click="inputClick" | ||||||||||
@input="inputInput" | ||||||||||
|
@@ -38,8 +38,9 @@ | |||||||||
</div> | ||||||||||
<teleport to="body" v-if="teleportToBody && showDropdown"> | ||||||||||
<div ref="dropdownEl" :style="getDropdownPosition" :class="{'shadow-none': isTop}" | ||||||||||
class="fixed z-[5] w-full bg-lightDropdownOptionsBackground shadow-lg dark:shadow-black dark:bg-darkDropdownOptionsBackground | ||||||||||
dark:border-gray-600 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm max-h-48"> | ||||||||||
class="fixed z-[5] w-full bg-white shadow-lg dark:shadow-black dark:bg-gray-700 | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The hardcoded colors 'bg-white' and 'dark:bg-gray-700' replace the original theme-aware classes. This could break theming consistency. Consider preserving the original 'bg-lightDropdownOptionsBackground' and 'dark:bg-darkDropdownOptionsBackground' classes.
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||||||
dark:border-gray-600 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm max-h-48" | ||||||||||
@scroll="handleDropdownScroll"> | ||||||||||
<div | ||||||||||
v-for="item in filteredItems" | ||||||||||
:key="item.value" | ||||||||||
|
@@ -61,8 +62,9 @@ | |||||||||
</teleport> | ||||||||||
|
||||||||||
<div v-if="!teleportToBody && showDropdown" ref="dropdownEl" :style="dropdownStyle" :class="{'shadow-none': isTop}" | ||||||||||
class="absolute z-10 mt-1 w-full bg-lightDropdownOptionsBackground shadow-lg text-lightDropdownOptionsText dark:shadow-black dark:bg-darkDropdownOptionsBackground | ||||||||||
dark:border-gray-600 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm max-h-48"> | ||||||||||
class="absolute z-10 mt-1 w-full bg-white shadow-lg dark:shadow-black dark:bg-gray-700 | ||||||||||
dark:border-gray-600 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm max-h-48" | ||||||||||
Comment on lines
+65
to
+66
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same theming issue as the teleported dropdown. The hardcoded colors should be replaced with the original theme-aware classes for consistency.
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||||||
@scroll="handleDropdownScroll"> | ||||||||||
<div | ||||||||||
v-for="item in filteredItems" | ||||||||||
:key="item.value" | ||||||||||
|
@@ -133,13 +135,17 @@ const props = defineProps({ | |||||||||
type: Boolean, | ||||||||||
default: false, | ||||||||||
}, | ||||||||||
searchDisabled: { | ||||||||||
type: Boolean, | ||||||||||
default: false, | ||||||||||
}, | ||||||||||
teleportToBody: { | ||||||||||
type: Boolean, | ||||||||||
default: false, | ||||||||||
}, | ||||||||||
}); | ||||||||||
|
||||||||||
const emit = defineEmits(['update:modelValue']); | ||||||||||
const emit = defineEmits(['update:modelValue', 'scroll-near-end', 'search']); | ||||||||||
|
||||||||||
const search = ref(''); | ||||||||||
const showDropdown = ref(false); | ||||||||||
|
@@ -160,6 +166,9 @@ function inputInput() { | |||||||||
selectedItems.value = []; | ||||||||||
emit('update:modelValue', null); | ||||||||||
} | ||||||||||
if (!props.searchDisabled) { | ||||||||||
emit('search', search.value); | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
function updateFromProps() { | ||||||||||
|
@@ -178,7 +187,7 @@ function updateFromProps() { | |||||||||
} | ||||||||||
|
||||||||||
async function inputClick() { | ||||||||||
if (props.readonly) return; | ||||||||||
if (props.readonly || props.searchDisabled) return; | ||||||||||
// Toggle local dropdown | ||||||||||
showDropdown.value = !showDropdown.value; | ||||||||||
// If the dropdown is about to close, reset the search | ||||||||||
|
@@ -227,6 +236,15 @@ const handleScroll = () => { | |||||||||
} | ||||||||||
}; | ||||||||||
|
||||||||||
const handleDropdownScroll = (event: Event) => { | ||||||||||
const target = event.target as HTMLElement; | ||||||||||
const threshold = 10; // pixels from bottom | ||||||||||
|
||||||||||
if (target.scrollTop + target.clientHeight >= target.scrollHeight - threshold) { | ||||||||||
emit('scroll-near-end'); | ||||||||||
} | ||||||||||
}; | ||||||||||
|
||||||||||
onMounted(() => { | ||||||||||
updateFromProps(); | ||||||||||
|
||||||||||
|
@@ -247,7 +265,12 @@ onMounted(() => { | |||||||||
}); | ||||||||||
|
||||||||||
const filteredItems = computed(() => { | ||||||||||
return props.options.filter(item => | ||||||||||
|
||||||||||
if (props.searchDisabled) { | ||||||||||
return props.options || []; | ||||||||||
} | ||||||||||
|
||||||||||
return (props.options || []).filter((item: any) => | ||||||||||
item.label.toLowerCase().includes(search.value.toLowerCase()) | ||||||||||
); | ||||||||||
}); | ||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@SerVitasik or text at least