Merge branch 'develop' of https://github.com/frappe/lms into zen-mode

This commit is contained in:
Jannat Patel
2025-04-24 18:42:44 +05:30
11 changed files with 245 additions and 118 deletions

View File

@@ -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">

View File

@@ -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>

View File

@@ -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,

View File

@@ -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

View File

@@ -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 [
'', '',

View File

@@ -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,
}, },
{ {

View File

@@ -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"
} }

View File

@@ -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

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: frappe\n" "Project-Id-Version: frappe\n"
"Report-Msgid-Bugs-To: jannat@frappe.io\n" "Report-Msgid-Bugs-To: jannat@frappe.io\n"
"POT-Creation-Date: 2025-04-18 16:04+0000\n" "POT-Creation-Date: 2025-04-18 16:04+0000\n"
"PO-Revision-Date: 2025-04-21 06:38\n" "PO-Revision-Date: 2025-04-24 08:07\n"
"Last-Translator: jannat@frappe.io\n" "Last-Translator: jannat@frappe.io\n"
"Language-Team: Chinese Simplified\n" "Language-Team: Chinese Simplified\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@@ -465,7 +465,7 @@ msgstr "作业标题"
#: frontend/src/components/Modals/AssignmentForm.vue:112 #: frontend/src/components/Modals/AssignmentForm.vue:112
msgid "Assignment created successfully" msgid "Assignment created successfully"
msgstr "" msgstr "作业创建成功"
#: lms/lms/doctype/lms_assignment_submission/lms_assignment_submission.py:24 #: lms/lms/doctype/lms_assignment_submission/lms_assignment_submission.py:24
msgid "Assignment for Lesson {0} by {1} already exists." msgid "Assignment for Lesson {0} by {1} already exists."
@@ -473,7 +473,7 @@ msgstr "学员{1}的课时{0}作业已存在。"
#: frontend/src/components/Modals/AssignmentForm.vue:129 #: frontend/src/components/Modals/AssignmentForm.vue:129
msgid "Assignment updated successfully" msgid "Assignment updated successfully"
msgstr "" msgstr "作业更新成功"
#. Description of the 'Question' (Small Text) field in DocType 'Course Lesson' #. Description of the 'Question' (Small Text) field in DocType 'Course Lesson'
#: lms/lms/doctype/course_lesson/course_lesson.json #: lms/lms/doctype/course_lesson/course_lesson.json
@@ -831,7 +831,7 @@ msgstr "已认证"
#: frontend/src/pages/CertifiedParticipants.vue:196 #: frontend/src/pages/CertifiedParticipants.vue:196
#: frontend/src/pages/CertifiedParticipants.vue:203 #: frontend/src/pages/CertifiedParticipants.vue:203
msgid "Certified Members" msgid "Certified Members"
msgstr "" msgstr "认证成员"
#. Label of the certified_participants (Check) field in DocType 'LMS Settings' #. Label of the certified_participants (Check) field in DocType 'LMS Settings'
#: lms/lms/doctype/lms_settings/lms_settings.json lms/www/lms.py:290 #: lms/lms/doctype/lms_settings/lms_settings.json lms/www/lms.py:290
@@ -1458,7 +1458,7 @@ msgstr "新建试题"
#: frontend/src/components/Modals/AssignmentForm.vue:7 #: frontend/src/components/Modals/AssignmentForm.vue:7
msgid "Create an Assignment" msgid "Create an Assignment"
msgstr "" msgstr "创建作业"
#: frontend/src/components/AppSidebar.vue:460 #: frontend/src/components/AppSidebar.vue:460
msgid "Create your first batch" msgid "Create your first batch"
@@ -1754,7 +1754,7 @@ msgstr "编辑"
#: frontend/src/components/Modals/AssignmentForm.vue:8 #: frontend/src/components/Modals/AssignmentForm.vue:8
msgid "Edit Assignment" msgid "Edit Assignment"
msgstr "" msgstr "编辑作业"
#: frontend/src/components/CourseOutline.vue:52 #: frontend/src/components/CourseOutline.vue:52
#: frontend/src/components/Modals/ChapterModal.vue:5 #: frontend/src/components/Modals/ChapterModal.vue:5
@@ -3584,11 +3584,11 @@ msgstr "无证书"
#: frontend/src/pages/CertifiedParticipants.vue:110 #: frontend/src/pages/CertifiedParticipants.vue:110
msgid "No certified members" msgid "No certified members"
msgstr "" msgstr "无认证成员"
#: frontend/src/pages/CertifiedParticipants.vue:114 #: frontend/src/pages/CertifiedParticipants.vue:114
msgid "No certified members found. Please check again later or get certified yourself." msgid "No certified members found. Please check again later or get certified yourself."
msgstr "" msgstr "未找到认证成员,请稍后再试或自行申请认证。"
#: frontend/src/components/BatchCourses.vue:67 #: frontend/src/components/BatchCourses.vue:67
msgid "No courses added" msgid "No courses added"
@@ -4077,11 +4077,11 @@ msgstr "请输入答案"
#: lms/lms/doctype/lms_batch/lms_batch.py:58 #: lms/lms/doctype/lms_batch/lms_batch.py:58
msgid "Please install the Payments App to create a paid batch. Refer to the documentation for more details. {0}" msgid "Please install the Payments App to create a paid batch. Refer to the documentation for more details. {0}"
msgstr "" msgstr "请安装支付应用以创建付费班级,详情请参阅文档{0}"
#: lms/lms/doctype/lms_course/lms_course.py:55 #: lms/lms/doctype/lms_course/lms_course.py:55
msgid "Please install the Payments App to create a paid course. Refer to the documentation for more details. {0}" msgid "Please install the Payments App to create a paid course. Refer to the documentation for more details. {0}"
msgstr "" msgstr "请安装支付应用以创建付费课程,详情请参阅文档{0}"
#: frontend/src/pages/Billing.vue:254 #: frontend/src/pages/Billing.vue:254
msgid "Please let us know where you heard about us from." msgid "Please let us know where you heard about us from."
@@ -4184,7 +4184,7 @@ msgstr "发布于"
#: frontend/src/components/AppSidebar.vue:92 #: frontend/src/components/AppSidebar.vue:92
msgid "Powered by Learning" msgid "Powered by Learning"
msgstr "" msgstr "技术支持:学习平台"
#. Name of a DocType #. Name of a DocType
#: lms/lms/doctype/preferred_function/preferred_function.json #: lms/lms/doctype/preferred_function/preferred_function.json
@@ -5135,7 +5135,7 @@ msgstr "提交列表"
#: frontend/src/components/Modals/AssignmentForm.vue:30 #: frontend/src/components/Modals/AssignmentForm.vue:30
msgid "Submission Type" msgid "Submission Type"
msgstr "" msgstr "提交类型"
#: frontend/src/components/Assignment.vue:13 #: frontend/src/components/Assignment.vue:13
#: frontend/src/components/Assignment.vue:16 #: frontend/src/components/Assignment.vue:16
@@ -6139,7 +6139,7 @@ msgstr "证书"
#: frontend/src/pages/CertifiedParticipants.vue:21 #: frontend/src/pages/CertifiedParticipants.vue:21
msgid "certified members" msgid "certified members"
msgstr "" msgstr "认证成员"
#: frontend/src/pages/Lesson.vue:178 #: frontend/src/pages/Lesson.vue:178
msgid "completed" msgid "completed"

View File

@@ -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

View 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)