From 78ecd7627d403510543cd22674c13af726b14b06 Mon Sep 17 00:00:00 2001 From: Naomi Calabretta Date: Wed, 7 Aug 2024 03:26:28 +0200 Subject: [PATCH] enormous app improvements --- src/lib/db/entities/boardMessages.ts | 29 +--- src/lib/db/entities/chatMessages.ts | 6 +- src/lib/db/entities/chats.ts | 6 +- src/lib/db/entities/frontingEntries.ts | 74 +-------- src/lib/db/entities/journalPosts.ts | 6 +- src/lib/db/entities/members.ts | 54 +----- src/lib/db/entities/reminders.ts | 6 +- src/lib/db/entities/system.ts | 28 +--- src/lib/db/entities/tags.ts | 26 +-- src/lib/db/liveQueries.ts | 55 ------- src/lib/db/search.ts | 218 +++++++++++++++++++++++++ src/lib/theme/components/list.css | 8 + src/lib/util/filterQuery.ts | 84 +++++++++- src/main.ts | 3 +- src/modals/BoardMessageEdit.vue | 8 +- src/modals/FrontingEntryEdit.vue | 53 ++++-- src/modals/MemberEdit.vue | 67 ++++---- src/modals/MemberSelect.vue | 28 +++- src/modals/TagEdit.vue | 10 +- src/modals/TagListSelect.vue | 21 ++- src/views/options/FrontHistory.vue | 51 +++++- src/views/options/MessageBoard.vue | 49 ++++-- src/views/options/SystemSettings.vue | 37 +++-- src/views/options/TagManagement.vue | 32 +++- src/views/tabbed/Members.vue | 50 ++++-- translations/en/options.json | 1 + vite.config.ts | 3 +- 27 files changed, 630 insertions(+), 383 deletions(-) delete mode 100644 src/lib/db/liveQueries.ts create mode 100644 src/lib/db/search.ts diff --git a/src/lib/db/entities/boardMessages.ts b/src/lib/db/entities/boardMessages.ts index 5a3c3ed..2a75a9f 100644 --- a/src/lib/db/entities/boardMessages.ts +++ b/src/lib/db/entities/boardMessages.ts @@ -1,11 +1,8 @@ -import { Ref, shallowRef, watch } from "vue"; import { db } from ".."; import { makeUUIDv5 } from "../../util/uuid"; import { UUID, UUIDable } from "../types"; import { getMembersTable, Member } from "./members"; import { getSystemUUID } from "./system"; -import { from, useObservable } from "@vueuse/rxjs"; -import { liveQuery } from "dexie"; export type BoardMessage = UUIDable & { member: UUID, @@ -20,33 +17,19 @@ export function getBoardMessagesTable() { return db.boardMessages; } -export const boardMessages: Ref = shallowRef([]); - -export async function updateBoardMessagesRef() { - const boardMessagesComplete: BoardMessageComplete[] = []; - for (const boardMessage of await getBoardMessagesTable().toArray()) { - const member = (await getMembersTable().get(boardMessage.member))! - boardMessagesComplete.push({ ...boardMessage, member}); - } - boardMessages.value = boardMessagesComplete; +export async function toBoardMessageComplete(boardMessage: BoardMessage): Promise { + const member = (await getMembersTable().get(boardMessage.member))!; + return { ...boardMessage, member }; } -watch([ - useObservable(from(liveQuery(() => getBoardMessagesTable().toArray()))), - useObservable(from(liveQuery(() => getMembersTable().toArray()))), -], updateBoardMessagesRef, { immediate: true }); - - -function genid(name: string) { - return makeUUIDv5(getSystemUUID(), `boardMessages\0${name}`); +async function genid(name: string) { + return makeUUIDv5((await getSystemUUID())!, `boardMessages\0${name}`); } export async function newBoardMessage(boardMessage: Omit) { - const uuid = genid(boardMessage.title); + const uuid = await genid(boardMessage.title); return await getBoardMessagesTable().add({ ...boardMessage, uuid }); } - - diff --git a/src/lib/db/entities/chatMessages.ts b/src/lib/db/entities/chatMessages.ts index 35e9bef..8489310 100644 --- a/src/lib/db/entities/chatMessages.ts +++ b/src/lib/db/entities/chatMessages.ts @@ -14,12 +14,12 @@ export function getChatMessagesTable() { return db.chatMessages; } -function genid(name: string) { - return makeUUIDv5(getSystemUUID(), `chatMessages\0${name}`); +async function genid(name: string) { + return makeUUIDv5((await getSystemUUID())!, `chatMessages\0${name}`); } export async function newChatMessage(chatMessage: Omit) { - const uuid = genid(chatMessage.member + chatMessage.date.toTimeString()); + const uuid = await genid(chatMessage.member + chatMessage.date.toTimeString()); return await getChatMessagesTable().add({ ...chatMessage, uuid diff --git a/src/lib/db/entities/chats.ts b/src/lib/db/entities/chats.ts index b68ef2e..503feda 100644 --- a/src/lib/db/entities/chats.ts +++ b/src/lib/db/entities/chats.ts @@ -12,12 +12,12 @@ export function getChatsTable(){ return db.chats; } -function genid(name: string) { - return makeUUIDv5(getSystemUUID(), `chats\0${name}`); +async function genid(name: string) { + return makeUUIDv5((await getSystemUUID())!, `chats\0${name}`); } export async function newChat(chat: Omit) { - const uuid = genid(chat.name); + const uuid = await genid(chat.name); return await getChatsTable().add({ ...chat, uuid diff --git a/src/lib/db/entities/frontingEntries.ts b/src/lib/db/entities/frontingEntries.ts index 1cba77b..a558cb0 100644 --- a/src/lib/db/entities/frontingEntries.ts +++ b/src/lib/db/entities/frontingEntries.ts @@ -1,13 +1,8 @@ -import { Ref, shallowRef, watch } from "vue"; import { db } from ".."; import { makeUUIDv5 } from "../../util/uuid"; import { UUID, UUIDable } from "../types"; import { getSystemUUID } from "./system"; -import { from, useObservable } from "@vueuse/rxjs"; -import { liveQuery } from "dexie"; import { getMembersTable, Member } from "./members"; -import { parseFrontingHistoryFilterQuery as parseFrontingEntriesFilterQuery } from "../../util/filterQuery"; -import dayjs from "dayjs"; export type FrontingEntry = UUIDable & { member: UUID, @@ -28,28 +23,12 @@ export async function toFrontingEntryComplete(frontingEntry: FrontingEntry): Pro return { ...frontingEntry, member }; } -export const frontingEntries: Ref = shallowRef([]); - -export async function updateFrontingEntriesRef() { - const frontingEntriesComplete: FrontingEntryComplete[] = []; - for (const frontingEntry of await getFrontingEntriesTable().toArray()) { - frontingEntriesComplete.push(await toFrontingEntryComplete(frontingEntry)); - } - frontingEntries.value = frontingEntriesComplete; -} - -watch([ - useObservable(from(liveQuery(() => getFrontingEntriesTable().toArray()))), - useObservable(from(liveQuery(() => getMembersTable().toArray()))) -], updateFrontingEntriesRef, { immediate: true }); - - -function genid(name: string) { - return makeUUIDv5(getSystemUUID(), `frontingEntries\0${name}`); +async function genid(name: string) { + return makeUUIDv5((await getSystemUUID())!, `frontingEntries\0${name}`); } export async function newFrontingEntry(frontingEntry: Omit) { - const uuid = genid(frontingEntry.member + frontingEntry.startTime.getTime()); + const uuid = await genid(frontingEntry.member + frontingEntry.startTime.getTime()); return await getFrontingEntriesTable().add({ ...frontingEntry, uuid @@ -108,7 +87,7 @@ export async function getCurrentFrontEntryForMember(member: Member){ } export async function getMainFronter(){ - const mainFronterEntry = await getFrontingEntriesTable().get({ endTime: undefined, isMainFronter: true }); + const mainFronterEntry = await getFrontingEntriesTable().filter(x => x.endTime === undefined && x.isMainFronter).first(); if(mainFronterEntry){ return await getMembersTable().get(mainFronterEntry.member); } @@ -124,47 +103,4 @@ export async function getFronting() { frontingMembers.push(member); } return frontingMembers; -} - -export async function getFrontingEntriesFromFilterQuery(filterQuery: string) { - const parsed = parseFrontingEntriesFilterQuery(filterQuery); - - const filtered: FrontingEntryComplete[] = []; - - for(const x of await getFrontingEntriesTable().toArray()){ - const complete = await toFrontingEntryComplete(x); - - if (!complete.member.name.startsWith(parsed.query)) - continue; - - if (parsed.currentlyFronting) { - if (x.endTime) - continue; - } - - if (parsed.dateString) { - const date = dayjs(parsed.dateString).startOf("day"); - if (date.valueOf() !== dayjs(x.startTime).startOf("day").valueOf()) - continue; - } - - if (parsed.day) { - if (parsed.day !== dayjs(x.startTime).get("date")) - continue; - } - - if (parsed.month) { - if (parsed.month !== dayjs(x.startTime).get("month") + 1) - continue; - } - - if (parsed.year) { - if (parsed.year !== dayjs(x.startTime).get("year")) - continue; - } - - filtered.push(complete) - } - - return filtered; -} +} \ No newline at end of file diff --git a/src/lib/db/entities/journalPosts.ts b/src/lib/db/entities/journalPosts.ts index 15548c8..cc3b549 100644 --- a/src/lib/db/entities/journalPosts.ts +++ b/src/lib/db/entities/journalPosts.ts @@ -22,12 +22,12 @@ export function getJournalPostsTable() { return db.journalPosts; } -function genid(name: string) { - return makeUUIDv5(getSystemUUID(), `journalPosts\0${name}`); +async function genid(name: string) { + return makeUUIDv5((await getSystemUUID())!, `journalPosts\0${name}`); } export async function newJournalPost(journalPost: Omit) { - const uuid = genid(journalPost.member + journalPost.title); + const uuid = await genid(journalPost.member + journalPost.title); return await getJournalPostsTable().add({ ...journalPost, uuid diff --git a/src/lib/db/entities/members.ts b/src/lib/db/entities/members.ts index d3145ac..83f8287 100644 --- a/src/lib/db/entities/members.ts +++ b/src/lib/db/entities/members.ts @@ -1,11 +1,7 @@ -import { shallowRef, watch } from "vue"; import { db } from ".."; -import { parseMemberFilterQuery } from "../../util/filterQuery"; import { makeUUIDv5 } from "../../util/uuid"; import { UUID, UUIDable } from "../types"; import { getSystemUUID } from "./system"; -import { from, useObservable } from "@vueuse/rxjs"; -import { liveQuery } from "dexie"; export type Member = UUIDable & { name: string, @@ -23,58 +19,14 @@ export function getMembersTable() { return db.members; } -export const members = shallowRef([]); - -export async function updateMembersRef(){ - members.value = await getMembersTable().toArray(); -} - -watch(useObservable(from(liveQuery(() => getMembersTable().toArray()))), updateMembersRef, { immediate: true }); - -function genid(name: string) { - return makeUUIDv5(getSystemUUID(), `members\0${name}`); +async function genid(name: string) { + return makeUUIDv5((await getSystemUUID())!, `members\0${name}`); } export async function newMember(member: Omit) { - const uuid = genid(member.name); + const uuid = await genid(member.name); return await getMembersTable().add({ ...member, uuid }); } - -export async function getMembersFromFilterQuery(filterQuery: string) { - const parsed = await parseMemberFilterQuery(filterQuery); - - return getMembersTable().where("name").startsWithIgnoreCase(parsed.query).filter(x => { - - if(parsed.pronouns){ - if (!x.pronouns || x.pronouns.toLowerCase() !== parsed.pronouns.toLowerCase()) - return false; - } - - if (parsed.role) { - if (!x.role || x.role.toLowerCase() !== parsed.role.toLowerCase()) - return false; - } - - if(parsed.isArchived !== undefined){ - if(x.isArchived !== parsed.isArchived) - return false; - } - - if (parsed.isCustomFront !== undefined) { - if (x.isCustomFront !== parsed.isCustomFront) - return false; - } - - if (parsed.tags.length) { - for(const uuid of parsed.tags){ - if (!x.tags.includes(uuid)) - return false; - } - } - - return true; - }).toArray(); -} diff --git a/src/lib/db/entities/reminders.ts b/src/lib/db/entities/reminders.ts index a0edc94..f728c13 100644 --- a/src/lib/db/entities/reminders.ts +++ b/src/lib/db/entities/reminders.ts @@ -36,12 +36,12 @@ export function getRemindersTable() { return db.reminders; } -function genid(name: string) { - return makeUUIDv5(getSystemUUID(), `reminders\0${name}`); +async function genid(name: string) { + return makeUUIDv5((await getSystemUUID())!, `reminders\0${name}`); } export async function newReminder(reminder: Omit) { - const uuid = genid(reminder.name); + const uuid = await genid(reminder.name); return await getRemindersTable().add({ ...reminder, uuid diff --git a/src/lib/db/entities/system.ts b/src/lib/db/entities/system.ts index 02f53db..de72041 100644 --- a/src/lib/db/entities/system.ts +++ b/src/lib/db/entities/system.ts @@ -1,9 +1,6 @@ -import { ref, Ref, watch } from "vue"; import { db } from ".."; import { AppNamespace, makeUUIDv5 } from "../../util/uuid" import { UUIDable } from "../types" -import { from, useObservable } from "@vueuse/rxjs"; -import { liveQuery } from "dexie"; export type System = UUIDable & { name: string, @@ -15,19 +12,6 @@ export function getSystemTable(){ return db.system; } -export const system: Ref = ref({ - name: "", - uuid: "" -}); - -export async function updateSystemRef() { - const _system = await getSystemTable().toArray(); - if(_system.length) - system.value = _system[0]; -} - -watch(useObservable(from(liveQuery(() => getSystemTable().toArray()))), updateSystemRef, { immediate: true }); - export function genid(name: string) { return makeUUIDv5(AppNamespace, name); } @@ -43,14 +27,14 @@ export async function newSystem(system: Omit){ } // Extra because there shall only be one -export function getSystem(){ - return {...system.value} +export async function getSystem(){ + return await getSystemTable().toCollection().first(); } -export function getSystemUUID(){ - return system.value.uuid; +export async function getSystemUUID(){ + return (await getSystemTable().toCollection().first())?.uuid; } -export function modifySystem(system: Partial) { - return db.system.update(getSystemUUID(), system); +export async function modifySystem(system: Partial) { + return db.system.update(await getSystemUUID(), system); } diff --git a/src/lib/db/entities/tags.ts b/src/lib/db/entities/tags.ts index d769e0a..8089be8 100644 --- a/src/lib/db/entities/tags.ts +++ b/src/lib/db/entities/tags.ts @@ -3,11 +3,8 @@ import { makeUUIDv5 } from "../../util/uuid"; import { UUID, UUIDable } from "../types"; import { getSystemUUID } from "./system"; -import { getMembersTable as getMembers } from "./members"; -import { getJournalPostsTable as getJournalPosts } from "./journalPosts"; -import { liveQuery } from "dexie"; -import { useObservable, from } from "@vueuse/rxjs"; -import { Ref, shallowRef, watch } from "vue"; +import { getMembersTable } from "./members"; +import { getJournalPostsTable } from "./journalPosts"; export type Tag = UUIDable & { name: string, @@ -20,21 +17,12 @@ export function getTagsTable() { return db.tags; } -export const tags: Ref = shallowRef([]); - -export async function updateTagsRef() { - tags.value = await getTagsTable().toArray(); -} - -watch(useObservable(from(liveQuery(() => getTagsTable().toArray()))), updateTagsRef, { immediate: true }); - - -function genid(name: string) { - return makeUUIDv5(getSystemUUID(), `tags\0${name}`); +async function genid(name: string) { + return makeUUIDv5((await getSystemUUID())!, `tags\0${name}`); } export async function newTag(tag: Omit) { - const uuid = genid(tag.name); + const uuid = await genid(tag.name); return await getTagsTable().add({ ...tag, uuid @@ -44,11 +32,11 @@ export async function newTag(tag: Omit) { export async function removeTag(uuid: UUID){ const tag = await getTagsTable().get(uuid); if(tag?.type === "member"){ - await getMembers().toCollection().modify(member => { + await getMembersTable().toCollection().modify(member => { member.tags = member.tags?.filter(tag => tag !== uuid) }); } else if(tag?.type === "journal") { - await getJournalPosts().toCollection().modify(journalPost => { + await getJournalPostsTable().toCollection().modify(journalPost => { journalPost.tags = journalPost.tags?.filter(tag => tag !== uuid) }); } diff --git a/src/lib/db/liveQueries.ts b/src/lib/db/liveQueries.ts deleted file mode 100644 index ce5f476..0000000 --- a/src/lib/db/liveQueries.ts +++ /dev/null @@ -1,55 +0,0 @@ - -import { Ref, ref, shallowReactive, watch } from "vue"; -import { Member, getMembersFromFilterQuery, members } from "./entities/members"; -import { Tag, getTagsTable as getTagsTable, tags } from "./entities/tags"; -import { frontingEntries, FrontingEntryComplete, getFrontingEntriesFromFilterQuery } from "./entities/frontingEntries"; - -export function getFilteredMembers(search: Ref){ - const _members = ref([]); - - watch([ - search, - members, - ], async () => { - _members.value = await getMembersFromFilterQuery(search.value); - }, { immediate: true }); - - return _members; -} - -export function getFilteredTags(search: Ref, type: Ref) { - const _tags = shallowReactive([]); - - watch([ - search, - type, - tags - ], async () => { - _tags.length = 0; - if(!search.value.length){ - _tags.push(...await getTagsTable().filter(x => x.type === type.value).toArray()); - } else { - _tags.push(...await getTagsTable().where("name").startsWithIgnoreCase(search.value).filter(x => x.type === type.value).toArray()); - } - }, { immediate: true }); - - return _tags; -} - -export function getFilteredFrontingEntries(search: Ref){ - const _frontingEntries = ref([]); - - watch([ - search, - frontingEntries - ], async () => { - if(!search.value.length) - _frontingEntries.value = [...frontingEntries.value]; - else { - _frontingEntries.value = await getFrontingEntriesFromFilterQuery(search.value); - } - - }, { immediate: true }); - - return _frontingEntries; -} \ No newline at end of file diff --git a/src/lib/db/search.ts b/src/lib/db/search.ts new file mode 100644 index 0000000..bdad86f --- /dev/null +++ b/src/lib/db/search.ts @@ -0,0 +1,218 @@ + +import { shallowRef, Ref, ShallowRef, watch } from "vue"; +import { Member } from "./entities/members"; +import { Tag } from "./entities/tags"; +import { FrontingEntry, FrontingEntryComplete, toFrontingEntryComplete } from "./entities/frontingEntries"; +import { parseBoardMessageFilterQuery, parseFrontingHistoryFilterQuery, parseMemberFilterQuery } from "../util/filterQuery"; +import dayjs from "dayjs"; +import { BoardMessage, BoardMessageComplete, toBoardMessageComplete } from "./entities/boardMessages"; + +export function getFilteredMembers(search: Ref, members: ShallowRef){ + const _members = shallowRef([]); + + watch([ + search, + members, + ], async () => { + const parsed = await parseMemberFilterQuery(search.value); + + _members.value = members.value.filter(x => { + + if (!x.name.toLowerCase().startsWith(parsed.query.toLowerCase())) + return false; + + if (parsed.pronouns) { + if (!x.pronouns || x.pronouns.toLowerCase() !== parsed.pronouns.toLowerCase()) + return false; + } + + if (parsed.role) { + if (!x.role || x.role.toLowerCase() !== parsed.role.toLowerCase()) + return false; + } + + if (parsed.isArchived !== undefined) { + if (x.isArchived !== parsed.isArchived) + return false; + } + + if (parsed.isCustomFront !== undefined) { + if (x.isCustomFront !== parsed.isCustomFront) + return false; + } + + if (parsed.tags.length) { + for (const uuid of parsed.tags) { + if (!x.tags.includes(uuid)) + return false; + } + } + + return true; + }); + }, { immediate: true }); + + return _members; +} + +export function getFilteredTags(search: Ref, type: Ref, tags: ShallowRef) { + const _tags = shallowRef([]); + + watch([ + search, + type, + tags + ], async () => { + if(!search.value.length) + _tags.value = tags.value.filter(x => x.type === type.value); + else + _tags.value = tags.value.filter(x => x.name.toLowerCase().startsWith(search.value.toLowerCase()) && x.type === type.value); + }, { immediate: true }); + + return _tags; +} + +export function getFilteredFrontingEntries(search: Ref, frontingEntries: ShallowRef){ + const _frontingEntries = shallowRef([]); + + watch([ + search, + frontingEntries + ], async () => { + const filtered: FrontingEntryComplete[] = []; + + if(!search.value.length){ + for (const x of frontingEntries.value) + filtered.push(await toFrontingEntryComplete(x)) + } else { + const parsed = parseFrontingHistoryFilterQuery(search.value); + + for (const x of frontingEntries.value) { + const complete = await toFrontingEntryComplete(x); + + if (!complete.member.name.toLowerCase().startsWith(parsed.query.toLowerCase())) + continue; + + if (parsed.member) { + if (x.member !== parsed.member) + continue; + } + + if (parsed.currentlyFronting) { + if (x.endTime) + continue; + } + + if (parsed.startDateString) { + const date = dayjs(parsed.startDateString).startOf("day"); + if (date.valueOf() !== dayjs(x.startTime).startOf("day").valueOf()) + continue; + } + + if (parsed.endDateString) { + const date = dayjs(parsed.endDateString).startOf("day"); + if (date.valueOf() !== dayjs(x.endTime).startOf("day").valueOf()) + continue; + } + + if (parsed.startDay) { + if (parsed.startDay !== dayjs(x.startTime).get("date")) + continue; + } + + if (parsed.endDay) { + if (parsed.endDay !== dayjs(x.endTime).get("date")) + continue; + } + + if (parsed.startMonth) { + if (parsed.startMonth !== dayjs(x.startTime).get("month") + 1) + continue; + } + + if (parsed.endMonth) { + if (parsed.endMonth !== dayjs(x.endTime).get("month") + 1) + continue; + } + + if (parsed.startYear) { + if (parsed.startYear !== dayjs(x.startTime).get("year")) + continue; + } + + if (parsed.endYear) { + if (parsed.endYear !== dayjs(x.endTime).get("year")) + continue; + } + + filtered.push(complete) + } + } + + _frontingEntries.value = filtered; + }, { immediate: true }); + + return _frontingEntries; +} + +export function getFilteredBoardMessages(search: Ref, boardMessages: ShallowRef) { + const _boardMessages = shallowRef([]); + + watch([ + search, + boardMessages + ], async () => { + const filtered: BoardMessageComplete[] = []; + + if (!search.value.length) { + for (const x of boardMessages.value) + filtered.push(await toBoardMessageComplete(x)) + } else { + const parsed = parseBoardMessageFilterQuery(search.value); + + for (const x of boardMessages.value) { + const complete = await toBoardMessageComplete(x); + + if ( + ![ + x.title.toLowerCase().split(" "), + complete.member.name.toLowerCase().split(" ") + ].flat().find(x => x.startsWith(parsed.query.toLowerCase())) + ) + continue; + + if (parsed.member) { + if (x.member !== parsed.member) + continue; + } + + if (parsed.dateString) { + const date = dayjs(parsed.dateString).startOf("day"); + if (date.valueOf() !== dayjs(x.date).startOf("day").valueOf()) + continue; + } + + if (parsed.day) { + if (parsed.day !== dayjs(x.date).get("date")) + continue; + } + + if (parsed.month) { + if (parsed.month !== dayjs(x.date).get("month") + 1) + continue; + } + + if (parsed.year) { + if (parsed.year !== dayjs(x.date).get("year")) + continue; + } + + filtered.push(complete) + } + } + + _boardMessages.value = filtered; + }, { immediate: true }); + + return _boardMessages; +} diff --git a/src/lib/theme/components/list.css b/src/lib/theme/components/list.css index d0f29ee..9278ac2 100644 --- a/src/lib/theme/components/list.css +++ b/src/lib/theme/components/list.css @@ -7,3 +7,11 @@ .md.md3 ion-list ion-item ion-icon[slot="start"] { margin-inline-end: 24px; } + +.md.md3 ion-item { + --inner-border-width: 0 0 0 0; +} + +.md.md3 ion-item[lines="lines"] { + --inner-border-width: unset; +} \ No newline at end of file diff --git a/src/lib/util/filterQuery.ts b/src/lib/util/filterQuery.ts index 1cb742f..59d05c7 100644 --- a/src/lib/util/filterQuery.ts +++ b/src/lib/util/filterQuery.ts @@ -1,5 +1,6 @@ import dayjs from "dayjs"; import { getTagFromNameHashtag } from "../db/entities/tags"; +import { UUID } from "../db/types"; export type MemberFilterQuery = { query: string, @@ -95,11 +96,16 @@ export async function parseMemberFilterQuery(search: string): Promise - + - + - + - +