mirror of
https://github.com/frappe/books.git
synced 2024-12-23 03:19:01 +00:00
feat: add the search filters
This commit is contained in:
parent
e008028b14
commit
4768cdfe07
14
src/App.vue
14
src/App.vue
@ -39,6 +39,7 @@
|
||||
import { ConfigKeys } from 'fyo/core/types';
|
||||
import { ModelNameEnum } from 'models/types';
|
||||
import { incrementOpenCount } from 'src/utils/misc';
|
||||
import { computed } from 'vue';
|
||||
import WindowsTitleBar from './components/WindowsTitleBar.vue';
|
||||
import { handleErrorWithDialog } from './errorHandling';
|
||||
import { fyo, initializeInstance } from './initFyo';
|
||||
@ -48,6 +49,7 @@ import SetupWizard from './pages/SetupWizard/SetupWizard.vue';
|
||||
import setupInstance from './setup/setupInstance';
|
||||
import './styles/index.css';
|
||||
import { checkForUpdates } from './utils/ipcCalls';
|
||||
import { Search } from './utils/search';
|
||||
import { routeTo } from './utils/ui';
|
||||
|
||||
export default {
|
||||
@ -57,6 +59,12 @@ export default {
|
||||
activeScreen: null,
|
||||
dbPath: '',
|
||||
companyName: '',
|
||||
searcher: null,
|
||||
};
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
searcher: computed(() => this.searcher),
|
||||
};
|
||||
},
|
||||
components: {
|
||||
@ -94,6 +102,11 @@ export default {
|
||||
ModelNameEnum.AccountingSettings,
|
||||
'companyName'
|
||||
);
|
||||
await this.setSearcher();
|
||||
},
|
||||
async setSearcher() {
|
||||
this.searcher = new Search(fyo);
|
||||
await this.searcher.initializeKeywords();
|
||||
},
|
||||
async fileSelected(filePath, isNew) {
|
||||
fyo.config.set(ConfigKeys.LastSelectedFilePath, filePath);
|
||||
@ -139,6 +152,7 @@ export default {
|
||||
fyo.purgeCache();
|
||||
this.activeScreen = 'DatabaseSelector';
|
||||
this.dbPath = '';
|
||||
this.searcher = null;
|
||||
this.companyName = '';
|
||||
},
|
||||
},
|
||||
|
@ -72,7 +72,7 @@ export default defineComponent({
|
||||
emits: ['index-change'],
|
||||
props: {
|
||||
itemCount: { type: Number, default: 0 },
|
||||
allowedCounts: { type: Array, default: () => [20, 100, 500, -1] },
|
||||
allowedCounts: { type: Array, default: () => [50, 100, 500, -1] },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -20,7 +20,7 @@
|
||||
>
|
||||
<feather-icon name="search" class="w-4 h-4" />
|
||||
<p>{{ t`Search` }}</p>
|
||||
<div v-if="!inputValue" class="text-gray-400 ml-auto text-sm">
|
||||
<div class="text-gray-400 ml-auto text-sm">
|
||||
{{ modKey('k') }}
|
||||
</div>
|
||||
</button>
|
||||
@ -58,7 +58,7 @@
|
||||
<hr v-if="suggestions.length" />
|
||||
|
||||
<!-- Search List -->
|
||||
<div :style="`max-height: ${49 * 6 - 1}px`" class="overflow-auto">
|
||||
<div :style="`max-height: ${49 * 8 - 1}px`" class="overflow-auto">
|
||||
<div
|
||||
v-for="(si, i) in suggestions"
|
||||
:key="`${i}-${si.key}`"
|
||||
@ -113,31 +113,65 @@
|
||||
<hr />
|
||||
<div class="m-1 flex justify-between flex-col gap-2 text-sm select-none">
|
||||
<!-- Group Filters -->
|
||||
<div class="flex justify-between" v-if="false">
|
||||
<div class="flex gap-2">
|
||||
<div class="flex justify-between">
|
||||
<div class="flex gap-1">
|
||||
<button
|
||||
v-for="g in searchGroups"
|
||||
:key="g"
|
||||
class="border px-1 py-0.5 rounded-lg"
|
||||
:class="getGroupFilterButtonClass(g)"
|
||||
@click="groupFilters[g] = !groupFilters[g]"
|
||||
@click="searcher.set(g, !searcher.filters.groupFilters[g])"
|
||||
>
|
||||
{{ groupLabelMap[g] }}
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
class="hover:bg-gray-100 px-2 py-0.5 rounded text-gray-800"
|
||||
@click="showMore = !showMore"
|
||||
>
|
||||
{{ showMore ? t`Less Filters` : t`More Filters` }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Additional Filters -->
|
||||
<div v-if="showMore" class="-mt-1">
|
||||
<!-- Group Skip Filters -->
|
||||
<div class="flex gap-1 text-gray-800">
|
||||
<button
|
||||
v-for="s in ['skipTables', 'skipTransactions']"
|
||||
:key="s"
|
||||
class="border px-1 py-0.5 rounded-lg"
|
||||
:class="{ 'bg-gray-200': searcher.filters[s] }"
|
||||
@click="searcher.set(s, !searcher.filters[s])"
|
||||
>
|
||||
{{
|
||||
s === 'skipTables' ? t`Skip Child Tables` : t`Skip Transactions`
|
||||
}}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Schema Name Filters -->
|
||||
<div class="flex mt-1 gap-1 text-blue-500 flex-wrap">
|
||||
<button
|
||||
v-for="sf in schemaFilters"
|
||||
:key="sf.value"
|
||||
class="
|
||||
bg-gray-100
|
||||
hover:bg-gray-200
|
||||
px-2
|
||||
border
|
||||
px-1
|
||||
py-0.5
|
||||
rounded
|
||||
text-gray-800
|
||||
rounded-lg
|
||||
border-blue-100
|
||||
whitespace-nowrap
|
||||
"
|
||||
:class="{ 'bg-blue-100': searcher.filters.schemaFilters[sf.value] }"
|
||||
@click="
|
||||
searcher.set(sf.value, !searcher.filters.schemaFilters[sf.value])
|
||||
"
|
||||
>
|
||||
{{ t`More Filters` }}
|
||||
{{ sf.label }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Keybindings Help -->
|
||||
<div class="flex text-sm text-gray-500 justify-between">
|
||||
@ -146,16 +180,37 @@
|
||||
<p>↩ {{ t`Select` }}</p>
|
||||
<p><span class="tracking-tighter">esc</span> {{ t`Close` }}</p>
|
||||
</div>
|
||||
<p v-if="suggestions.length">
|
||||
{{ t`${suggestions.length} out of ${totalLength} shown` }}
|
||||
|
||||
<p v-if="searcher?.numSearches" class="ml-auto mr-2">
|
||||
{{ t`${suggestions.length} out of ${searcher.numSearches}` }}
|
||||
</p>
|
||||
|
||||
<div
|
||||
class="border border-gray-100 rounded flex justify-self-end"
|
||||
v-if="(searcher?.numSearches ?? 0) > 50"
|
||||
>
|
||||
<template
|
||||
v-for="c in allowedLimits.filter(
|
||||
(c) => c < searcher.numSearches || c === -1
|
||||
)"
|
||||
:key="c + '-count'"
|
||||
>
|
||||
<button
|
||||
@click="limit = parseInt(c)"
|
||||
class="w-9"
|
||||
:class="limit === c ? 'bg-gray-100' : ''"
|
||||
>
|
||||
{{ c === -1 ? t`All` : c }}
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
<script>
|
||||
import { getBgTextColorClass } from 'src/utils/colors';
|
||||
import { getGroupLabelMap, searcher, searchGroups } from 'src/utils/search';
|
||||
import { getGroupLabelMap, searchGroups } from 'src/utils/search';
|
||||
import { useKeys } from 'src/utils/vueUtils';
|
||||
import { getIsNullOrUndef } from 'utils/';
|
||||
import { nextTick, watch } from 'vue';
|
||||
@ -172,23 +227,14 @@ export default {
|
||||
searchGroups,
|
||||
openModal: false,
|
||||
inputValue: '',
|
||||
searchList: [],
|
||||
searcher: null,
|
||||
totalLength: 0,
|
||||
groupFilters: {
|
||||
List: true,
|
||||
Report: true,
|
||||
Create: true,
|
||||
Page: true,
|
||||
Docs: true,
|
||||
},
|
||||
showMore: false,
|
||||
limit: 50,
|
||||
allowedLimits: [50, 100, 500, -1],
|
||||
};
|
||||
},
|
||||
inject: ['searcher'],
|
||||
components: { Modal },
|
||||
async mounted() {
|
||||
this.searcher = searcher;
|
||||
await this.searcher.initializeKeywords();
|
||||
|
||||
if (fyo.store.isDevelopment) {
|
||||
window.search = this;
|
||||
}
|
||||
@ -240,11 +286,12 @@ export default {
|
||||
const digit = matches[1];
|
||||
const index = parseInt(digit) - 1;
|
||||
const group = searchGroups[index];
|
||||
if (!group || this.groupFilters[group] === undefined) {
|
||||
const value = this.searcher.filters.groupFilters[group];
|
||||
if (!group || typeof value !== 'boolean') {
|
||||
return;
|
||||
}
|
||||
|
||||
this.groupFilters[group] = !this.groupFilters[group];
|
||||
this.searcher.set(group, !value);
|
||||
},
|
||||
modKey(key) {
|
||||
key = key.toUpperCase();
|
||||
@ -266,9 +313,6 @@ export default {
|
||||
this.reset();
|
||||
},
|
||||
reset() {
|
||||
this.searchGroups.forEach((g) => {
|
||||
this.groupFilters[g] = true;
|
||||
});
|
||||
this.inputValue = '';
|
||||
},
|
||||
up() {
|
||||
@ -292,7 +336,7 @@ export default {
|
||||
ref.scrollIntoView({ block: 'nearest' });
|
||||
},
|
||||
getGroupFilterButtonClass(g) {
|
||||
const isOn = this.groupFilters[g];
|
||||
const isOn = this.searcher.filters.groupFilters[g];
|
||||
const color = this.groupColorMap[g];
|
||||
if (isOn) {
|
||||
return `${getBgTextColorClass(color)} border-${color}-100`;
|
||||
@ -305,6 +349,23 @@ export default {
|
||||
groupLabelMap() {
|
||||
return getGroupLabelMap();
|
||||
},
|
||||
schemaFilters() {
|
||||
const schemaNames = Object.keys(this.searcher?.searchables) ?? [];
|
||||
return schemaNames
|
||||
.map((sn) => {
|
||||
const schema = fyo.schemaMap[sn];
|
||||
const value = sn;
|
||||
const label = schema.label;
|
||||
let index = 1;
|
||||
if (schema.isSubmittable) {
|
||||
index = 0;
|
||||
} else if (schema.isChild) {
|
||||
index = 2;
|
||||
}
|
||||
return { value, label, index };
|
||||
})
|
||||
.sort((a, b) => a.index - b.index);
|
||||
},
|
||||
groupColorMap() {
|
||||
return {
|
||||
Docs: 'blue',
|
||||
@ -322,9 +383,11 @@ export default {
|
||||
},
|
||||
suggestions() {
|
||||
const suggestions = this.searcher.search(this.inputValue);
|
||||
// eslint-disable-next-line
|
||||
this.totalLength = suggestions.length;
|
||||
return suggestions.slice(0, 50);
|
||||
if (this.limit === -1) {
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
return suggestions.slice(0, this.limit);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -17,7 +17,9 @@ export default function registerIpcRendererListeners() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fyo.store.isDevelopment) {
|
||||
console.log(...stuff);
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('visibilitychange', function () {
|
||||
|
@ -1,10 +1,9 @@
|
||||
import { t } from 'fyo';
|
||||
import { Fyo, t } from 'fyo';
|
||||
import { RawValueMap } from 'fyo/core/types';
|
||||
import { groupBy } from 'lodash';
|
||||
import { ModelNameEnum } from 'models/types';
|
||||
import { reports } from 'reports';
|
||||
import { OptionField } from 'schemas/types';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { getEntryRoute } from 'src/router';
|
||||
import { GetAllOptions } from 'utils/db/types';
|
||||
import { fuzzyMatch } from '.';
|
||||
@ -71,7 +70,7 @@ export function getGroupLabelMap() {
|
||||
};
|
||||
}
|
||||
|
||||
async function openQuickEditDoc(schemaName: string) {
|
||||
async function openQuickEditDoc(schemaName: string, fyo: Fyo) {
|
||||
await routeTo(`/list/${schemaName}`);
|
||||
const doc = await fyo.doc.getNewDoc(schemaName);
|
||||
const { openQuickEdit } = await import('src/utils/ui');
|
||||
@ -82,14 +81,14 @@ async function openQuickEditDoc(schemaName: string) {
|
||||
});
|
||||
}
|
||||
|
||||
async function openFormEditDoc(schemaName: string) {
|
||||
async function openFormEditDoc(schemaName: string, fyo: Fyo) {
|
||||
const doc = fyo.doc.getNewDoc(schemaName);
|
||||
const name = doc.name;
|
||||
|
||||
routeTo(`/edit/${schemaName}/${name}`);
|
||||
}
|
||||
|
||||
function getCreateList(): SearchItem[] {
|
||||
function getCreateList(fyo: Fyo): SearchItem[] {
|
||||
const quickEditCreateList = [
|
||||
ModelNameEnum.Item,
|
||||
ModelNameEnum.Party,
|
||||
@ -100,7 +99,7 @@ function getCreateList(): SearchItem[] {
|
||||
label: fyo.schemaMap[schemaName]?.label,
|
||||
group: 'Create',
|
||||
action() {
|
||||
openQuickEditDoc(schemaName);
|
||||
openQuickEditDoc(schemaName, fyo);
|
||||
},
|
||||
} as SearchItem)
|
||||
);
|
||||
@ -115,7 +114,7 @@ function getCreateList(): SearchItem[] {
|
||||
label: fyo.schemaMap[schemaName]?.label,
|
||||
group: 'Create',
|
||||
action() {
|
||||
openFormEditDoc(schemaName);
|
||||
openFormEditDoc(schemaName, fyo);
|
||||
},
|
||||
} as SearchItem)
|
||||
);
|
||||
@ -169,7 +168,7 @@ function getCreateList(): SearchItem[] {
|
||||
return [quickEditCreateList, formEditCreateList, filteredCreateList].flat();
|
||||
}
|
||||
|
||||
function getReportList(): SearchItem[] {
|
||||
function getReportList(fyo: Fyo): SearchItem[] {
|
||||
const hasGstin = !!fyo.singles?.AccountingSettings?.gstin;
|
||||
return Object.keys(reports)
|
||||
.filter((r) => {
|
||||
@ -189,7 +188,7 @@ function getReportList(): SearchItem[] {
|
||||
});
|
||||
}
|
||||
|
||||
function getListViewList(): SearchItem[] {
|
||||
function getListViewList(fyo: Fyo): SearchItem[] {
|
||||
let schemaNames = [
|
||||
ModelNameEnum.Account,
|
||||
ModelNameEnum.Party,
|
||||
@ -268,8 +267,13 @@ function getSetupList(): SearchItem[] {
|
||||
];
|
||||
}
|
||||
|
||||
function getNonDocSearchList() {
|
||||
return [getListViewList(), getCreateList(), getReportList(), getSetupList()]
|
||||
function getNonDocSearchList(fyo: Fyo) {
|
||||
return [
|
||||
getListViewList(fyo),
|
||||
getCreateList(fyo),
|
||||
getReportList(fyo),
|
||||
getSetupList(),
|
||||
]
|
||||
.flat()
|
||||
.map((d) => {
|
||||
if (d.route && !d.action) {
|
||||
@ -281,8 +285,27 @@ function getNonDocSearchList() {
|
||||
});
|
||||
}
|
||||
|
||||
class Search {
|
||||
export class Search {
|
||||
/**
|
||||
* A simple fuzzy searcher.
|
||||
*
|
||||
* How the Search works:
|
||||
* - Pulls `keywordFields` (string columns) from the db.
|
||||
* - `name` or `parent` (parent doc's name) is used as the main
|
||||
* label.
|
||||
* - The `name`, `keywordFields` and schema label are used as
|
||||
* search target terms.
|
||||
* - Input is split on `' '` (whitespace) and each part has to completely
|
||||
* or partially match the search target terms.
|
||||
* - Non matches are ignored.
|
||||
* - Each letter in the input narrows the search using the `this._intermediate`
|
||||
* object where the incremental searches are stored.
|
||||
* - Search index is marked for updation when a doc is entered or deleted.
|
||||
* - Marked indices are rebuilt when the modal is opened.
|
||||
*/
|
||||
|
||||
_obsSet: boolean = false;
|
||||
numSearches: number = 0;
|
||||
searchables: Record<string, Searchable>;
|
||||
keywords: Record<string, Keyword[]>;
|
||||
priorityMap: Record<string, number> = {
|
||||
@ -307,15 +330,79 @@ class Search {
|
||||
skipTransactions: false,
|
||||
};
|
||||
|
||||
fyo: Fyo;
|
||||
|
||||
_intermediate: SearchIntermediate = { suggestions: [] };
|
||||
|
||||
_nonDocSearchList: SearchItem[];
|
||||
_groupLabelMap?: Record<SearchGroup, string>;
|
||||
|
||||
constructor() {
|
||||
constructor(fyo: Fyo) {
|
||||
this.fyo = fyo;
|
||||
this.keywords = {};
|
||||
this.searchables = {};
|
||||
this._nonDocSearchList = getNonDocSearchList();
|
||||
this._nonDocSearchList = getNonDocSearchList(fyo);
|
||||
}
|
||||
|
||||
/**
|
||||
* these getters are used for hacky two way binding between the
|
||||
* `skipT*` filters and the `schemaFilters`.
|
||||
*/
|
||||
|
||||
get skipTables() {
|
||||
let value = true;
|
||||
for (const val of Object.values(this.searchables)) {
|
||||
if (!val.isChild) {
|
||||
continue;
|
||||
}
|
||||
|
||||
value &&= !this.filters.schemaFilters[val.schemaName];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
get skipTransactions() {
|
||||
let value = true;
|
||||
for (const val of Object.values(this.searchables)) {
|
||||
if (!val.isSubmittable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
value &&= !this.filters.schemaFilters[val.schemaName];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
set(filterName: string, value: boolean) {
|
||||
/**
|
||||
* When a filter is set, intermediate is reset
|
||||
* this way the groups are rebuild with the filters
|
||||
* applied.
|
||||
*/
|
||||
|
||||
if (filterName in this.filters.groupFilters) {
|
||||
this.filters.groupFilters[filterName as SearchGroup] = value;
|
||||
} else if (filterName in this.searchables) {
|
||||
this.filters.schemaFilters[filterName] = value;
|
||||
this.filters.skipTables = this.skipTables;
|
||||
this.filters.skipTransactions = this.skipTransactions;
|
||||
} else if (filterName === 'skipTables') {
|
||||
Object.values(this.searchables)
|
||||
.filter(({ isChild }) => isChild)
|
||||
.forEach(({ schemaName }) => {
|
||||
this.filters.schemaFilters[schemaName] = !value;
|
||||
});
|
||||
this.filters.skipTables = value;
|
||||
} else if (filterName === 'skipTransactions') {
|
||||
Object.values(this.searchables)
|
||||
.filter(({ isSubmittable }) => isSubmittable)
|
||||
.forEach(({ schemaName }) => {
|
||||
this.filters.schemaFilters[schemaName] = !value;
|
||||
});
|
||||
this.filters.skipTransactions = value;
|
||||
}
|
||||
|
||||
this._setIntermediate([]);
|
||||
}
|
||||
|
||||
async initializeKeywords() {
|
||||
@ -324,6 +411,18 @@ class Search {
|
||||
this._setDocObservers();
|
||||
this._setSchemaFilters();
|
||||
this._groupLabelMap = getGroupLabelMap();
|
||||
this._setFilterDefaults();
|
||||
}
|
||||
|
||||
_setFilterDefaults() {
|
||||
const totalChildKeywords = Object.values(this.searchables)
|
||||
.filter((s) => s.isChild)
|
||||
.map((s) => this.keywords[s.schemaName].length)
|
||||
.reduce((a, b) => a + b);
|
||||
|
||||
if (totalChildKeywords > 2_000) {
|
||||
this.set('skipTables', true);
|
||||
}
|
||||
}
|
||||
|
||||
_setSchemaFilters() {
|
||||
@ -347,13 +446,13 @@ class Search {
|
||||
options.orderBy = 'modified';
|
||||
}
|
||||
|
||||
const maps = await fyo.db.getAllRaw(searchable.schemaName, options);
|
||||
const maps = await this.fyo.db.getAllRaw(searchable.schemaName, options);
|
||||
this._setKeywords(maps, searchable);
|
||||
this.searchables[searchable.schemaName].needsUpdate = false;
|
||||
}
|
||||
}
|
||||
|
||||
searchSuggestions(input: string): SearchItems {
|
||||
_searchSuggestions(input: string): SearchItems {
|
||||
const matches: { si: SearchItem | DocSearchItem; distance: number }[] = [];
|
||||
|
||||
for (const si of this._intermediate.suggestions) {
|
||||
@ -372,12 +471,11 @@ class Search {
|
||||
|
||||
matches.sort((a, b) => a.distance - b.distance);
|
||||
const suggestions = matches.map((m) => m.si);
|
||||
console.log('here', suggestions.length, input);
|
||||
this.setIntermediate(suggestions, input);
|
||||
this._setIntermediate(suggestions, input);
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
shouldUseSuggestions(input?: string): boolean {
|
||||
_shouldUseSuggestions(input?: string): boolean {
|
||||
if (!input) {
|
||||
return false;
|
||||
}
|
||||
@ -394,31 +492,23 @@ class Search {
|
||||
return true;
|
||||
}
|
||||
|
||||
setIntermediate(suggestions: SearchItems, previousInput?: string) {
|
||||
_setIntermediate(suggestions: SearchItems, previousInput?: string) {
|
||||
this.numSearches = suggestions.length;
|
||||
this._intermediate.suggestions = suggestions;
|
||||
this._intermediate.previousInput = previousInput;
|
||||
}
|
||||
|
||||
search(input?: string): SearchItems {
|
||||
const useSuggestions = this.shouldUseSuggestions(input);
|
||||
/*
|
||||
console.log(
|
||||
input,
|
||||
this._intermediate.previousInput,
|
||||
useSuggestions,
|
||||
this._intermediate.suggestions.length
|
||||
);
|
||||
*/
|
||||
|
||||
const useSuggestions = this._shouldUseSuggestions(input);
|
||||
/**
|
||||
* If the suggestion list is already populated
|
||||
* and the input is an extention of the previous
|
||||
* then use the suggestions.
|
||||
*/
|
||||
if (useSuggestions) {
|
||||
// return this.searchSuggestions(input!);
|
||||
return this._searchSuggestions(input!);
|
||||
} else {
|
||||
this.setIntermediate([]);
|
||||
this._setIntermediate([]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -439,7 +529,7 @@ class Search {
|
||||
}
|
||||
}
|
||||
|
||||
this.setIntermediate(array, input);
|
||||
this._setIntermediate(array, input);
|
||||
return array;
|
||||
}
|
||||
|
||||
@ -513,7 +603,7 @@ class Search {
|
||||
*/
|
||||
|
||||
let distance = Number.MAX_SAFE_INTEGER;
|
||||
for (const part of input.split(' ')) {
|
||||
for (const part of input.split(' ').filter(Boolean)) {
|
||||
const match = this._getInternalMatch(part, values);
|
||||
if (!match.isMatch) {
|
||||
return { isMatch: false, distance: Number.MAX_SAFE_INTEGER };
|
||||
@ -554,7 +644,7 @@ class Search {
|
||||
|
||||
_getDocSearchItemFromKeyword(keyword: Keyword): DocSearchItem {
|
||||
const schemaName = keyword.meta.schemaName as string;
|
||||
const schemaLabel = fyo.schemaMap[schemaName]?.label!;
|
||||
const schemaLabel = this.fyo.schemaMap[schemaName]?.label!;
|
||||
const route = this._getRouteFromKeyword(keyword);
|
||||
return {
|
||||
label: keyword.values[0],
|
||||
@ -603,8 +693,8 @@ class Search {
|
||||
}
|
||||
|
||||
_setSearchables() {
|
||||
for (const schemaName of Object.keys(fyo.schemaMap)) {
|
||||
const schema = fyo.schemaMap[schemaName];
|
||||
for (const schemaName of Object.keys(this.fyo.schemaMap)) {
|
||||
const schema = this.fyo.schemaMap[schemaName];
|
||||
if (!schema?.keywordFields?.length || this.searchables[schemaName]) {
|
||||
continue;
|
||||
}
|
||||
@ -636,11 +726,11 @@ class Search {
|
||||
}
|
||||
|
||||
for (const { schemaName } of Object.values(this.searchables)) {
|
||||
fyo.doc.observer.on(`sync:${schemaName}`, () => {
|
||||
this.fyo.doc.observer.on(`sync:${schemaName}`, () => {
|
||||
this.searchables[schemaName].needsUpdate = true;
|
||||
});
|
||||
|
||||
fyo.doc.observer.on(`delete:${schemaName}`, () => {
|
||||
this.fyo.doc.observer.on(`delete:${schemaName}`, () => {
|
||||
this.searchables[schemaName].needsUpdate = true;
|
||||
});
|
||||
}
|
||||
@ -673,7 +763,7 @@ class Search {
|
||||
// Set individual field values
|
||||
for (const fn of searchable.fields) {
|
||||
let value = map[fn] as string | undefined;
|
||||
const field = fyo.getField(searchable.schemaName, fn);
|
||||
const field = this.fyo.getField(searchable.schemaName, fn);
|
||||
|
||||
const { options } = field as OptionField;
|
||||
if (options) {
|
||||
@ -722,5 +812,3 @@ class Search {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const searcher = new Search();
|
||||
|
Loading…
Reference in New Issue
Block a user