Merge pull request #1464 from pateljannat/issues-96
feat: redesigned job list
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="flex flex-col border-2 hover:bg-surface-gray-2 rounded-md p-4 h-full"
|
class="flex flex-col border hover:border-outline-gray-4 rounded-md p-4 h-full"
|
||||||
style="min-height: 150px"
|
style="min-height: 150px"
|
||||||
>
|
>
|
||||||
<div class="text-lg leading-5 font-semibold mb-2 text-ink-gray-9">
|
<div class="text-lg leading-5 font-semibold mb-2 text-ink-gray-9">
|
||||||
|
|||||||
@@ -1,22 +1,35 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col border rounded-md p-4 h-full">
|
<div
|
||||||
<div class="flex space-x-4 mb-2">
|
class="flex flex-col border rounded-md p-3 h-full hover:border-outline-gray-4"
|
||||||
<img :src="job.company_logo" class="size-8 rounded-full object-contain" />
|
>
|
||||||
<div class="flex flex-col space-y-1 flex-1">
|
<div class="flex space-x-4 mb-4">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex flex-col space-y-2 flex-1">
|
||||||
<span class="text-lg font-semibold text-ink-gray-9">
|
<div class="text-lg font-semibold text-ink-gray-9">
|
||||||
{{ job.job_title }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="text-xs text-ink-gray-5">
|
|
||||||
{{ job.company_name }}
|
{{ job.company_name }}
|
||||||
</div>
|
</div>
|
||||||
|
<span class="font-medium text-ink-gray-7 leading-5">
|
||||||
|
{{ job.job_title }}
|
||||||
|
</span>
|
||||||
|
<div class="flex items-center space-x-1 text-sm text-ink-gray-7">
|
||||||
|
<MapPin class="size-3" />
|
||||||
|
<span>
|
||||||
|
{{ job.location }}{{ job.country ? `, ${job.country}` : '' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="job.applicants"
|
||||||
|
class="flex items-center space-x-1 text-sm text-ink-gray-7"
|
||||||
|
>
|
||||||
|
<User class="size-3" />
|
||||||
|
<span>
|
||||||
|
{{ job.applicants }}
|
||||||
|
{{ job.applicants > 1 ? __('applicants') : __('applicant') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- <img :src="job.company_logo" alt="Company Logo" class="size-8 rounded-full object-contain bg-white" /> -->
|
||||||
</div>
|
</div>
|
||||||
<div class="space-x-4 mt-auto">
|
<div class="space-x-2 mt-auto">
|
||||||
<Badge>
|
|
||||||
{{ job.location }}
|
|
||||||
</Badge>
|
|
||||||
<Badge>
|
<Badge>
|
||||||
{{ job.type }}
|
{{ job.type }}
|
||||||
</Badge>
|
</Badge>
|
||||||
@@ -24,11 +37,16 @@
|
|||||||
{{ dayjs(job.creation).fromNow() }}
|
{{ dayjs(job.creation).fromNow() }}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- <div
|
||||||
|
class="description text-ink-gray-9 text-sm"
|
||||||
|
v-html="job.description"
|
||||||
|
></div> -->
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { inject } from 'vue'
|
import { inject } from 'vue'
|
||||||
import { Badge } from 'frappe-ui'
|
import { Badge } from 'frappe-ui'
|
||||||
|
import { MapPin, User } from 'lucide-vue-next'
|
||||||
|
|
||||||
const dayjs = inject('$dayjs')
|
const dayjs = inject('$dayjs')
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -38,3 +56,15 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
<style>
|
||||||
|
.description {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-top: auto;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -16,11 +16,11 @@
|
|||||||
},
|
},
|
||||||
]"
|
]"
|
||||||
/>
|
/>
|
||||||
<div v-if="user.data?.name" class="flex space-x-2">
|
<div v-if="user.data?.name" class="flex items-center space-x-2">
|
||||||
<router-link
|
<router-link
|
||||||
v-if="user.data.name == job.data?.owner"
|
v-if="user.data.name == job.data?.owner"
|
||||||
:to="{
|
:to="{
|
||||||
name: 'JobCreation',
|
name: 'JobForm',
|
||||||
params: { jobName: job.data?.name },
|
params: { jobName: job.data?.name },
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
@@ -47,6 +47,12 @@
|
|||||||
</template>
|
</template>
|
||||||
{{ __('Apply') }}
|
{{ __('Apply') }}
|
||||||
</Button>
|
</Button>
|
||||||
|
<Badge v-else variant="subtle" theme="green" size="lg">
|
||||||
|
<template #prefix>
|
||||||
|
<Check class="h-4 w-4" />
|
||||||
|
</template>
|
||||||
|
{{ __('You have applied') }}
|
||||||
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<Button @click="redirectToLogin(job.data?.name)">
|
<Button @click="redirectToLogin(job.data?.name)">
|
||||||
@@ -56,13 +62,13 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div v-if="job.data" class="max-w-3xl mx-auto">
|
<div v-if="job.data" class="max-w-3xl mx-auto pt-5">
|
||||||
<div class="p-4">
|
<div class="p-4">
|
||||||
<div class="space-y-5 mb-10">
|
<div class="space-y-5 mb-10">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<img
|
<img
|
||||||
:src="job.data.company_logo"
|
:src="job.data.company_logo"
|
||||||
class="w-16 h-16 rounded-lg object-contain cursor-pointer mr-4"
|
class="size-10 rounded-lg object-contain cursor-pointer mr-4"
|
||||||
:alt="job.data.company_name"
|
:alt="job.data.company_name"
|
||||||
@click="redirectToWebsite(job.data.company_website)"
|
@click="redirectToWebsite(job.data.company_website)"
|
||||||
/>
|
/>
|
||||||
@@ -75,7 +81,7 @@
|
|||||||
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-x-10 gap-y-5 md:gap-y-5"
|
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-x-10 gap-y-5 md:gap-y-5"
|
||||||
>
|
>
|
||||||
<div class="flex items-center space-x-4">
|
<div class="flex items-center space-x-4">
|
||||||
<Building2 class="h-4 w-4 text-ink-green-2" />
|
<Building2 class="size-4 stroke-1.5 text-ink-gray-7" />
|
||||||
<div class="flex flex-col space-y-1 text-ink-gray-7">
|
<div class="flex flex-col space-y-1 text-ink-gray-7">
|
||||||
<span class="text-xs text-ink-gray-5 font-medium uppercase">
|
<span class="text-xs text-ink-gray-5 font-medium uppercase">
|
||||||
{{ __('Organisation') }}
|
{{ __('Organisation') }}
|
||||||
@@ -86,9 +92,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center space-x-4">
|
<div class="flex items-center space-x-4">
|
||||||
<MapPin class="size-4 text-ink-red-3" />
|
<MapPin class="size-4 stroke-1.5 text-ink-gray-7" />
|
||||||
<div class="flex flex-col space-y-1 text-ink-gray-7">
|
<div class="flex flex-col space-y-1 text-ink-gray-7">
|
||||||
<span class="text-xs font-medium uppercase">
|
<span class="text-xs text-ink-gray-5 font-medium uppercase">
|
||||||
{{ __('Location') }}
|
{{ __('Location') }}
|
||||||
</span>
|
</span>
|
||||||
<span class="text-sm font-semibold">
|
<span class="text-sm font-semibold">
|
||||||
@@ -97,9 +103,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center space-x-4">
|
<div class="flex items-center space-x-4">
|
||||||
<ClipboardType class="h-4 w-4 text-yellow-500" />
|
<ClipboardType class="size-4 stroke-1.5 text-ink-gray-7" />
|
||||||
<div class="flex flex-col space-y-1 text-ink-gray-7">
|
<div class="flex flex-col space-y-1 text-ink-gray-7">
|
||||||
<span class="text-xs font-medium uppercase">
|
<span class="text-xs text-ink-gray-5 font-medium uppercase">
|
||||||
{{ __('Category') }}
|
{{ __('Category') }}
|
||||||
</span>
|
</span>
|
||||||
<span class="text-sm font-semibold">
|
<span class="text-sm font-semibold">
|
||||||
@@ -108,9 +114,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center space-x-4">
|
<div class="flex items-center space-x-4">
|
||||||
<CalendarDays class="h-4 w-4 text-ink-blue-2" />
|
<CalendarDays class="size-4 stroke-1.5 text-ink-gray-7" />
|
||||||
<div class="flex flex-col space-y-1 text-ink-gray-7">
|
<div class="flex flex-col space-y-1 text-ink-gray-7">
|
||||||
<span class="text-xs font-medium uppercase">
|
<span class="text-xs text-ink-gray-5 font-medium uppercase">
|
||||||
{{ __('Posted on') }}
|
{{ __('Posted on') }}
|
||||||
</span>
|
</span>
|
||||||
<span class="text-sm font-semibold">
|
<span class="text-sm font-semibold">
|
||||||
@@ -122,9 +128,9 @@
|
|||||||
v-if="applicationCount.data"
|
v-if="applicationCount.data"
|
||||||
class="flex items-center space-x-4"
|
class="flex items-center space-x-4"
|
||||||
>
|
>
|
||||||
<SquareUserRound class="h-4 w-4 text-purple-500" />
|
<SquareUserRound class="size-4 stroke-1.5 text-ink-gray-7" />
|
||||||
<div class="flex flex-col space-y-1 text-ink-gray-7">
|
<div class="flex flex-col space-y-1 text-ink-gray-7">
|
||||||
<span class="text-xs font-medium uppercase">
|
<span class="text-xs text-ink-gray-5 font-medium uppercase">
|
||||||
{{ __('Applications Received') }}
|
{{ __('Applications Received') }}
|
||||||
</span>
|
</span>
|
||||||
<span class="text-sm font-semibold">
|
<span class="text-sm font-semibold">
|
||||||
@@ -149,12 +155,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Button, Breadcrumbs, createResource, usePageMeta } from 'frappe-ui'
|
import {
|
||||||
|
Badge,
|
||||||
|
Button,
|
||||||
|
Breadcrumbs,
|
||||||
|
createResource,
|
||||||
|
usePageMeta,
|
||||||
|
} from 'frappe-ui'
|
||||||
import { inject, ref } from 'vue'
|
import { inject, ref } from 'vue'
|
||||||
import { sessionStore } from '../stores/session'
|
import { sessionStore } from '../stores/session'
|
||||||
import JobApplicationModal from '@/components/Modals/JobApplicationModal.vue'
|
import JobApplicationModal from '@/components/Modals/JobApplicationModal.vue'
|
||||||
import {
|
import {
|
||||||
MapPin,
|
MapPin,
|
||||||
|
Check,
|
||||||
SendHorizonal,
|
SendHorizonal,
|
||||||
Pencil,
|
Pencil,
|
||||||
Building2,
|
Building2,
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<div class="text-lg font-semibold mb-4">
|
<div class="text-lg font-semibold mb-4">
|
||||||
{{ __('Job Details') }}
|
{{ __('Job Details') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-2 gap-4">
|
<div class="grid grid-cols-2 gap-5">
|
||||||
<div>
|
<div>
|
||||||
<FormControl
|
<FormControl
|
||||||
v-model="job.job_title"
|
v-model="job.job_title"
|
||||||
@@ -45,25 +45,12 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-4">
|
|
||||||
<label class="block text-ink-gray-5 text-xs mb-1">
|
|
||||||
{{ __('Description') }}
|
|
||||||
<span class="text-ink-red-3">*</span>
|
|
||||||
</label>
|
|
||||||
<TextEditor
|
|
||||||
:content="job.description"
|
|
||||||
@change="(val) => (job.description = val)"
|
|
||||||
:editable="true"
|
|
||||||
:fixedMenu="true"
|
|
||||||
editorClass="prose-sm max-w-none border-b border-x bg-surface-gray-2 rounded-b-md py-1 px-2 min-h-[7rem] mb-4"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="container mb-4 pb-4">
|
<div class="container border-b mb-4 pb-4">
|
||||||
<div class="text-lg font-semibold mb-4">
|
<div class="text-lg font-semibold mb-4">
|
||||||
{{ __('Company Details') }}
|
{{ __('Company Details') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-2 gap-4">
|
<div class="grid grid-cols-2 gap-5">
|
||||||
<div>
|
<div>
|
||||||
<FormControl
|
<FormControl
|
||||||
v-model="job.company_name"
|
v-model="job.company_name"
|
||||||
@@ -128,6 +115,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="container mt-4">
|
||||||
|
<label class="block text-ink-gray-5 text-xs mb-1">
|
||||||
|
{{ __('Description') }}
|
||||||
|
<span class="text-ink-red-3">*</span>
|
||||||
|
</label>
|
||||||
|
<TextEditor
|
||||||
|
:content="job.description"
|
||||||
|
@change="(val) => (job.description = val)"
|
||||||
|
:editable="true"
|
||||||
|
:fixedMenu="true"
|
||||||
|
editorClass="prose-sm max-w-none border-b border-x bg-surface-gray-2 rounded-b-md py-1 px-2 min-h-[7rem] mb-4"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -317,7 +317,7 @@ const breadcrumbs = computed(() => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: props.jobName == 'new' ? 'New Job' : 'Edit Job',
|
label: props.jobName == 'new' ? 'New Job' : 'Edit Job',
|
||||||
route: { name: 'JobCreation' },
|
route: { name: 'JobForm' },
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
return crumbs
|
return crumbs
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
<router-link
|
<router-link
|
||||||
v-if="user.data?.name"
|
v-if="user.data?.name"
|
||||||
:to="{
|
:to="{
|
||||||
name: 'JobCreation',
|
name: 'JobForm',
|
||||||
params: {
|
params: {
|
||||||
jobName: 'new',
|
jobName: 'new',
|
||||||
},
|
},
|
||||||
@@ -25,40 +25,48 @@
|
|||||||
</router-link>
|
</router-link>
|
||||||
</header>
|
</header>
|
||||||
<div>
|
<div>
|
||||||
<div v-if="jobs.data?.length" class="p-5">
|
<div
|
||||||
|
class="flex flex-col lg:flex-row space-y-4 lg:space-y-0 lg:items-center justify-between w-full md:w-4/5 mx-auto p-5"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
class="flex flex-col lg:flex-row space-y-4 lg:space-y-0 lg:items-center justify-between mb-5"
|
v-if="jobCount"
|
||||||
|
class="text-xl font-semibold text-ink-gray-7 mb-4 md:mb-0"
|
||||||
>
|
>
|
||||||
<div class="text-xl text-ink-gray-9 font-semibold">
|
{{ __('{0} Open Jobs').format(jobCount) }}
|
||||||
{{ __('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-ink-gray-5"
|
|
||||||
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>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-2">
|
||||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-5">
|
<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-ink-gray-5"
|
||||||
|
name="search"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</FormControl>
|
||||||
|
<Link
|
||||||
|
doctype="Country"
|
||||||
|
v-model="country"
|
||||||
|
:placeholder="__('Country')"
|
||||||
|
class="min-w-40 lg:min-w-0 lg:w-32 xl:w-40"
|
||||||
|
/>
|
||||||
|
<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-if="jobs.data?.length" class="w-full md:w-4/5 mx-auto p-5 pt-0">
|
||||||
|
<div class="grid grid-cols-1 lg:grid-cols-3 gap-4">
|
||||||
<router-link
|
<router-link
|
||||||
v-for="job in jobs.data"
|
v-for="job in jobs.data"
|
||||||
:to="{
|
:to="{
|
||||||
@@ -73,18 +81,17 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
class="flex flex-col items-center justify-center text-sm text-ink-gray-5 mt-48"
|
class="flex flex-col items-center justify-center text-sm text-ink-gray-5 mt-56"
|
||||||
>
|
>
|
||||||
<Laptop class="size-10 mx-auto stroke-1 text-ink-gray-4" />
|
<Laptop class="size-10 mx-auto stroke-1 text-ink-gray-4" />
|
||||||
<div class="text-lg font-medium mb-1">
|
<div class="text-lg font-medium mb-1">
|
||||||
{{ __('No jobs found') }}
|
{{ __('No jobs found') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="leading-5 w-2/5 text-center">
|
<div class="leading-5 w-2/5 text-center">
|
||||||
{{
|
{{ __('There are no jobs available at the moment.') }}
|
||||||
__(
|
</div>
|
||||||
'There are no jobs available at the moment. Open a job opportunity or check here again later.'
|
<div class="leading-5 w-1/5 text-center">
|
||||||
)
|
{{ __('Post a new job or check again later.') }}
|
||||||
}}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -94,21 +101,25 @@
|
|||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Breadcrumbs,
|
Breadcrumbs,
|
||||||
|
call,
|
||||||
createResource,
|
createResource,
|
||||||
FormControl,
|
FormControl,
|
||||||
usePageMeta,
|
usePageMeta,
|
||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
import { Laptop, Plus, Search } from 'lucide-vue-next'
|
import { Laptop, Plus, Search } from 'lucide-vue-next'
|
||||||
import { sessionStore } from '../stores/session'
|
import { sessionStore } from '../stores/session'
|
||||||
import { inject, computed, ref, onMounted } from 'vue'
|
import { inject, computed, ref, onMounted, watch } from 'vue'
|
||||||
import JobCard from '@/components/JobCard.vue'
|
import JobCard from '@/components/JobCard.vue'
|
||||||
|
import Link from '@/components/Controls/Link.vue'
|
||||||
|
|
||||||
const user = inject('$user')
|
const user = inject('$user')
|
||||||
const jobType = ref(null)
|
const jobType = ref(null)
|
||||||
const { brand } = sessionStore()
|
const { brand } = sessionStore()
|
||||||
const searchQuery = ref('')
|
const searchQuery = ref('')
|
||||||
|
const country = ref(null)
|
||||||
const filters = ref({})
|
const filters = ref({})
|
||||||
const orFilters = ref({})
|
const orFilters = ref({})
|
||||||
|
const jobCount = ref(0)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
let queries = new URLSearchParams(location.search)
|
let queries = new URLSearchParams(location.search)
|
||||||
@@ -116,6 +127,7 @@ onMounted(() => {
|
|||||||
jobType.value = queries.get('type')
|
jobType.value = queries.get('type')
|
||||||
}
|
}
|
||||||
updateJobs()
|
updateJobs()
|
||||||
|
getJobCount()
|
||||||
})
|
})
|
||||||
|
|
||||||
const jobs = createResource({
|
const jobs = createResource({
|
||||||
@@ -153,8 +165,30 @@ const updateFilters = () => {
|
|||||||
} else {
|
} else {
|
||||||
orFilters.value = {}
|
orFilters.value = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (country.value) {
|
||||||
|
filters.value.country = country.value
|
||||||
|
} else {
|
||||||
|
delete filters.value.country
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getJobCount = () => {
|
||||||
|
call('frappe.client.get_count', {
|
||||||
|
doctype: 'Job Opportunity',
|
||||||
|
filters: {
|
||||||
|
status: 'Open',
|
||||||
|
disabled: 0,
|
||||||
|
},
|
||||||
|
}).then((data) => {
|
||||||
|
jobCount.value = data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(country, (val) => {
|
||||||
|
updateJobs()
|
||||||
|
})
|
||||||
|
|
||||||
const jobTypes = computed(() => {
|
const jobTypes = computed(() => {
|
||||||
return [
|
return [
|
||||||
'',
|
'',
|
||||||
|
|||||||
@@ -134,8 +134,8 @@ const routes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/job-opening/:jobName/edit',
|
path: '/job-opening/:jobName/edit',
|
||||||
name: 'JobCreation',
|
name: 'JobForm',
|
||||||
component: () => import('@/pages/JobCreation.vue'),
|
component: () => import('@/pages/JobForm.vue'),
|
||||||
props: true,
|
props: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,18 +9,19 @@
|
|||||||
"field_order": [
|
"field_order": [
|
||||||
"job_title",
|
"job_title",
|
||||||
"location",
|
"location",
|
||||||
"disabled",
|
"country",
|
||||||
"column_break_5",
|
"column_break_5",
|
||||||
"type",
|
"type",
|
||||||
"status",
|
"status",
|
||||||
|
"disabled",
|
||||||
"section_break_6",
|
"section_break_6",
|
||||||
"description",
|
|
||||||
"company_details_section",
|
|
||||||
"company_name",
|
"company_name",
|
||||||
"company_website",
|
"company_website",
|
||||||
"column_break_11",
|
"column_break_phkm",
|
||||||
"company_logo",
|
"company_logo",
|
||||||
"company_email_address"
|
"company_email_address",
|
||||||
|
"company_details_section",
|
||||||
|
"description"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -36,7 +37,7 @@
|
|||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Location",
|
"label": "City",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -62,7 +63,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "section_break_6",
|
"fieldname": "section_break_6",
|
||||||
"fieldtype": "Section Break"
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Company Details"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "description",
|
"fieldname": "description",
|
||||||
@@ -72,8 +74,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "company_details_section",
|
"fieldname": "company_details_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break"
|
||||||
"label": "Company Details"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "company_name",
|
"fieldname": "company_name",
|
||||||
@@ -89,10 +90,6 @@
|
|||||||
"label": "Company Website",
|
"label": "Company Website",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "column_break_11",
|
|
||||||
"fieldtype": "Column Break"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "company_logo",
|
"fieldname": "company_logo",
|
||||||
"fieldtype": "Attach Image",
|
"fieldtype": "Attach Image",
|
||||||
@@ -111,13 +108,30 @@
|
|||||||
"label": "Company Email Address",
|
"label": "Company Email Address",
|
||||||
"options": "Email",
|
"options": "Email",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_phkm",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "country",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Country",
|
||||||
|
"options": "Country",
|
||||||
|
"reqd": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"grid_page_length": 50,
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [
|
||||||
|
{
|
||||||
|
"link_doctype": "LMS Job Application",
|
||||||
|
"link_fieldname": "job"
|
||||||
|
}
|
||||||
|
],
|
||||||
"make_attachments_public": 1,
|
"make_attachments_public": 1,
|
||||||
"modified": "2025-01-17 12:38:57.134919",
|
"modified": "2025-04-24 14:34:35.920242",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "sayali@frappe.io",
|
||||||
"module": "Job",
|
"module": "Job",
|
||||||
"name": "Job Opportunity",
|
"name": "Job Opportunity",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
@@ -157,8 +171,9 @@
|
|||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"row_format": "Dynamic",
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"states": [],
|
"states": [],
|
||||||
"title_field": "job_title"
|
"title_field": "job_title"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -306,14 +306,20 @@ def get_job_opportunities(filters=None, orFilters=None):
|
|||||||
fields=[
|
fields=[
|
||||||
"job_title",
|
"job_title",
|
||||||
"location",
|
"location",
|
||||||
|
"country",
|
||||||
"type",
|
"type",
|
||||||
"company_name",
|
"company_name",
|
||||||
"company_logo",
|
"company_logo",
|
||||||
"name",
|
"name",
|
||||||
"creation",
|
"creation",
|
||||||
|
"description",
|
||||||
],
|
],
|
||||||
order_by="creation desc",
|
order_by="creation desc",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
for job in jobs:
|
||||||
|
job.description = frappe.utils.strip_html_tags(job.description)
|
||||||
|
job.applicants = frappe.db.count("LMS Job Application", {"job": job.name})
|
||||||
return jobs
|
return jobs
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -101,4 +101,5 @@ lms.patches.v2_0.allow_guest_access #05-02-2025
|
|||||||
lms.patches.v2_0.migrate_batch_student_data #10-02-2025
|
lms.patches.v2_0.migrate_batch_student_data #10-02-2025
|
||||||
lms.patches.v2_0.delete_old_enrollment_doctypes
|
lms.patches.v2_0.delete_old_enrollment_doctypes
|
||||||
lms.patches.v2_0.delete_unused_custom_fields
|
lms.patches.v2_0.delete_unused_custom_fields
|
||||||
lms.patches.v2_0.update_certificate_request_status
|
lms.patches.v2_0.update_certificate_request_status
|
||||||
|
lms.patches.v2_0.update_job_city_and_country
|
||||||
28
lms/patches/v2_0/update_job_city_and_country.py
Normal file
28
lms/patches/v2_0/update_job_city_and_country.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import frappe
|
||||||
|
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
jobs = frappe.get_all("Job Opportunity", fields=["name", "location"])
|
||||||
|
|
||||||
|
for job in jobs:
|
||||||
|
if "," in job.location:
|
||||||
|
city, country = job.location.split(",", 1)
|
||||||
|
city = city.strip()
|
||||||
|
country = country.strip()
|
||||||
|
save_country(country, job)
|
||||||
|
frappe.db.set_value("Job Opportunity", job.name, "location", city)
|
||||||
|
else:
|
||||||
|
save_country(job.location, job)
|
||||||
|
|
||||||
|
|
||||||
|
def save_country(country, job):
|
||||||
|
if frappe.db.exists("Country", country):
|
||||||
|
frappe.db.set_value("Job Opportunity", job.name, "country", country)
|
||||||
|
else:
|
||||||
|
country_mapping = {
|
||||||
|
"US": "United States",
|
||||||
|
"USA": "United States",
|
||||||
|
"UAE": "United Arab Emirates",
|
||||||
|
}
|
||||||
|
country = country_mapping.get(country, country)
|
||||||
|
frappe.db.set_value("Job Opportunity", job.name, "country", country)
|
||||||
Reference in New Issue
Block a user