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:
parent
0e37f0073e
commit
aa73a33669
@ -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>;
|
||||
|
@ -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);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user