Skip to content

Enhance job search with sorting, pagination, and UI improvements#5

Merged
khaledfahmy123 merged 3 commits into
mainfrom
feat/filters-search
May 30, 2026
Merged

Enhance job search with sorting, pagination, and UI improvements#5
khaledfahmy123 merged 3 commits into
mainfrom
feat/filters-search

Conversation

@mohamedhamdy746

Copy link
Copy Markdown
Contributor

This pull request introduces a comprehensive search and filtering system for job listings, enabling candidates to filter jobs by multiple criteria (such as location, work type, experience level, category, and date) and have their selections reflected in the URL for shareable/searchable links. It adds a Pinia store for managing search state, new composables for syncing state with the router, API enhancements to support filtering, and new UI components for filter display and interaction. The home page search now routes directly to the filtered jobs page.

Search and Filtering System:

  • Added a new Pinia store (src/stores/search.ts) to manage job search filters, query, sorting, and results, including loading job listings and filter options from the API.
  • Implemented a composable (src/composables/useJobSearch.ts) that synchronizes the search/filter state with URL query parameters, supporting shareable and restorable filter states.
  • Enhanced the jobs API (src/api/jobs.ts) to support advanced filtering, including new filter types, a locations endpoint, and paginated responses.

UI Components for Filters:

  • Added ActiveFilters.vue to display current filters as removable chips and provide a "Clear all" option.
  • Added FilterSection.vue for collapsible filter groups in the sidebar, improving filter organization and UX.

Routing and Home Page Integration:

  • Updated the router to clarify candidate and employer job-related routes and meta fields, and to support the new filterable jobs view. [1] [2]
  • Modified the home page (HomeView.vue) to include a search form that routes users to the filterable jobs page with the selected query and location as URL parameters. [1] [2] [3]

Copilot AI review requested due to automatic review settings May 30, 2026 04:32

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a URL-synchronized job search/filtering experience for candidate job listings, with API helpers, Pinia search state, sidebar filters, active filter chips, and home page search routing.

Changes:

  • Added search state management and URL sync for filters, sorting, and job results.
  • Reworked candidate jobs UI with filters, active chips, sorting, pagination, and application actions.
  • Added API support types/helpers for filtered job search, categories, and locations.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
src/api/jobs.ts Adds filter types, paginated response typing, search endpoint, and locations endpoint.
src/components/search/ActiveFilters.vue Displays removable active filter chips and clear-all action.
src/components/search/FilterSection.vue Adds a collapsible filter section component.
src/components/search/FilterSidebar.vue Adds advanced keyword/location/work-type/date/category filter controls.
src/composables/useJobSearch.ts Synchronizes search store state with URL query parameters and loads results.
src/router/index.ts Reformats and clarifies candidate/employer job route metadata.
src/stores/search.ts Adds Pinia store for search filters, options, jobs, pagination, and loading state.
src/views/CandidateJobsView.vue Replaces basic candidate jobs list with filterable, paginated search UI.
src/views/HomeView.vue Adds search form state and routes home searches to the jobs page.
src/views/search/JobSearchView.vue Adds a separate job search page implementation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +19 to +30
defineProps<{
title: string;
icon: string;
open: boolean;
}>();

const emit = defineEmits<{
"update:open": [value: boolean];
}>();

function toggle() {
emit("update:open", !open);
Comment on lines +256 to +272
import { ref, computed } from "vue";
import { useRouter } from "vue-router";
import AppLayout from "@/layouts/AppLayout.vue";
import FilterSidebar from "@/components/search/FilterSidebar.vue";
import ActiveFilters from "@/components/search/ActiveFilters.vue";
import { useJobSearch } from "@/composables/useJobSearch";
import { useApplicationsStore } from "@/stores/applications";
import type { JobListing } from "@/api/jobs";

const router = useRouter();
const applicationsStore = useApplicationsStore();
const sidebarOpen = ref(true);
const { store, goToPage } = useJobSearch();

function goToDetail(jobId: number) {
router.push({ name: "job-detail", params: { jobId } });
}
Comment on lines +32 to +43
function initFromUrl() {
const q = route.query;
store.query = (q.q as string) ?? "";
store.sort = q.sort === "deadline" ? "deadline" : "newest";
store.filters.location = (q.location as string) ?? "";
store.filters.work_type = ((q.work_type as string) ??
"") as typeof store.filters.work_type;
store.filters.experience_level = ((q.experience_level as string) ??
"") as typeof store.filters.experience_level;
store.filters.category = (q.category as string) ?? "";
store.filters.date_from = (q.date_from as string) ?? "";
}
Comment on lines +55 to +58
function goToPage(page: number) {
syncToUrl();
store.loadJobs(page);
window.scrollTo({ top: 0, behavior: "smooth" });
Comment on lines +222 to +223
<template v-for="item in visiblePages" :key="item">
<span v-if="item === '...'" class="page-ellipsis">…</span>
Comment on lines +1 to +3
<template>
<AppLayout>
<div class="search-page">
Comment on lines +22 to +31
<label class="filter-label">Keywords</label>
<div class="icon-field">
<span class="material-symbols-outlined icon-field__icon">search</span>
<input
v-model="store.query"
type="text"
placeholder="e.g. Vue, Laravel, React"
class="icon-field__input"
autocomplete="off"
/>
Comment on lines +37 to +49
<label class="filter-label">Location</label>
<div class="icon-field">
<span class="material-symbols-outlined icon-field__icon"
>location_on</span
>
<input
v-model="store.filters.location"
type="text"
placeholder="City, State, or Remote"
class="icon-field__input"
list="loc-suggestions"
autocomplete="off"
/>
Comment on lines +58 to +62
<span
v-if="store.activeFilterCount > 0 || store.query"
class="filter-badge"
>{{ store.activeFilterCount + (store.query ? 1 : 0) }}</span
>
Comment on lines +78 to +82
<span
v-if="store.activeFilterCount > 0 || store.query"
class="filter-badge"
>{{ store.activeFilterCount + (store.query ? 1 : 0) }}</span
>
@khaledfahmy123 khaledfahmy123 merged commit 6fcc4b1 into main May 30, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants