Skip to content

Commit

Permalink
Merge pull request #1254 from pateljannat/jobs-page-responsive
Browse files Browse the repository at this point in the history
fix: improved jobs page ui
  • Loading branch information
pateljannat authored Jan 17, 2025
2 parents 28a687f + cd4f2b1 commit 215ae94
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 121 deletions.
84 changes: 27 additions & 57 deletions frontend/src/components/JobCard.vue
Original file line number Diff line number Diff line change
@@ -1,71 +1,41 @@
<template>
<div class="flex rounded p-1 lg:px-2 lg:py-4 hover:bg-gray-100">
<div class="flex w-3/5 md:w-2/5">
<img
:src="job.company_logo"
class="w-12 h-12 rounded-lg object-contain mr-4"
:alt="job.company_name"
/>
<div>
<div class="font-medium mb-1">
<div class="flex space-x-4 border rounded-md p-2">
<Avatar :image="job.company_logo" :label="job.job_title" size="2xl" />
<div class="flex flex-col space-y-2 flex-1">
<div class="flex items-center justify-between">
<span class="font-semibold">
{{ job.job_title }}
</div>
<div class="text-gray-700">
{{ job.company_name }}
</div>
</span>
</div>
</div>
<div class="flex justify-end w-1/5 text-gray-700">
{{ job.location.replace(',', '').split(' ')[0] }}
</div>
<div
class="flex justify-end w-1/5 text-gray-700 text-right hidden md:block"
>
{{ job.type }}
</div>
<div class="flex justify-end w-1/5 text-sm text-gray-700 text-right">
{{ dayjs(job.creation).format('DD MMM YYYY') }}
</div>
</div>
<!-- <div class="flex flex-col shadow rounded-md p-4 h-full">
<div class="flex justify-between">
<div>
<div class="text-xl font-semibold mb-2">
{{ job.job_title }}
</div>
<div>
{{ __("posted by") }}
<span class="font-medium">
{{ job.company_name }}
</span>
</div>
<div class="flex items-center space-x-2">
<Building2 class="w-4 h-4 stroke-1.5 text-gray-600" />
<span>
{{ job.company_name }}
</span>
</div>
<img
:src="job.company_logo"
class="w-12 h-12 rounded-lg object-contain"
/>
</div>
<div class="flex justify-between mt-8">
<div class="flex items-center">
<Badge :label="job.type" theme="green" size="lg" class="mr-4"/>
<Badge :label="job.location" theme="gray" size="lg">
<template #prefix>
<MapPin class="h-4 w-4 stroke-1.5" />
</template>
</Badge>
<div class="flex items-center space-x-2">
<MapPin class="w-4 h-4 stroke-1.5 text-gray-600" />
<span>
{{ job.location }}
</span>
</div>
<div>
<span class="font-medium">
{{ dayjs(job.creation).format('DD MMM YYYY') }}
<div class="flex items-center space-x-2">
<Shapes class="w-4 h-4 stroke-1.5 text-gray-600" />
<span>
{{ job.type }}
</span>
</div>
<div class="flex items-center space-x-2">
<Calendar class="w-4 h-4 stroke-1.5 text-gray-600" />
<span> {{ __('posted') }} {{ dayjs(job.creation).fromNow() }} </span>
</div>
</div>
</div> -->
</div>
</template>
<script setup>
import { MapPin } from 'lucide-vue-next'
import { Badge } from 'frappe-ui'
import { Building2, Calendar, MapPin, Shapes } from 'lucide-vue-next'
import { inject } from 'vue'
import { Avatar } from 'frappe-ui'
const dayjs = inject('$dayjs')
const props = defineProps({
Expand Down
166 changes: 102 additions & 64 deletions frontend/src/pages/Jobs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,47 +7,63 @@
class="h-7"
:items="[{ label: __('Jobs'), route: { name: 'Jobs' } }]"
/>
<div class="flex space-x-2">
<div class="w-40 md:w-44">
<FormControl
v-model="jobType"
type="select"
:options="jobTypes"
:placeholder="__('Type')"
/>
</div>
<div class="w-28 md:w-36">
<FormControl type="text" placeholder="Search" v-model="searchQuery">
<template #prefix>
<Search class="w-4 h-4 stroke-1.5 text-gray-600" name="search" />
</template>
</FormControl>
</div>
<router-link
v-if="user.data?.name"
:to="{
name: 'JobCreation',
params: {
jobName: 'new',
},
}"
>
<Button variant="solid">
<template #prefix>
<Plus class="h-4 w-4" />
</template>
{{ __('New Job') }}
</Button>
</router-link>
</div>
<router-link
v-if="user.data?.name"
:to="{
name: 'JobCreation',
params: {
jobName: 'new',
},
}"
>
<Button variant="solid">
<template #prefix>
<Plus class="h-4 w-4" />
</template>
{{ __('New Job') }}
</Button>
</router-link>
</header>
<div v-if="jobsList?.length">
<div>
<div class="lg:w-3/4 mx-auto p-5">
<div class="text-xl font-semibold mb-5">
{{ __('Find the perfect job for you') }}
<div
class="flex flex-col lg:flex-row space-y-4 lg:space-y-0 lg:items-center justify-between mb-5"
>
<div class="text-xl font-semibold">
{{ __('Find the perfect job for you') }}
</div>
<div class="grid grid-cols-2 gap-2">
<FormControl
type="text"
:placeholder="__('Search')"
v-model="searchQuery"
class="min-w-40 lg:min-w-0 lg:w-32 xl:w-40"
@input="updateJobs"
>
<template #prefix>
<Search
class="w-4 h-4 stroke-1.5 text-gray-600"
name="search"
/>
</template>
</FormControl>
<FormControl
v-model="jobType"
type="select"
:options="jobTypes"
class="min-w-40 lg:min-w-0 lg:w-32 xl:w-40"
:placeholder="__('Type')"
@change="updateJobs"
/>
</div>
</div>
<div v-for="job in jobsList" class="divide-y">

