diff --git a/src/screens/Analytics/GlobalSearch/GlobalSearchBar.res b/src/screens/Analytics/GlobalSearch/GlobalSearchBar.res
index 6be146e6d..35c4984fe 100644
--- a/src/screens/Analytics/GlobalSearch/GlobalSearchBar.res
+++ b/src/screens/Analytics/GlobalSearch/GlobalSearchBar.res
@@ -16,10 +16,11 @@ let make = () => {
let (localSearchText, setLocalSearchText) = React.useState(_ => "")
let (selectedOption, setSelectedOption) = React.useState(_ => ""->getDefaultOption)
let (allOptions, setAllOptions) = React.useState(_ => [])
+ let (selectedFilter, setSelectedFilter) = React.useState(_ => None)
+ let (allFilters, setAllFilters) = React.useState(_ => [])
let (categorieSuggestionResponse, setCategorieSuggestionResponse) = React.useState(_ =>
Dict.make()->JSON.Encode.object
)
- let {userHasAccess} = GroupACLHooks.useUserGroupACLHook()
let (searchResults, setSearchResults) = React.useState(_ => [])
let merchentDetails = HSwitchUtils.useMerchantDetailsValue()
let isReconEnabled = merchentDetails.recon_status === Active
@@ -32,7 +33,7 @@ let make = () => {
let redirectOnSelect = element => {
mixpanelEvent(~eventName="global_search_redirect")
- let redirectLink = element.redirect_link->JSON.Decode.string->Option.getOr("/search")
+ let redirectLink = element.redirect_link->JSON.Decode.string->Option.getOr(defaultRoute)
if redirectLink->isNonEmptyString {
setShowModal(_ => false)
GlobalVars.appendDashboardPath(~url=redirectLink)->RescriptReactRouter.push
@@ -63,13 +64,13 @@ let make = () => {
let getSearchResults = async results => {
try {
- let url = getURL(~entityName=GLOBAL_SEARCH, ~methodType=Post)
+ let local_results = []
+ let url = getURL(~entityName=GLOBAL_SEARCH, ~methodType=Post)
let body = searchText->generateQuery
let response = await fetchDetails(url, body->JSON.Encode.object, Post)
- let local_results = []
results->Array.forEach((item: resultType) => {
switch item.section {
| Local => local_results->Array.pushMany(item.results)
@@ -87,15 +88,11 @@ let make = () => {
let values = response->getRemoteResults
results->Array.pushMany(values)
+ let defaultItem = searchText->getDefaultResult
- if results->Array.length > 0 {
- let defaultItem = searchText->getDefaultResult
- let arr = [defaultItem]->Array.concat(results)
+ let finalResults = results->Array.length > 0 ? [defaultItem]->Array.concat(results) : []
- setSearchResults(_ => arr)
- } else {
- setSearchResults(_ => [])
- }
+ setSearchResults(_ => finalResults)
setState(_ => Loaded)
} catch {
| _ => setState(_ => Loaded)
@@ -112,7 +109,7 @@ let make = () => {
React.useEffect(_ => {
let results = []
- if searchText->String.length > 0 && activeFilter->LogicUtils.isEmptyString {
+ if searchText->isNonEmptyString && searchText->getSearchValidation {
setState(_ => Loading)
let localResults: resultType = searchText->getLocalMatchedResults(hswitchTabs)
@@ -123,14 +120,10 @@ let make = () => {
if isShowRemoteResults {
getSearchResults(results)->ignore
} else {
- if results->Array.length > 0 {
- let defaultItem = searchText->getDefaultResult
- let arr = [defaultItem]->Array.concat(results)
+ let defaultItem = searchText->getDefaultResult
+ let finalResults = results->Array.length > 0 ? [defaultItem]->Array.concat(results) : []
- setSearchResults(_ => arr)
- } else {
- setSearchResults(_ => [])
- }
+ setSearchResults(_ => finalResults)
setState(_ => Loaded)
}
} else {
@@ -141,31 +134,40 @@ let make = () => {
None
}, [searchText])
+ let setFilterText = value => {
+ setActiveFilter(_ => value)
+ }
+
React.useEffect(_ => {
setSearchText(_ => "")
setLocalSearchText(_ => "")
- setActiveFilter(_ => "")
+ setFilterText("")
+ setSelectedFilter(_ => None)
None
}, [showModal])
+ let onKeyPress = event => {
+ open ReactEvent.Keyboard
+ let metaKey = event->metaKey
+ let keyPressed = event->key
+ let ctrlKey = event->ctrlKey
+ let cmdKey = Window.Navigator.platform->String.includes("Mac")
+
+ if (
+ (cmdKey && metaKey && keyPressed == global_search_activate_key) ||
+ (ctrlKey && keyPressed == global_search_activate_key)
+ ) {
+ setShowModal(_ => true)
+ }
+
+ event->preventDefault
+ }
+
React.useEffect(() => {
if userHasAccess(~groupAccess=AnalyticsView) === Access {
getCategoryOptions()->ignore
}
- let onKeyPress = event => {
- let metaKey = event->ReactEvent.Keyboard.metaKey
- let keyPressed = event->ReactEvent.Keyboard.key
- let ctrlKey = event->ReactEvent.Keyboard.ctrlKey
-
- if Window.Navigator.platform->String.includes("Mac") && metaKey && keyPressed == "k" {
- event->ReactEvent.Keyboard.preventDefault
- setShowModal(_ => true)
- } else if ctrlKey && keyPressed == "k" {
- event->ReactEvent.Keyboard.preventDefault
- setShowModal(_ => true)
- }
- }
Window.addEventListener("keydown", onKeyPress)
Some(() => Window.removeEventListener("keydown", onKeyPress))
}, [])
@@ -178,15 +180,38 @@ let make = () => {
setSearchText(_ => value)
}, ~wait=500)
+ let onFilterClicked = category => {
+ let newFilter = category.categoryType->getcategoryFromVariant
+ let lastString = searchText->getEndChar
+ if activeFilter->isNonEmptyString && lastString !== filterSeparator {
+ let end = searchText->String.length - activeFilter->String.length
+ let newText = searchText->String.substring(~start=0, ~end)
+ setLocalSearchText(_ => `${newText} ${newFilter}:`)
+ setFilterText(newFilter)
+ } else if lastString !== filterSeparator {
+ setLocalSearchText(_ => `${searchText} ${newFilter}:`)
+ setFilterText(newFilter)
+ }
+ }
+
+ let onSuggestionClicked = option => {
+ let value = activeFilter->String.split(filterSeparator)->getValueFromArray(1, "")
+ let key = if value->isNonEmptyString {
+ let end = searchText->String.length - (value->String.length + 1)
+ searchText->String.substring(~start=0, ~end)
+ } else {
+ searchText
+ }
+ let saparater = searchText->getEndChar == filterSeparator ? "" : filterSeparator
+ setLocalSearchText(_ => `${key}${saparater}${option}`)
+ setFilterText("")
+ }
+
React.useEffect(() => {
setGlobalSearchText(localSearchText)
None
}, [localSearchText])
- let setFilterText = ReactDebounce.useDebounced(value => {
- setActiveFilter(_ => value)
- }, ~wait=500)
-
let leftIcon = switch state {
| Loading =>
@@ -200,6 +225,8 @@ let make = () => {
}
+ let viewType = getViewType(~state, ~searchResults, ~searchText)
+
@@ -214,9 +241,16 @@ let make = () => {
allOptions
selectedOption
setSelectedOption
+ allFilters
+ selectedFilter
+ setSelectedFilter
+ viewType
redirectOnSelect
+ activeFilter
+ onFilterClicked
+ onSuggestionClicked
/>
- {switch getViewType(~state, ~searchResults) {
+ {switch viewType {
| Load =>
@@ -229,9 +263,12 @@ let make = () => {
| EmptyResult =>
}}
diff --git a/src/screens/Analytics/GlobalSearch/GlobalSearchBarHelper.res b/src/screens/Analytics/GlobalSearch/GlobalSearchBarHelper.res
index e907662c0..787645459 100644
--- a/src/screens/Analytics/GlobalSearch/GlobalSearchBarHelper.res
+++ b/src/screens/Analytics/GlobalSearch/GlobalSearchBarHelper.res
@@ -1,24 +1,22 @@
open LogicUtils
open GlobalSearchTypes
module RenderedComponent = {
+ open String
@react.component
let make = (~ele, ~searchText) => {
+ let defaultStyle = "font-medium text-fs-14 text-lightgray_background opacity-50"
+
listOfMatchedText(ele, searchText)
- ->Array.mapWithIndex((item, i) => {
- if (
- String.toLowerCase(item) == String.toLowerCase(searchText) && String.length(searchText) > 0
- ) {
-
Int.toString}
- className="border-searched_text_border bg-yellow-searched_text font-medium text-fs-14 text-lightgray_background opacity-50">
- {item->React.string}
+ ->Array.mapWithIndex((item, index) => {
+ let key = index->Int.toString
+ let element = item->React.string
+
+ if item->toLowerCase == searchText->toLowerCase && searchText->isNonEmptyString {
+
+ element
} else {
- Int.toString}
- className="font-medium text-fs-14 text-lightgray_background opacity-50">
- {item->React.string}
-
+ element
}
})
->React.array
@@ -65,7 +63,8 @@ module EmptyResult = {
module OptionWrapper = {
@react.component
let make = (~index, ~value, ~children, ~selectedOption, ~redirectOnSelect) => {
- let activeClass = value == selectedOption ? "bg-gray-100 rounded-lg p-2 group items-center" : ""
+ let activeClass = value == selectedOption ? "bg-gray-100 rounded-lg border" : ""
+
value->redirectOnSelect}
className={`flex ${activeClass} flex-row truncate hover:bg-gray-100 cursor-pointer hover:rounded-lg p-2 group items-center`}
@@ -79,6 +78,8 @@ module ModalWrapper = {
open FramerMotion.Motion
@react.component
let make = (~showModal, ~setShowModal, ~children) => {
+ let borderRadius = ["15px", "15px", "15px", "15px"]
+
{children}
@@ -106,9 +107,33 @@ module ShowMoreLink = {
~textStyleClass="",
~searchText,
) => {
- 10}>
+ let totalCount = section.total_results
+
+ let generateLink = (path, domain) => {
+ `${path}?query=${searchText}&domain=${domain}`
+ }
+
+ let onClick = _ => {
+ let link = switch section.section {
+ | PaymentAttempts => generateLink("payment-attempts", "payment_attempts")
+ | SessionizerPaymentAttempts =>
+ generateLink("payment-attempts", "sessionizer_payment_attempts")
+ | PaymentIntents => generateLink("payment-intents", "payment_intents")
+ | SessionizerPaymentIntents => generateLink("payment-intents", "sessionizer_payment_intents")
+ | Refunds => generateLink("refunds-global", "refunds")
+ | SessionizerPaymentRefunds => generateLink("refunds-global", "sessionizer_refunds")
+ | Disputes => generateLink("dispute-global", "disputes")
+ | SessionizerPaymentDisputes => generateLink("dispute-global", "sessionizer_disputes")
+ | Local
+ | Others
+ | Default => ""
+ }
+ GlobalVars.appendDashboardPath(~url=link)->RescriptReactRouter.push
+ cleanUpFunction()
+ }
+
+ 10}>
{
- let totalCount = section.total_results
let suffix = totalCount > 1 ? "s" : ""
let linkText = `View ${totalCount->Int.toString} result${suffix}`
@@ -122,27 +147,7 @@ module ShowMoreLink = {
| Refunds
| Disputes =>
{
- let link = switch section.section {
- | PaymentAttempts => `payment-attempts?query=${searchText}&domain=payment_attempts`
- | SessionizerPaymentAttempts =>
- `payment-attempts?query=${searchText}&domain=sessionizer_payment_attempts`
- | PaymentIntents => `payment-intents?query=${searchText}&domain=payment_intents`
- | SessionizerPaymentIntents =>
- `payment-intents?query=${searchText}&domain=sessionizer_payment_intents`
- | Refunds => `refunds-global?query=${searchText}&domain=refunds`
- | SessionizerPaymentRefunds =>
- `refunds-global?query=${searchText}&domain=sessionizer_refunds`
- | Disputes => `dispute-global?query=${searchText}&domain=disputes`
- | SessionizerPaymentDisputes =>
- `dispute-global?query=${searchText}&domain=sessionizer_disputes`
- | Local
- | Others
- | Default => ""
- }
- GlobalVars.appendDashboardPath(~url=link)->RescriptReactRouter.push
- cleanUpFunction()
- }}
+ onClick
className={`font-medium cursor-pointer underline underline-offset-2 opacity-50 ${textStyleClass}`}>
{linkText->React.string}
@@ -212,30 +217,46 @@ module SearchResultsComponent = {
}
}
-let sidebarScrollbarCss = `
- @supports (-webkit-appearance: none){
- .sidebar-scrollbar {
- scrollbar-width: auto;
- scrollbar-color: #8a8c8f;
- }
-
- .sidebar-scrollbar::-webkit-scrollbar {
- display: block;
- overflow: scroll;
- height: 4px;
- width: 5px;
- }
-
- .sidebar-scrollbar::-webkit-scrollbar-thumb {
- background-color: #8a8c8f;
- border-radius: 3px;
- }
-
- .sidebar-scrollbar::-webkit-scrollbar-track {
- display: none;
- }
+module FilterOption = {
+ @react.component
+ let make = (~onClick, ~value, ~placeholder=None, ~filter, ~selectedFilter=None) => {
+ let activeBg = "bg-gray-200"
+ let wrapperBg = "bg-gray-400/40"
+ let rounded = "rounded-lg"
+
+ let (activeWrapperClass, activeClass) = switch selectedFilter {
+ | Some(val) =>
+ filter == val
+ ? (`${activeBg} ${rounded} border`, `${wrapperBg} border`)
+ : (
+ `hover:${activeBg} hover:${rounded} hover:border`,
+ `hover:${wrapperBg} hover:border ${activeBg}`,
+ )
+ | None => (
+ `hover:${activeBg} hover:${rounded} hover:border`,
+ `hover:${wrapperBg} hover:border ${activeBg}`,
+ )
+ }
+
+
+
+ {value->React.string}
+
+
Option.isSome}>
+ {placeholder->Option.getOr("")->React.string}
+
+
+ }
+}
+
+module NoResults = {
+ @react.component
+ let make = () => {
+ {"No Results"->React.string}
+ }
}
- `
module FilterResultsComponent = {
open GlobalSearchBarUtils
@@ -244,20 +265,22 @@ module FilterResultsComponent = {
let make = (
~categorySuggestions: array,
~activeFilter,
- ~setActiveFilter,
~searchText,
- ~setLocalSearchText,
+ ~setAllFilters,
+ ~selectedFilter,
+ ~setSelectedFilter,
+ ~onFilterClicked,
+ ~onSuggestionClicked,
) => {
- let filterKey = activeFilter->String.split(":")->getValueFromArray(0, "")
+ let filterKey = activeFilter->String.split(filterSeparator)->getValueFromArray(0, "")
let filters = categorySuggestions->Array.filter(category => {
- if !(activeFilter->isEmptyString) {
- if searchText->String.charAt(searchText->String.length - 1) == ":" {
- `${category.categoryType->getcategoryFromVariant}:` == `${filterKey}:`
+ if activeFilter->isNonEmptyString {
+ let categoryType = category.categoryType->getcategoryFromVariant
+ if searchText->getEndChar == filterSeparator {
+ `${categoryType}:` == `${filterKey}:`
} else {
- category.categoryType
- ->getcategoryFromVariant
- ->String.includes(filterKey)
+ categoryType->String.includes(filterKey)
}
} else {
true
@@ -272,6 +295,49 @@ module FilterResultsComponent = {
}
}
+ let updateAllFilters = () => {
+ if filters->Array.length == 1 {
+ switch filters->Array.get(0) {
+ | Some(filter) =>
+ if filter.options->Array.length > 0 && filters->checkFilterKey {
+ let filterValue = activeFilter->String.split(filterSeparator)->getValueFromArray(1, "")
+
+ let options = if filterValue->isNonEmptyString {
+ filter.options->Array.filter(option => option->String.includes(filterValue))
+ } else {
+ filter.options
+ }
+
+ let newFilters = options->Array.map(option => {
+ let value = {
+ categoryType: filter.categoryType,
+ options: [option],
+ placeholder: filter.placeholder,
+ }
+
+ value
+ })
+ setAllFilters(_ => newFilters)
+ } else {
+ setAllFilters(_ => filters)
+ }
+ | _ => ()
+ }
+ } else {
+ setAllFilters(_ => filters)
+ }
+ }
+
+ React.useEffect(() => {
+ updateAllFilters()
+ None
+ }, [activeFilter])
+
+ React.useEffect(() => {
+ setSelectedFilter(_ => None)
+ None
+ }, [filters->Array.length])
+
Array.length > 0}>
{React.string(sidebarScrollbarCss)}
{switch filters->Array.get(0) {
| Some(value) =>
- value.options
- ->Array.map(option => {
-
{
- let saparater =
- searchText->String.charAt(searchText->String.length - 1) == ":" ? "" : ":"
- setLocalSearchText(_ => `${searchText}${saparater}${option}`)
- setActiveFilter(_ => "")
- }}>
-
-
- {`${value.categoryType
- ->getcategoryFromVariant
- ->String.toLocaleLowerCase} : ${option}`->React.string}
-
-
-
- })
- ->React.array
- | _ => React.null
+ let filterValue =
+ activeFilter->String.split(filterSeparator)->getValueFromArray(1, "")
+
+ let options = if filterValue->isNonEmptyString {
+ value.options->Array.filter(option => option->String.includes(filterValue))
+ } else {
+ value.options
+ }
+
+ if options->Array.length > 0 {
+ options
+ ->Array.map(option => {
+ let filter = {
+ categoryType: value.categoryType,
+ options: [option],
+ placeholder: value.placeholder,
+ }
+
+ let itemValue = `${value.categoryType
+ ->getcategoryFromVariant
+ ->String.toLocaleLowerCase} : ${option}`
+
+
option->onSuggestionClicked}
+ value=itemValue
+ filter
+ selectedFilter
+ />
+ })
+ ->React.array
+ } else {
+
+ }
+ | _ =>
}}
Array.length === 1 && filters->checkFilterKey)}>
{filters
->Array.map(category => {
- {
- let newFilter = category.categoryType->getcategoryFromVariant
- let lastString = searchText->String.charAt(searchText->String.length - 1)
- if activeFilter->isNonEmptyString && lastString !== ":" {
- let end = searchText->String.length - activeFilter->String.length
- let newText = searchText->String.substring(~start=0, ~end)
- setLocalSearchText(_ => `${newText} ${newFilter}:`)
- setActiveFilter(_ => newFilter)
- } else if lastString !== ":" {
- setLocalSearchText(_ => `${searchText} ${newFilter}:`)
- setActiveFilter(_ => newFilter)
- }
- }}>
-
-
- {`${category.categoryType
- ->getcategoryFromVariant
- ->String.toLocaleLowerCase} : `->React.string}
-
-
-
{category.placeholder->React.string}
-
+ let itemValue = `${category.categoryType
+ ->getcategoryFromVariant
+ ->String.toLocaleLowerCase} : `
+ category->onFilterClicked}
+ value=itemValue
+ placeholder={Some(category.placeholder)}
+ filter={category}
+ selectedFilter
+ />
})
->React.array}
@@ -348,7 +413,9 @@ module FilterResultsComponent = {
}
module ModalSearchBox = {
+ open GlobalSearchBarUtils
open FramerMotion.Motion
+ open ReactEvent.Keyboard
@react.component
let make = (
~leftIcon,
@@ -360,9 +427,23 @@ module ModalSearchBox = {
~selectedOption,
~setSelectedOption,
~redirectOnSelect,
+ ~allFilters,
+ ~selectedFilter,
+ ~setSelectedFilter,
+ ~viewType,
+ ~activeFilter,
+ ~onFilterClicked,
+ ~onSuggestionClicked,
) => {
+ let inputRef = React.useRef(Nullable.null)
let (errorMessage, setErrorMessage) = React.useState(_ => "")
+ let tabKey = 9
+ let arrowDown = 40
+ let arrowUp = 38
+ let enterKey = 13
+ let spaceKey = 32
+
let input: ReactFinalForm.fieldRenderPropsInput = {
{
name: "global_search",
@@ -377,44 +458,142 @@ module ModalSearchBox = {
}
}
+ let getNextIndex = (selectedIndex, options) => {
+ let count = options->Array.length
+ selectedIndex == count - 1 ? 0 : Int.mod(selectedIndex + 1, count)
+ }
+ let getPrevIndex = (selectedIndex, options) => {
+ let count = options->Array.length
+ selectedIndex === 0 ? count - 1 : Int.mod(selectedIndex - 1, count)
+ }
+
+ let tabKeyPressHandler = e => {
+ switch inputRef.current->Js.Nullable.toOption {
+ | Some(elem) => elem->MultipleFileUpload.focus
+ | None => ()
+ }
+
+ let keyPressed = e->keyCode
+
+ switch viewType {
+ | EmptyResult | Load => ()
+ | Results => {
+ let index = allOptions->Array.findIndex(item => {
+ item == selectedOption
+ })
+
+ if keyPressed == tabKey {
+ let newIndex = getNextIndex(index, allOptions)
+ switch allOptions->Array.get(newIndex) {
+ | Some(val) => setSelectedOption(_ => val)
+ | _ => ()
+ }
+ }
+ }
+ | FiltersSugsestions => {
+ let index = allFilters->Array.findIndex(item => {
+ switch selectedFilter {
+ | Some(val) => item == val
+ | _ => false
+ }
+ })
+
+ if keyPressed == tabKey {
+ let newIndex = getNextIndex(index, allFilters)
+ switch allFilters->Array.get(newIndex) {
+ | Some(val) => setSelectedFilter(_ => val->Some)
+ | _ => ()
+ }
+ }
+ }
+ }
+ }
+
+ React.useEffect(() => {
+ Window.addEventListener("keydown", tabKeyPressHandler)
+ Some(() => Window.removeEventListener("keydown", tabKeyPressHandler))
+ }, (selectedFilter, selectedOption))
+
let handleKeyDown = e => {
- open ReactEvent.Keyboard
+ let keyPressed = e->keyCode
- let index = allOptions->Array.findIndex(item => {
- item == selectedOption
- })
+ switch viewType {
+ | Results => {
+ let index = allOptions->Array.findIndex(item => {
+ item == selectedOption
+ })
- if e->keyCode == 40 {
- let newIndex =
- index == allOptions->Array.length - 1 ? 0 : Int.mod(index + 1, allOptions->Array.length)
- switch allOptions->Array.get(newIndex) {
- | Some(val) => setSelectedOption(_ => val)
- | _ => ()
+ if keyPressed == arrowDown {
+ let newIndex = getNextIndex(index, allOptions)
+ switch allOptions->Array.get(newIndex) {
+ | Some(val) => setSelectedOption(_ => val)
+ | _ => ()
+ }
+ } else if keyPressed == arrowUp {
+ let newIndex = getPrevIndex(index, allOptions)
+ switch allOptions->Array.get(newIndex) {
+ | Some(val) => setSelectedOption(_ => val)
+ | _ => ()
+ }
+ } else if keyPressed == enterKey {
+ selectedOption->redirectOnSelect
+ }
}
- } else if e->keyCode == 38 {
- let newIndex =
- index === 0 ? allOptions->Array.length - 1 : Int.mod(index - 1, allOptions->Array.length)
- switch allOptions->Array.get(newIndex) {
- | Some(val) => setSelectedOption(_ => val)
- | _ => ()
+
+ | FiltersSugsestions => {
+ let index = allFilters->Array.findIndex(item => {
+ switch selectedFilter {
+ | Some(val) => item == val
+ | _ => false
+ }
+ })
+
+ if keyPressed == arrowDown {
+ let newIndex = getNextIndex(index, allFilters)
+ switch allFilters->Array.get(newIndex) {
+ | Some(val) => setSelectedFilter(_ => val->Some)
+ | _ => ()
+ }
+ } else if keyPressed == arrowUp {
+ let newIndex = getPrevIndex(index, allFilters)
+ switch allFilters->Array.get(newIndex) {
+ | Some(val) => setSelectedFilter(_ => val->Some)
+ | _ => ()
+ }
+ } else if keyPressed == enterKey {
+ switch selectedFilter {
+ | Some(filter) =>
+ if activeFilter->String.includes(filterSeparator) {
+ switch filter.options->Array.get(0) {
+ | Some(val) => val->onSuggestionClicked
+ | _ => ()
+ }
+ } else {
+ filter->onFilterClicked
+ }
+ | _ => ()
+ }
+ }
}
- } else if e->keyCode == 13 && localSearchText->isNonEmptyString {
- selectedOption->redirectOnSelect
+
+ | EmptyResult | Load => ()
}
- if e->keyCode === 32 {
+ if keyPressed == spaceKey {
setFilterText("")
} else {
let values = localSearchText->String.split(" ")
let filter = values->getValueFromArray(values->Array.length - 1, "")
- setFilterText(filter)
+ if activeFilter !== filter {
+ setFilterText(filter)
+ }
}
}
- let validateForm = _values => {
+ let validateForm = _ => {
let errors = Dict.make()
- let lastChar = localSearchText->String.charCodeAt(localSearchText->String.length - 1)
- if localSearchText->GlobalSearchBarUtils.validateQuery && lastChar == 32.0 {
+ let lastChar = localSearchText->getEndChar
+ if lastChar == " " {
setErrorMessage(_ => "Multiple free-text terms found")
} else if !(localSearchText->GlobalSearchBarUtils.validateQuery) {
setErrorMessage(_ => "")
@@ -437,16 +616,19 @@ module ModalSearchBox = {
{leftIcon}
- ReactDOM.Ref.domRef}
+ autoComplete="off"
autoFocus=true
placeholder="Search"
- autoComplete="off"
+ className="w-full pr-2 pl-2 text-jp-gray-900 text-opacity-75 focus:text-opacity-100 placeholder-jp-gray-900 focus:outline-none rounded h-10 text-lg font-normal placeholder-opacity-50 "
+ name={input.name}
+ label="No"
+ value=localSearchText
+ type_="text"
+ checked={false}
+ onChange=input.onChange
onKeyUp=handleKeyDown
- customStyle="bg-white border-none"
- onActiveStyle="bg-white"
- onHoverCss="bg-white"
- inputStyle="!text-lg"
/>
{
+ string->String.charAt(string->String.length - 1)
+}
+
let matchInSearchOption = (searchOptions, searchText, name, link, ~sectionName) => {
searchOptions
->Option.getOr([])
@@ -433,7 +441,7 @@ let generateFilter = (queryArray: array) => {
queryArray->Array.forEach(query => {
let keyValuePair =
query
- ->String.split(":")
+ ->String.split(filterSeparator)
->Array.filter(query => {
query->String.trim->isNonEmptyString
})
@@ -442,8 +450,14 @@ let generateFilter = (queryArray: array) => {
let value = keyValuePair->getValueFromArray(1, "")
switch filter->Dict.get(key) {
- | Some(prevArr) => filter->Dict.set(key, prevArr->Array.concat([value]))
- | _ => filter->Dict.set(key, [value])
+ | Some(prevArr) =>
+ if !(prevArr->Array.includes(value)) && value->isNonEmptyString {
+ filter->Dict.set(key, prevArr->Array.concat([value]))
+ }
+ | _ =>
+ if value->isNonEmptyString {
+ filter->Dict.set(key, [value])
+ }
}
})
@@ -467,7 +481,7 @@ let generateQuery = searchQuery => {
query->String.trim->isNonEmptyString
})
->Array.forEach(query => {
- if RegExp.test(%re("/^[^:\s]+:[^:\s]+$/"), query) {
+ if RegExp.test(%re("/^[^:\s]+:[^:\s]*$/"), query) {
filters->Array.push(query)
} else {
queryText := query
@@ -475,13 +489,15 @@ let generateQuery = searchQuery => {
})
let body = {
- let query = if filters->Array.length > 0 {
- [("filters", filters->generateFilter->JSON.Encode.object)]
+ let filterObj = filters->generateFilter
+ let query = if filters->Array.length > 0 && filterObj->Dict.keysToArray->Array.length > 0 {
+ [("filters", filterObj->JSON.Encode.object)]
} else {
[]
}
- let query = query->Array.concat([("query", queryText.contents->JSON.Encode.string)])
+ let query =
+ query->Array.concat([("query", queryText.contents->String.trim->JSON.Encode.string)])
query->Dict.fromArray
}
@@ -506,15 +522,53 @@ let validateQuery = searchQuery => {
freeTextCount.contents > 1
}
-let getViewType = (~state, ~searchResults) => {
+let getViewType = (~state, ~searchResults, ~searchText) => {
switch state {
| Loading => Load
- | Loaded =>
- if searchResults->Array.length > 0 {
- Results
- } else {
- EmptyResult
+ | Loaded => {
+ let endChar = searchText->String.charAt(searchText->String.length - 1)
+ let isFilter = endChar == filterSeparator || endChar == " "
+
+ if isFilter {
+ FiltersSugsestions
+ } else if searchResults->Array.length > 0 {
+ Results
+ } else {
+ EmptyResult
+ }
}
| Idle => FiltersSugsestions
}
}
+
+let getSearchValidation = query => {
+ let paylod = query->generateQuery
+ let query = paylod->getString("query", "")->String.trim
+
+ !(paylod->getObj("filters", Dict.make())->isEmptyDict && query->isEmptyString)
+}
+
+let sidebarScrollbarCss = `
+ @supports (-webkit-appearance: none){
+ .sidebar-scrollbar {
+ scrollbar-width: auto;
+ scrollbar-color: #8a8c8f;
+ }
+
+ .sidebar-scrollbar::-webkit-scrollbar {
+ display: block;
+ overflow: scroll;
+ height: 4px;
+ width: 5px;
+ }
+
+ .sidebar-scrollbar::-webkit-scrollbar-thumb {
+ background-color: #8a8c8f;
+ border-radius: 3px;
+ }
+
+ .sidebar-scrollbar::-webkit-scrollbar-track {
+ display: none;
+ }
+}
+ `
diff --git a/src/screens/Analytics/GlobalSearchResults/SearchResultsPage.res b/src/screens/Analytics/GlobalSearchResults/SearchResultsPage.res
index 7b57545e0..c5339d611 100644
--- a/src/screens/Analytics/GlobalSearchResults/SearchResultsPage.res
+++ b/src/screens/Analytics/GlobalSearchResults/SearchResultsPage.res
@@ -95,12 +95,11 @@ let make = () => {
let {globalSearch} = HyperswitchAtom.featureFlagAtom->Recoil.useRecoilValueFromAtom
let {userHasAccess} = GroupACLHooks.useUserGroupACLHook()
let isShowRemoteResults = globalSearch && userHasAccess(~groupAccess=OperationsView) === Access
- let fallBackQuery = UrlUtils.useGetFilterDictFromUrl("")->LogicUtils.getString("query", "")
let getSearchResults = async results => {
try {
let url = getURL(~entityName=GLOBAL_SEARCH, ~methodType=Post)
- let query = searchText->isNonEmptyString ? searchText : fallBackQuery
+
let body = query->generateQuery
let response = await fetchDetails(url, body->JSON.Encode.object, Post)