From 820ea7e2a4f1ab7d4c7a88ce087f8fda4398e5a0 Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Thu, 11 Dec 2025 14:56:28 +0530 Subject: [PATCH] feat: search page functionality --- .../CommandPalette/CommandPalette.vue | 2 +- .../src/components/Sidebar/AppSidebar.vue | 9 +- frontend/src/pages/Search/Search.vue | 107 ++++++++++++------ lms/command_palette.py | 21 +++- 4 files changed, 98 insertions(+), 41 deletions(-) diff --git a/frontend/src/components/CommandPalette/CommandPalette.vue b/frontend/src/components/CommandPalette/CommandPalette.vue index 27e6eb4b..6fd6a02d 100644 --- a/frontend/src/components/CommandPalette/CommandPalette.vue +++ b/frontend/src/components/CommandPalette/CommandPalette.vue @@ -215,7 +215,7 @@ const shortcutForEnter = () => { const navigateTo = (route: string) => { show.value = false query.value = '' - router.push({ name: route.name, params: route.params, query: route.query }) + router.replace({ name: route.name, params: route.params, query: route.query }) } const jumpToOptions = ref([ diff --git a/frontend/src/components/Sidebar/AppSidebar.vue b/frontend/src/components/Sidebar/AppSidebar.vue index 39607382..b51e72a5 100644 --- a/frontend/src/components/Sidebar/AppSidebar.vue +++ b/frontend/src/components/Sidebar/AppSidebar.vue @@ -240,7 +240,14 @@ const showPageModal = ref(false) const isModerator = ref(false) const isInstructor = ref(false) const pageToEdit = ref(null) -const { settings, sidebarSettings, activeTab, isSettingsOpen } = useSettings() +const { + settings, + sidebarSettings, + activeTab, + isSettingsOpen, + isCommandPaletteOpen, +} = useSettings() +const settingsStore = useSettings() const showOnboarding = ref(false) const showIntermediateModal = ref(false) const currentStep = ref({}) diff --git a/frontend/src/pages/Search/Search.vue b/frontend/src/pages/Search/Search.vue index d04c947b..c0d9a3ff 100644 --- a/frontend/src/pages/Search/Search.vue +++ b/frontend/src/pages/Search/Search.vue @@ -4,7 +4,7 @@ > -
+
-
+
{{ searchResults.length }} {{ searchResults.length === 1 ? __('match') : __('matches') }}
+
+ {{ __('Press enter to search') }} +
+
+ {{ __('No results found') }} +
-
+
- - - -
-
-
-
- {{ result.doctype == 'LMS Course' ? 'Course' : 'Batch' }} -
-
- {{ - dayjs(result.published_on || result.start_date).format( - 'DD MMM YYYY' - ) - }} +
+ + + +
+
+
+
+ {{ result.doctype == 'LMS Course' ? 'Course' : 'Batch' }} +
+
+ {{ + dayjs(result.published_on || result.start_date).format( + 'DD MMM YYYY' + ) + }} +
+
-
@@ -85,23 +103,23 @@ import { Tooltip, usePageMeta, } from 'frappe-ui' -import { inject, onMounted, ref } from 'vue' +import { inject, onMounted, ref, watch } from 'vue' import { Search, X } from 'lucide-vue-next' import { sessionStore } from '@/stores/session' -import { useRouter } from 'vue-router' +import { useRouter, useRoute } from 'vue-router' const query = ref('') const searchInput = ref(null) -const newSearch = ref(false) const searchResults = ref>([]) const { brand } = sessionStore() const router = useRouter() +const route = useRoute() +const queryChanged = ref(false) const dayjs = inject('$dayjs') onMounted(() => { if (router.currentRoute.value.query.q) { query.value = router.currentRoute.value.query.q as string - searchInput.value.el.focus() submit() } }) @@ -130,12 +148,12 @@ const search = createResource({ const generateSearchResults = () => { searchResults.value = [] if (search.data) { + queryChanged.value = false search.data.forEach((group: any) => { group.items.forEach((item: any) => { searchResults.value.push(item) }) }) - // sort Search results by item.score descending searchResults.value.sort((a, b) => b.score - a.score) } } @@ -158,9 +176,28 @@ const navigate = (result: any) => { } } +watch(query, () => { + if (query.value && query.value != search.params?.query) { + queryChanged.value = true + } else if (!query.value) { + queryChanged.value = false + searchResults.value = [] + } +}) + +watch( + () => route.query.q, + (newQ) => { + if (newQ && newQ !== query.value) { + query.value = newQ as string + submit() + } + } +) + const clearSearch = () => { query.value = '' - searchInput.value?.focus() + updateQuery('') } usePageMeta(() => { diff --git a/lms/command_palette.py b/lms/command_palette.py index 8b7b4a87..fed94b3e 100644 --- a/lms/command_palette.py +++ b/lms/command_palette.py @@ -23,10 +23,10 @@ def prepare_search_results(result): for r in result["results"]: doctype = r["doctype"] if doctype == "LMS Course" and can_access_course(r, roles): - r["author_info"] = get_author_info(r.get("author")) + r["instructors_info"] = get_instructor_info(doctype, r) groups.setdefault("Courses", []).append(r) elif doctype == "LMS Batch" and can_access_batch(r, roles): - r["author_info"] = get_author_info(r.get("author")) + r["instructors_info"] = get_instructor_info(doctype, r) groups.setdefault("Batches", []).append(r) out = [] @@ -60,5 +60,18 @@ def can_create_batch(roles): return "Batch Evaluator" in roles or "Moderator" in roles -def get_author_info(owner): - return frappe.db.get_value("User", owner, ["full_name", "user_image", "username", "email"], as_dict=True) +def get_instructor_info(doctype, record): + instructors = frappe.get_all( + "Course Instructor", filters={"parenttype": doctype, "parent": record.get("name")}, pluck="instructor" + ) + + instructor = record.get("author") + if len(instructors): + instructor = instructors[0] + + return frappe.db.get_value( + "User", + instructor, + ["full_name", "email", "user_image", "username"], + as_dict=True, + )