2
0
mirror of https://github.com/frappe/books.git synced 2024-12-22 19:09:01 +00:00

incr: add docs to search

- still incomplete, lotsa improvements left
This commit is contained in:
18alantom 2022-05-24 00:02:28 +05:30
parent 0e37f0073e
commit aa73a33669
4 changed files with 143 additions and 37 deletions

View File

@ -3,6 +3,7 @@ import { GeneralLedger } from './GeneralLedger/GeneralLedger';
import { GSTR1 } from './GoodsAndServiceTax/GSTR1';
import { GSTR2 } from './GoodsAndServiceTax/GSTR2';
import { ProfitAndLoss } from './ProfitAndLoss/ProfitAndLoss';
import { Report } from './Report';
import { TrialBalance } from './TrialBalance/TrialBalance';
export const reports = {
@ -12,4 +13,4 @@ export const reports = {
TrialBalance,
GSTR1,
GSTR2,
};
} as Record<string, typeof Report>;

View File

@ -90,30 +90,49 @@ const keys = useKeys();
<!-- Footer -->
<hr />
<div class="m-1 flex justify-between items-center flex-col gap-2 text-sm select-none">
<div class="m-1 flex justify-between flex-col gap-2 text-sm select-none">
<!-- Group Filters -->
<div class="flex flex-row gap-2">
<button
v-for="(g, i) in searchGroups"
:key="g"
class="border px-1 py-0.5 rounded-lg"
:class="getGroupFilterButtonClass(g)"
@click="groupFilters[g] = !groupFilters[g]"
>
{{ groupLabelMap[g]
}}<span
class="ml-2 whitespace-nowrap brightness-50 tracking-tighter"
:class="`text-${groupColorMap[g]}-500`"
>{{ modKey(String(i + 1)) }}</span
<div class="flex justify-between">
<div class="flex gap-2">
<button
v-for="(g, i) in searchGroups"
:key="g"
class="border px-1 py-0.5 rounded-lg"
:class="getGroupFilterButtonClass(g)"
@click="groupFilters[g] = !groupFilters[g]"
>
{{ groupLabelMap[g]
}}<span
class="ml-2 whitespace-nowrap brightness-50 tracking-tighter"
:class="`text-${groupColorMap[g]}-500`"
>{{ modKey(String(i + 1)) }}</span
>
</button>
</div>
<button
class="
bg-gray-100
hover:bg-gray-200
px-2
py-0.5
rounded
text-gray-800
"
>
{{ t`More Filters` }}
</button>
</div>
<!-- Keybindings Help -->
<div class="flex text-sm gap-8 text-gray-500">
<p> {{ t`Navigate` }}</p>
<p> {{ t`Select` }}</p>
<p><span class="tracking-tighter">esc</span> {{ t`Close` }}</p>
<div class="flex text-sm text-gray-500 justify-between">
<div class="flex gap-4">
<p> {{ t`Navigate` }}</p>
<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>
</div>
</div>
</Modal>
@ -122,7 +141,7 @@ const keys = useKeys();
import { t } from 'fyo';
import { fuzzyMatch } from 'src/utils';
import { getBgTextColorClass } from 'src/utils/colors';
import { getSearchList, searchGroups } from 'src/utils/search';
import { docSearch, getSearchList, searchGroups } from 'src/utils/search';
import { routeTo } from 'src/utils/ui';
import { useKeys } from 'src/utils/vueUtils';
import { getIsNullOrUndef } from 'utils/';
@ -137,6 +156,8 @@ export default {
openModal: false,
inputValue: '',
searchList: [],
docSearch: null,
totalLength: 0,
groupFilters: {
List: true,
Report: true,
@ -147,7 +168,10 @@ export default {
};
},
components: { Modal },
mounted() {
async mounted() {
this.docSearch = docSearch;
await this.docSearch.fetchKeywords();
this.makeSearchList();
watch(this.keys, (keys) => {
if (
@ -297,7 +321,7 @@ export default {
this.searchGroups.filter((g) => this.groupFilters[g])
);
return this.searchList
const nonDocs = this.searchList
.filter((si) => filters.has(si.group))
.map((si) => ({
...fuzzyMatch(this.inputValue, `${si.label} ${si.group}`),
@ -306,6 +330,15 @@ export default {
.filter(({ isMatch }) => isMatch)
.sort((a, b) => a.distance - b.distance)
.map(({ si }) => si);
let docs = [];
if (this.groupFilters.Docs && this.inputValue) {
docs = this.docSearch.search(this.inputValue);
}
const all = [docs, nonDocs].flat();
this.totalLength = all.length;
return all.slice(0, 50);
},
},
};

View File

@ -1,5 +1,6 @@
import { ipcRenderer } from 'electron';
import { ConfigKeys } from 'fyo/core/types';
import { groupBy } from 'lodash';
import { DateTime } from 'luxon';
import { IPC_ACTIONS } from 'utils/messages';
import { App as VueApp, createApp } from 'vue';
@ -105,5 +106,7 @@ function setOnWindow() {
window.fyo = fyo;
// @ts-ignore
window.DateTime = DateTime;
// @ts-ignore
window.groupBy = groupBy;
}
}

View File

@ -1,9 +1,12 @@
import { t } from 'fyo';
import { DocValueMap } from 'fyo/core/types';
import { Dictionary, groupBy } from 'lodash';
import { ModelNameEnum } from 'models/types';
import { reports } from 'reports';
import { OptionField } from 'schemas/types';
import { fyo } from 'src/initFyo';
import { GetAllOptions } from 'utils/db/types';
import { fuzzyMatch } from '.';
import { routeTo } from './ui';
export const searchGroups = ['Docs', 'List', 'Create', 'Report', 'Page'];
@ -23,6 +26,11 @@ interface SearchItem {
action?: () => void;
}
interface DocSearchItem extends SearchItem {
schemaLabel: string;
more: string[];
}
async function openQuickEditDoc(schemaName: string) {
await routeTo(`/list/${schemaName}`);
const doc = await fyo.doc.getNewDoc(schemaName);
@ -122,15 +130,23 @@ function getCreateList(): SearchItem[] {
}
function getReportList(): SearchItem[] {
/*return Object.values(reports).map((report) => {
return {
label: report.title,
route: `/report/${report.method}`,
group: 'Report',
};
});
*/
return [];
const hasGstin = !!fyo.singles?.AccountingSettings?.gstin;
return Object.keys(reports)
.filter((r) => {
const report = reports[r];
if (report.title.startsWith('GST') && !hasGstin) {
return false;
}
return true;
})
.map((r) => {
const report = reports[r];
return {
label: report.title,
route: `/report/${r}`,
group: 'Report',
};
});
}
function getListViewList(): SearchItem[] {
@ -251,13 +267,62 @@ class Search {
[ModelNameEnum.JournalEntry]: 50,
};
#groupedKeywords?: Dictionary<Keyword[]>;
constructor() {
this.keywords = {};
}
getSearchList() {
get groupedKeywords() {
if (!this.#groupedKeywords || !Object.keys(this.#groupedKeywords!).length) {
this.#groupedKeywords = this.getGroupedKeywords();
}
return this.#groupedKeywords!;
}
search(keyword: string /*, array: DocSearchItem[]*/): DocSearchItem[] {
const array: DocSearchItem[] = [];
if (!keyword) {
return [];
}
const groupedKeywords = this.groupedKeywords;
const keys = Object.keys(groupedKeywords).sort(
(a, b) => parseFloat(b) - parseFloat(a)
);
for (const key of keys) {
for (const kw of groupedKeywords[key]) {
let isMatch = false;
for (const word of kw.values) {
isMatch ||= fuzzyMatch(keyword, word).isMatch;
if (isMatch) {
break;
}
}
if (!isMatch) {
continue;
}
array.push({
label: kw.values[0],
schemaLabel: fyo.schemaMap[kw.meta.schemaName as string]?.label!,
more: kw.values.slice(1),
group: 'Docs',
action: () => {
console.log('selected', kw);
},
});
}
}
return array;
}
getGroupedKeywords() {
const keywords = Object.values(this.keywords);
return keywords.map((kw) => kw.keywords).flat();
return groupBy(keywords.map((kw) => kw.keywords).flat(), 'priority');
}
async fetchKeywords() {
@ -273,7 +338,7 @@ class Search {
}
const maps = await fyo.db.getAllRaw(searchable.schemaName, options);
this.addToSearchable(maps, searchable);
this.#addToSearchable(maps, searchable);
}
this.#setPriority();
@ -332,7 +397,7 @@ class Search {
}
}
addToSearchable(maps: DocValueMap[], searchable: Searchable) {
#addToSearchable(maps: DocValueMap[], searchable: Searchable) {
if (!maps.length) {
return;
}
@ -376,5 +441,9 @@ class Search {
}
}
//@ts-ignore
window.sc = new Search();
export const docSearch = new Search();
if (fyo.store.isDevelopment) {
//@ts-ignore
window.search = docSearch;
}