<div
v-if="jobs.data?.length"
class="grid grid-cols-1 lg:grid-cols-2 gap-5"
>
<router-link
v-for="job in jobs.data"
:to="{
name: 'JobDetail',
params: { job: job.name },
Expand All @@ -57,15 +73,15 @@
<JobCard :job="job" />
</router-link>
</div>
<div v-else class="text-gray-700 italic p-5 w-fit mx-auto">
{{ __('No jobs posted') }}
</div>
</div>
</div>
<div v-else class="text-gray-700 italic p-5 w-fit mx-auto">
{{ __('No jobs posted') }}
</div>
</div>
</template>
<script setup>
import { Button, Breadcrumbs, createResource, FormControl } from 'frappe-ui'
import { Button, Breadcrumbs, createListResource, FormControl } from 'frappe-ui'
import { Plus, Search } from 'lucide-vue-next'
import { inject, computed, ref, onMounted } from 'vue'
import JobCard from '@/components/JobCard.vue'
Expand All @@ -74,43 +90,59 @@ import { updateDocumentTitle } from '@/utils'
const user = inject('$user')
const jobType = ref(null)
const searchQuery = ref('')
const filters = ref({})
const orFilters = ref({})
onMounted(() => {
let queries = new URLSearchParams(location.search)
if (queries.has('type')) {
jobType.value = queries.get('type')
}
updateJobs()
})
const jobs = createResource({
url: 'lms.lms.api.get_job_opportunities',
cache: ['jobs'],
auto: true,
const jobs = createListResource({
doctype: 'Job Opportunity',
fields: [
'name',
'job_title',
'company_name',
'company_logo',
'location',
'type',
'creation',
],
start: 0,
pageLength: 20,
cache: ['jobOpportunities'],
})
const pageMeta = computed(() => {
return {
title: 'Jobs',
description: 'An open job board for the community',
}
})
const updateJobs = () => {
updateFilters()
jobs.update({
filters: filters.value,
orFilters: orFilters.value,
})
jobs.reload()
}
const jobsList = computed(() => {
let jobData = jobs.data
if (jobType.value && jobType.value != '') {
jobData = jobData.filter((job) => job.type == jobType.value)
const updateFilters = () => {
if (jobType.value) {
filters.value.type = jobType.value
} else {
delete filters.value.type
}
if (searchQuery.value) {
let query = searchQuery.value.toLowerCase()
jobData = jobData.filter(
(job) =>
job.job_title.toLowerCase().includes(query) ||
job.company_name.toLowerCase().includes(query) ||
job.location.toLowerCase().includes(query)
)
orFilters.value = {
job_title: ['like', `%${searchQuery.value}%`],
company_name: ['like', `%${searchQuery.value}%`],
location: ['like', `%${searchQuery.value}%`],
}
} else {
orFilters.value = {}
}
return jobData
})
}
const jobTypes = computed(() => {
return [
Expand All @@ -121,6 +153,12 @@ const jobTypes = computed(() => {
{ label: __('Freelance'), value: 'Freelance' },
]
})
const pageMeta = computed(() => {
return {
title: 'Jobs',
description: 'An open job board for the community',
}
})
updateDocumentTitle(pageMeta)
</script>

0 comments on commit 215ae94

Please sign in to comment.