mirror of
https://github.com/frappe/lms.git
synced 2026-05-02 13:39:31 +03:00
Merge pull request #1957 from frappe/develop
chore: merge 'develop' into 'main'
This commit is contained in:
@@ -34,11 +34,11 @@ jobs:
|
||||
- name: setup python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.10'
|
||||
python-version: '3.14'
|
||||
- name: setup node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
node-version: '24'
|
||||
check-latest: true
|
||||
- name: setup cache for bench
|
||||
uses: actions/cache@v4
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
python-version: '3.14'
|
||||
|
||||
- name: Check for valid Python & Merge Conflicts
|
||||
run: |
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 24
|
||||
check-latest: true
|
||||
|
||||
- name: Add to Hosts
|
||||
|
||||
@@ -80,6 +80,7 @@ onMounted(() => {
|
||||
{},
|
||||
{
|
||||
onSuccess(data) {
|
||||
destructureSidebarLinks()
|
||||
filterLinksToShow(data)
|
||||
addOtherLinks()
|
||||
},
|
||||
@@ -103,6 +104,16 @@ watch(showMenu, (val) => {
|
||||
}
|
||||
})
|
||||
|
||||
const destructureSidebarLinks = () => {
|
||||
let links = []
|
||||
sidebarLinks.value.forEach((link) => {
|
||||
link.items?.forEach((item) => {
|
||||
links.push(item)
|
||||
})
|
||||
})
|
||||
sidebarLinks.value = links
|
||||
}
|
||||
|
||||
const filterLinksToShow = (data) => {
|
||||
Object.keys(data).forEach((key) => {
|
||||
if (!parseInt(data[key])) {
|
||||
|
||||
@@ -1,72 +1,70 @@
|
||||
<template>
|
||||
<Dialog
|
||||
:options="{
|
||||
title: 'Edit your profile',
|
||||
size: '3xl',
|
||||
}"
|
||||
>
|
||||
<template #body-content>
|
||||
<div>
|
||||
<div class="grid grid-cols-2 gap-10">
|
||||
<div>
|
||||
<div class="text-xs text-ink-gray-5 mb-1">
|
||||
{{ __('Profile Image') }}
|
||||
</div>
|
||||
<FileUploader
|
||||
v-if="!profile.image"
|
||||
:fileTypes="['image/*']"
|
||||
:validateFile="validateFile"
|
||||
@success="(file) => saveImage(file)"
|
||||
>
|
||||
<template
|
||||
v-slot="{ file, progress, uploading, openFileSelector }"
|
||||
>
|
||||
<div class="mb-4">
|
||||
<Button @click="openFileSelector" :loading="uploading">
|
||||
{{
|
||||
uploading
|
||||
? `Uploading ${progress}%`
|
||||
: 'Upload a profile image'
|
||||
}}
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</FileUploader>
|
||||
<div v-else class="mb-4">
|
||||
<div class="flex items-center">
|
||||
<img
|
||||
:src="profile.image?.file_url"
|
||||
class="object-cover h-[50px] w-[50px] rounded-full border-4 border-white object-cover"
|
||||
/>
|
||||
|
||||
<div class="text-base flex flex-col ml-2">
|
||||
<span>
|
||||
{{ profile.image?.file_name }}
|
||||
</span>
|
||||
<span class="text-sm text-ink-gray-4 mt-1">
|
||||
{{ getFileSize(profile.image?.file_size) }}
|
||||
</span>
|
||||
</div>
|
||||
<X
|
||||
@click="removeImage()"
|
||||
class="bg-surface-gray-3 rounded-md cursor-pointer stroke-1.5 w-5 h-5 p-1 ml-4"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Switch
|
||||
v-model="profile.looking_for_job"
|
||||
:label="__('Open to Opportunities')"
|
||||
:description="
|
||||
__('Show recruiters and others that you are open to work.')
|
||||
"
|
||||
class="!px-0"
|
||||
/>
|
||||
<template #body-header>
|
||||
<div class="flex items-center mb-5">
|
||||
<div class="text-2xl font-semibold leading-6 text-ink-gray-9">
|
||||
{{ __('Edit Profile') }}
|
||||
</div>
|
||||
|
||||
<Badge v-if="isDirty" class="ml-4" theme="orange">
|
||||
{{ __('Not Saved') }}
|
||||
</Badge>
|
||||
</div>
|
||||
</template>
|
||||
<template #body-content>
|
||||
<div class="text-base">
|
||||
<div class="grid grid-cols-2 gap-10">
|
||||
<div class="space-y-4">
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<div class="text-xs text-ink-gray-5 mb-1">
|
||||
{{ __('Profile Image') }}
|
||||
</div>
|
||||
<FileUploader
|
||||
v-if="!profile.image"
|
||||
:fileTypes="['image/*']"
|
||||
:validateFile="validateFile"
|
||||
@success="(file) => saveImage(file)"
|
||||
>
|
||||
<template
|
||||
v-slot="{ file, progress, uploading, openFileSelector }"
|
||||
>
|
||||
<div class="mb-4">
|
||||
<Button @click="openFileSelector" :loading="uploading">
|
||||
{{
|
||||
uploading
|
||||
? `Uploading ${progress}%`
|
||||
: 'Upload a profile image'
|
||||
}}
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</FileUploader>
|
||||
<div v-else class="mb-4">
|
||||
<div class="flex items-center">
|
||||
<img
|
||||
:src="profile.image?.file_url"
|
||||
class="object-cover h-[50px] w-[50px] rounded-full border-4 border-white object-cover"
|
||||
/>
|
||||
|
||||
<div class="text-base flex flex-col ml-2">
|
||||
<span>
|
||||
{{ profile.image?.file_name }}
|
||||
</span>
|
||||
<span class="text-sm text-ink-gray-4 mt-1">
|
||||
{{ getFileSize(profile.image?.file_size) }}
|
||||
</span>
|
||||
</div>
|
||||
<X
|
||||
@click="removeImage()"
|
||||
class="bg-surface-gray-3 rounded-md cursor-pointer stroke-1.5 w-5 h-5 p-1 ml-4"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<FormControl
|
||||
v-model="profile.first_name"
|
||||
:label="__('First Name')"
|
||||
@@ -89,6 +87,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
<FormControl
|
||||
v-model="profile.open_to"
|
||||
type="select"
|
||||
:options="[' ', 'Opportunities', 'Hiring']"
|
||||
:label="__('Open to')"
|
||||
:placeholder="__('Looking for new work or hiring talent?')"
|
||||
/>
|
||||
<Link
|
||||
:label="__('Language')"
|
||||
v-model="profile.language"
|
||||
@@ -103,7 +108,7 @@
|
||||
@change="(val) => (profile.bio = val)"
|
||||
:content="profile.bio"
|
||||
:rows="15"
|
||||
editorClass="prose-sm py-2 px-2 min-h-[200px] border-outline-gray-2 hover:border-outline-gray-3 rounded-b-md bg-surface-gray-3"
|
||||
editorClass="prose-sm py-2 px-2 min-h-[280px] border-outline-gray-2 hover:border-outline-gray-3 rounded-b-md bg-surface-gray-3"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -121,12 +126,12 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import {
|
||||
Badge,
|
||||
Button,
|
||||
createResource,
|
||||
Dialog,
|
||||
FormControl,
|
||||
FileUploader,
|
||||
Switch,
|
||||
TextEditor,
|
||||
toast,
|
||||
} from 'frappe-ui'
|
||||
@@ -137,6 +142,7 @@ import Link from '@/components/Controls/Link.vue'
|
||||
|
||||
const reloadProfile = defineModel('reloadProfile')
|
||||
const hasLanguageChanged = ref(false)
|
||||
const isDirty = ref(false)
|
||||
|
||||
const props = defineProps({
|
||||
profile: {
|
||||
@@ -151,7 +157,7 @@ const profile = reactive({
|
||||
headline: '',
|
||||
bio: '',
|
||||
image: '',
|
||||
looking_for_job: false,
|
||||
open_to: '',
|
||||
linkedin: '',
|
||||
github: '',
|
||||
twitter: '',
|
||||
@@ -222,6 +228,27 @@ const removeImage = () => {
|
||||
profile.image = null
|
||||
}
|
||||
|
||||
watch(
|
||||
() => profile,
|
||||
(newVal) => {
|
||||
if (!props.profile.data) return
|
||||
let keys = Object.keys(newVal)
|
||||
keys.splice(keys.indexOf('image'), 1)
|
||||
for (let key of keys) {
|
||||
if (newVal[key] !== props.profile.data[key]) {
|
||||
isDirty.value = true
|
||||
return
|
||||
}
|
||||
}
|
||||
if (profile.image?.file_url !== props.profile.data.user_image) {
|
||||
isDirty.value = true
|
||||
return
|
||||
}
|
||||
isDirty.value = false
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.profile.data,
|
||||
(newVal) => {
|
||||
@@ -231,11 +258,12 @@ watch(
|
||||
profile.headline = newVal.headline
|
||||
profile.language = newVal.language
|
||||
profile.bio = newVal.bio
|
||||
profile.looking_for_job = newVal.looking_for_job
|
||||
profile.open_to = newVal.open_to
|
||||
profile.linkedin = newVal.linkedin
|
||||
profile.github = newVal.github
|
||||
profile.twitter = newVal.twitter
|
||||
if (newVal.user_image) imageResource.submit({ image: newVal.user_image })
|
||||
isDirty.value = false
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1,42 +1,50 @@
|
||||
<template>
|
||||
<div class="border rounded-md w-1/3 mx-auto my-32">
|
||||
<div class="border-b px-5 py-3 font-medium text-ink-gray-9">
|
||||
<span
|
||||
class="inline-flex items-center before:bg-surface-red-5 before:w-2 before:h-2 before:rounded-md before:mr-2"
|
||||
></span>
|
||||
{{ __('Not Permitted') }}
|
||||
</div>
|
||||
<div v-if="user.data" class="px-5 py-3">
|
||||
<div class="text-ink-gray-7">
|
||||
{{ __('You do not have permission to access this page.') }}
|
||||
<div class="bg-surface-white w-full h-full">
|
||||
<div class="w-fit mx-auto mt-56 text-center p-4">
|
||||
<div class="text-3xl font-semibold text-ink-gray-5 pb-4 mb-2 border-b">
|
||||
{{ __('Not Permitted') }}
|
||||
</div>
|
||||
<router-link
|
||||
:to="{
|
||||
name: 'Courses',
|
||||
}"
|
||||
>
|
||||
<Button variant="solid" class="mt-2">
|
||||
{{ __('Checkout Courses') }}
|
||||
<div v-if="user.data" class="px-5 py-3">
|
||||
<div class="text-ink-gray-5">
|
||||
{{ __('You do not have permission to access this page.') }}
|
||||
</div>
|
||||
<router-link
|
||||
:to="{
|
||||
name: 'Courses',
|
||||
}"
|
||||
>
|
||||
<Button variant="solid" class="mt-2 w-full">
|
||||
{{ __('Checkout Courses') }}
|
||||
</Button>
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="px-5 py-3">
|
||||
<div class="text-ink-gray-5">
|
||||
{{ __('You are not permitted to access this page.') }}
|
||||
</div>
|
||||
<Button @click="redirectToLogin()" class="mt-4 w-full" variant="solid">
|
||||
{{ __('Login') }}
|
||||
</Button>
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="px-5 py-3">
|
||||
<div class="text-ink-gray-7">
|
||||
{{ __('Please login to access this page.') }}
|
||||
</div>
|
||||
<Button @click="redirectToLogin()" class="mt-4">
|
||||
{{ __('Login') }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { inject } from 'vue'
|
||||
import { Button } from 'frappe-ui'
|
||||
import { Button, usePageMeta } from 'frappe-ui'
|
||||
import { sessionStore } from '../stores/session'
|
||||
|
||||
const user = inject('$user')
|
||||
const { brand } = sessionStore()
|
||||
|
||||
const redirectToLogin = () => {
|
||||
window.location.href = '/login'
|
||||
}
|
||||
|
||||
usePageMeta(() => {
|
||||
return {
|
||||
title: __('Not Permitted'),
|
||||
icon: brand.favicon,
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -7,13 +7,20 @@
|
||||
:size="size"
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<template v-if="user.looking_for_job" #indicator>
|
||||
<template v-if="user.open_to === 'Opportunities'" #indicator>
|
||||
<Tooltip :text="__('Open to Opportunities')" placement="right">
|
||||
<div class="rounded-full bg-surface-green-3 w-fit">
|
||||
<BadgeCheckIcon :class="'text-ink-white ' + checkSize" />
|
||||
</div>
|
||||
</Tooltip>
|
||||
</template>
|
||||
<template v-else-if="user.open_to === 'Hiring'" #indicator>
|
||||
<Tooltip :text="__('Hiring')" placement="right">
|
||||
<div class="rounded-full bg-purple-500 w-fit">
|
||||
<BadgeCheckIcon :class="'text-ink-white ' + checkSize" />
|
||||
</div>
|
||||
</Tooltip>
|
||||
</template>
|
||||
</Avatar>
|
||||
</template>
|
||||
<script setup>
|
||||
|
||||
@@ -91,6 +91,16 @@
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p
|
||||
class="bg-surface-amber-2 text-ink-amber-2 text-sm leading-5 p-2 rounded-md"
|
||||
>
|
||||
{{
|
||||
__(
|
||||
'Please ensure that the billing name you enter is correct, as it will be used on your invoice.'
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 lg:mr-10">
|
||||
@@ -104,16 +114,22 @@
|
||||
<FormControl
|
||||
:label="__('Billing Name')"
|
||||
v-model="billingDetails.billing_name"
|
||||
:required="true"
|
||||
/>
|
||||
<FormControl
|
||||
:label="__('Address Line 1')"
|
||||
v-model="billingDetails.address_line1"
|
||||
:required="true"
|
||||
/>
|
||||
<FormControl
|
||||
:label="__('Address Line 2')"
|
||||
v-model="billingDetails.address_line2"
|
||||
/>
|
||||
<FormControl :label="__('City')" v-model="billingDetails.city" />
|
||||
<FormControl
|
||||
:label="__('City')"
|
||||
v-model="billingDetails.city"
|
||||
:required="true"
|
||||
/>
|
||||
<FormControl
|
||||
:label="__('State/Province')"
|
||||
v-model="billingDetails.state"
|
||||
@@ -125,20 +141,24 @@
|
||||
:value="billingDetails.country"
|
||||
@change="(option) => changeCurrency(option)"
|
||||
:label="__('Country')"
|
||||
:required="true"
|
||||
/>
|
||||
<FormControl
|
||||
:label="__('Postal Code')"
|
||||
v-model="billingDetails.pincode"
|
||||
:required="true"
|
||||
/>
|
||||
<FormControl
|
||||
:label="__('Phone Number')"
|
||||
v-model="billingDetails.phone"
|
||||
:required="true"
|
||||
/>
|
||||
<Link
|
||||
doctype="LMS Source"
|
||||
:value="billingDetails.source"
|
||||
@change="(option) => (billingDetails.source = option)"
|
||||
:label="__('Where did you hear about us?')"
|
||||
:required="true"
|
||||
/>
|
||||
<FormControl
|
||||
v-if="billingDetails.country == 'India'"
|
||||
@@ -152,14 +172,29 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between border-t pt-4 mt-8">
|
||||
<p class="text-ink-gray-5">
|
||||
{{
|
||||
__(
|
||||
'Make sure to enter the correct billing name as the same will be used in your invoice.'
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<div
|
||||
class="flex flex-col lg:flex-row items-start lg:items-center justify-between border-t pt-4 mt-8 space-y-4 lg:space-y-0"
|
||||
>
|
||||
<div>
|
||||
<FormControl
|
||||
:label="
|
||||
__(
|
||||
'I consent to my personal information being stored for invoicing'
|
||||
)
|
||||
"
|
||||
type="checkbox"
|
||||
class="leading-6"
|
||||
v-model="billingDetails.member_consent"
|
||||
/>
|
||||
<div
|
||||
v-if="showConsentWarning"
|
||||
class="mt-1 text-xs text-ink-red-3"
|
||||
>
|
||||
{{
|
||||
__('Please provide your consent to proceed with the payment')
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
<Button variant="solid" size="md" @click="generatePaymentLink()">
|
||||
{{ __('Proceed to Payment') }}
|
||||
</Button>
|
||||
@@ -194,7 +229,7 @@ import {
|
||||
toast,
|
||||
call,
|
||||
} from 'frappe-ui'
|
||||
import { reactive, inject, onMounted, computed, ref } from 'vue'
|
||||
import { reactive, inject, onMounted, computed, ref, watch } from 'vue'
|
||||
import { sessionStore } from '../stores/session'
|
||||
import Link from '@/components/Controls/Link.vue'
|
||||
import NotPermitted from '@/components/NotPermitted.vue'
|
||||
@@ -202,6 +237,7 @@ import { X } from 'lucide-vue-next'
|
||||
|
||||
const user = inject('$user')
|
||||
const { brand } = sessionStore()
|
||||
const showConsentWarning = ref(false)
|
||||
|
||||
onMounted(() => {
|
||||
const script = document.createElement('script')
|
||||
@@ -296,6 +332,10 @@ const generatePaymentLink = () => {
|
||||
if (!billingDetails.source) {
|
||||
return __('Please let us know where you heard about us from.')
|
||||
}
|
||||
if (!billingDetails.member_consent) {
|
||||
showConsentWarning.value = true
|
||||
return __('Please provide your consent to proceed with the payment.')
|
||||
}
|
||||
return validateAddress()
|
||||
},
|
||||
onSuccess(data) {
|
||||
@@ -406,6 +446,12 @@ const redirectTo = computed(() => {
|
||||
}
|
||||
})
|
||||
|
||||
watch(billingDetails, () => {
|
||||
if (billingDetails.member_consent) {
|
||||
showConsentWarning.value = false
|
||||
}
|
||||
})
|
||||
|
||||
usePageMeta(() => {
|
||||
return {
|
||||
title: __('Billing Details'),
|
||||
|
||||
@@ -13,27 +13,45 @@
|
||||
</router-link>
|
||||
</header>
|
||||
<div class="mx-auto w-full max-w-4xl pt-6 pb-10">
|
||||
<div class="flex flex-col md:flex-row justify-between mb-4 px-3">
|
||||
<div class="text-xl font-semibold text-ink-gray-7 mb-4 md:mb-0">
|
||||
<div class="flex flex-col md:flex-row justify-between mb-8 px-3">
|
||||
<div class="text-xl font-semibold text-ink-gray-9 mb-4 md:mb-0">
|
||||
{{ memberCount }} {{ __('certified members') }}
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<FormControl
|
||||
v-model="nameFilter"
|
||||
:placeholder="__('Search by Name')"
|
||||
type="text"
|
||||
class="min-w-40 lg:min-w-0 lg:w-32 xl:w-40"
|
||||
@input="updateParticipants()"
|
||||
/>
|
||||
<div
|
||||
v-if="categories.data?.length"
|
||||
class="min-w-40 lg:min-w-0 lg:w-32 xl:w-40"
|
||||
>
|
||||
<Select
|
||||
v-model="currentCategory"
|
||||
:options="categories.data"
|
||||
:placeholder="__('Category')"
|
||||
@update:modelValue="updateParticipants()"
|
||||
<div
|
||||
class="flex flex-col md:flex-row md:items-center space-y-4 md:space-y-0 md:space-x-4"
|
||||
>
|
||||
<div class="flex items-center space-x-4">
|
||||
<FormControl
|
||||
v-model="nameFilter"
|
||||
:placeholder="__('Search by Name')"
|
||||
type="text"
|
||||
class="min-w-40 lg:min-w-0 lg:w-32 xl:w-40"
|
||||
@input="updateParticipants()"
|
||||
/>
|
||||
<div
|
||||
v-if="categories.data?.length"
|
||||
class="min-w-40 lg:min-w-0 lg:w-32 xl:w-40"
|
||||
>
|
||||
<Select
|
||||
v-model="currentCategory"
|
||||
:options="categories.data"
|
||||
:placeholder="__('Category')"
|
||||
@update:modelValue="updateParticipants()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center space-x-4">
|
||||
<FormControl
|
||||
v-model="openToOpportunities"
|
||||
:label="__('Open to Opportunities')"
|
||||
type="checkbox"
|
||||
@change="updateParticipants()"
|
||||
/>
|
||||
<FormControl
|
||||
v-model="hiring"
|
||||
:label="__('Hiring')"
|
||||
type="checkbox"
|
||||
@change="updateParticipants()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -42,15 +60,15 @@
|
||||
<template v-for="(participant, index) in participants.data">
|
||||
<router-link
|
||||
:to="{
|
||||
name: 'ProfileCertificates',
|
||||
name: 'ProfileAbout',
|
||||
params: {
|
||||
username: participant.username,
|
||||
},
|
||||
}"
|
||||
class="flex h-15 rounded-md hover:bg-surface-gray-2 px-3"
|
||||
class="flex rounded-md hover:bg-surface-gray-2 px-3"
|
||||
>
|
||||
<div
|
||||
class="flex items-center w-full space-x-3 py-2"
|
||||
class="flex w-full space-x-3 py-2"
|
||||
:class="{
|
||||
'border-b': index < participants.data.length - 1,
|
||||
}"
|
||||
@@ -118,9 +136,11 @@ import { sessionStore } from '../stores/session'
|
||||
import EmptyState from '@/components/EmptyState.vue'
|
||||
import UserAvatar from '@/components/UserAvatar.vue'
|
||||
|
||||
const currentCategory = ref('')
|
||||
const filters = ref({})
|
||||
const currentCategory = ref('')
|
||||
const nameFilter = ref('')
|
||||
const openToOpportunities = ref(false)
|
||||
const hiring = ref(false)
|
||||
const { brand } = sessionStore()
|
||||
const memberCount = ref(0)
|
||||
const dayjs = inject('$dayjs')
|
||||
@@ -152,7 +172,7 @@ const categories = createListResource({
|
||||
cache: ['certification_categories'],
|
||||
auto: true,
|
||||
transform(data) {
|
||||
data.unshift({ label: __(''), value: '' })
|
||||
data.unshift({ label: __(' '), value: ' ' })
|
||||
return data
|
||||
},
|
||||
})
|
||||
@@ -169,16 +189,19 @@ const updateParticipants = () => {
|
||||
}
|
||||
|
||||
const updateFilters = () => {
|
||||
if (currentCategory.value) {
|
||||
filters.value.category = currentCategory.value
|
||||
} else {
|
||||
delete filters.value.category
|
||||
}
|
||||
|
||||
if (nameFilter.value) {
|
||||
filters.value.member_name = ['like', `%${nameFilter.value}%`]
|
||||
} else {
|
||||
delete filters.value.member_name
|
||||
filters.value = {
|
||||
...(currentCategory.value && {
|
||||
category: currentCategory.value,
|
||||
}),
|
||||
...(nameFilter.value && {
|
||||
member_name: ['like', `%${nameFilter.value}%`],
|
||||
}),
|
||||
...(openToOpportunities.value && {
|
||||
open_to_opportunities: true,
|
||||
}),
|
||||
...(hiring.value && {
|
||||
hiring: true,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,10 +210,12 @@ const setQueryParams = () => {
|
||||
let filterKeys = {
|
||||
category: currentCategory.value,
|
||||
name: nameFilter.value,
|
||||
'open-to-opportunities': openToOpportunities.value,
|
||||
hiring: hiring.value,
|
||||
}
|
||||
|
||||
Object.keys(filterKeys).forEach((key) => {
|
||||
if (filterKeys[key]) {
|
||||
if (filterKeys[key] && hasValue(filterKeys[key])) {
|
||||
queries.set(key, filterKeys[key])
|
||||
} else {
|
||||
queries.delete(key)
|
||||
@@ -203,10 +228,19 @@ const setQueryParams = () => {
|
||||
)
|
||||
}
|
||||
|
||||
const hasValue = (value) => {
|
||||
if (typeof value === 'string') {
|
||||
return value.trim() !== ''
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
const setFiltersFromQuery = () => {
|
||||
let queries = new URLSearchParams(location.search)
|
||||
nameFilter.value = queries.get('name') || ''
|
||||
currentCategory.value = queries.get('category') || ''
|
||||
openToOpportunities.value = queries.get('open-to-opportunities') === 'true'
|
||||
hiring.value = queries.get('hiring') === 'true'
|
||||
}
|
||||
|
||||
const breadcrumbs = computed(() => [
|
||||
|
||||
+57
-41
@@ -26,56 +26,72 @@
|
||||
</header>
|
||||
<div>
|
||||
<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"
|
||||
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 mb-2 p-5"
|
||||
>
|
||||
<div class="text-xl font-semibold text-ink-gray-7 mb-4 md:mb-0">
|
||||
{{ __('{0} Open Jobs').format(jobCount) }}
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between space-x-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="text-xl font-semibold text-ink-gray-9 md:mb-0">
|
||||
{{ __('{0} {1} Jobs').format(jobCount, activeTab) }}
|
||||
</div>
|
||||
<TabButtons
|
||||
v-if="tabs.length > 1"
|
||||
v-model="activeTab"
|
||||
:buttons="tabs"
|
||||
class="lg:hidden"
|
||||
@change="updateJobs"
|
||||
/>
|
||||
<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
|
||||
v-if="user.data"
|
||||
doctype="Country"
|
||||
v-model="country"
|
||||
:placeholder="__('Country')"
|
||||
class="min-w-32 lg:min-w-0 lg:w-32 xl:w-32"
|
||||
/>
|
||||
<FormControl
|
||||
v-model="jobType"
|
||||
type="select"
|
||||
:options="jobTypes"
|
||||
class="min-w-32 lg:min-w-0 lg:w-32 xl:w-32"
|
||||
:placeholder="__('Type')"
|
||||
@change="updateJobs"
|
||||
/>
|
||||
<FormControl
|
||||
v-model="workMode"
|
||||
type="select"
|
||||
:options="workModes"
|
||||
class="min-w-32 lg:min-w-0 lg:w-32 xl:w-32"
|
||||
:placeholder="__('Work Mode')"
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex flex-col md:flex-row md:items-center md:space-x-4 space-y-4 md:space-y-0"
|
||||
>
|
||||
<TabButtons
|
||||
v-if="tabs.length > 1"
|
||||
v-model="activeTab"
|
||||
:buttons="tabs"
|
||||
class="hidden lg:block"
|
||||
@change="updateJobs"
|
||||
/>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<FormControl
|
||||
type="text"
|
||||
:placeholder="__('Search')"
|
||||
v-model="searchQuery"
|
||||
class="w-full max-w-40"
|
||||
@input="updateJobs"
|
||||
>
|
||||
<template #prefix>
|
||||
<Search
|
||||
class="w-4 h-4 stroke-1.5 text-ink-gray-5"
|
||||
name="search"
|
||||
/>
|
||||
</template>
|
||||
</FormControl>
|
||||
<Link
|
||||
v-if="user.data"
|
||||
doctype="Country"
|
||||
v-model="country"
|
||||
:placeholder="__('Country')"
|
||||
class="w-full"
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<FormControl
|
||||
v-model="jobType"
|
||||
type="select"
|
||||
:options="jobTypes"
|
||||
class="w-full"
|
||||
:placeholder="__('Type')"
|
||||
@change="updateJobs"
|
||||
/>
|
||||
<FormControl
|
||||
v-model="workMode"
|
||||
type="select"
|
||||
:options="workModes"
|
||||
class="w-full"
|
||||
:placeholder="__('Work Mode')"
|
||||
@change="updateJobs"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="jobs.data?.length" class="w-full md:w-4/5 mx-auto p-5 pt-0">
|
||||
|
||||
@@ -56,15 +56,32 @@
|
||||
:src="profile.data.user_image"
|
||||
class="object-cover h-[100px] w-[100px] rounded-full border-4 border-white object-cover"
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
class="flex items-center justify-center h-[100px] w-[100px] rounded-full border-4 border-white bg-surface-gray-2 text-3xl font-semibold text-ink-gray-7"
|
||||
>
|
||||
{{ profile.data.full_name.charAt(0).toUpperCase() }}
|
||||
</div>
|
||||
<Tooltip
|
||||
v-if="profile.data.looking_for_job"
|
||||
:text="__('Open to Opportunities')"
|
||||
v-if="profile.data.open_to"
|
||||
:text="
|
||||
profile.data.open_to === 'Opportunities'
|
||||
? __('Open to Opportunities')
|
||||
: __('Hiring')
|
||||
"
|
||||
placement="right"
|
||||
>
|
||||
<div
|
||||
class="absolute bottom-3 right-1 p-0.5 bg-surface-white rounded-full"
|
||||
>
|
||||
<div class="rounded-full bg-surface-green-3 w-fit">
|
||||
<div
|
||||
class="rounded-full w-fit"
|
||||
:class="
|
||||
profile.data.open_to === 'Opportunities'
|
||||
? 'bg-surface-green-3'
|
||||
: 'bg-purple-500'
|
||||
"
|
||||
>
|
||||
<BadgeCheckIcon class="text-ink-white size-5" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
__version__ = "2.43.0"
|
||||
__version__ = "2.44.0"
|
||||
|
||||
@@ -238,8 +238,8 @@
|
||||
"dt": "User",
|
||||
"fetch_from": null,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "looking_for_job",
|
||||
"fieldtype": "Check",
|
||||
"fieldname": "open_to",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"hide_border": 0,
|
||||
"hide_days": 0,
|
||||
@@ -253,16 +253,16 @@
|
||||
"insert_after": "verify_terms",
|
||||
"is_system_generated": 1,
|
||||
"is_virtual": 0,
|
||||
"label": "Open to Opportunities",
|
||||
"label": "Open to",
|
||||
"length": 0,
|
||||
"link_filters": null,
|
||||
"mandatory_depends_on": null,
|
||||
"modified": "2021-12-31 12:56:32.110405",
|
||||
"modified": "2025-12-24 12:56:32.110405",
|
||||
"module": null,
|
||||
"name": "User-looking_for_job",
|
||||
"name": "User-open_to",
|
||||
"no_copy": 0,
|
||||
"non_negative": 0,
|
||||
"options": null,
|
||||
"options": "\nOpportunities\nHiring",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
|
||||
+1
-1
@@ -136,7 +136,7 @@ def delete_custom_fields():
|
||||
"medium",
|
||||
"linkedin",
|
||||
"profession",
|
||||
"looking_for_job",
|
||||
"open_to",
|
||||
"cover_image" "work_environment",
|
||||
"dream_companies",
|
||||
"career_preference_column",
|
||||
|
||||
+57
-22
@@ -281,10 +281,34 @@ def get_evaluator_details(evaluator):
|
||||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
def get_certified_participants(filters=None, start=0, page_length=100):
|
||||
filters, or_filters, open_to_opportunities, hiring = update_certification_filters(filters)
|
||||
|
||||
participants = frappe.db.get_all(
|
||||
"LMS Certificate",
|
||||
filters=filters,
|
||||
or_filters=or_filters,
|
||||
fields=["member", "issue_date", "batch_name", "course", "name"],
|
||||
group_by="member",
|
||||
order_by="issue_date desc",
|
||||
start=start,
|
||||
page_length=page_length,
|
||||
)
|
||||
|
||||
for participant in participants:
|
||||
details = get_certified_participant_details(participant.member)
|
||||
participant.update(details)
|
||||
|
||||
participants = filter_by_open_to_criteria(participants, open_to_opportunities, hiring)
|
||||
|
||||
return participants
|
||||
|
||||
|
||||
def update_certification_filters(filters):
|
||||
open_to_opportunities = False
|
||||
hiring = False
|
||||
or_filters = {}
|
||||
if not filters:
|
||||
filters = {}
|
||||
|
||||
filters.update({"published": 1})
|
||||
|
||||
category = filters.get("category")
|
||||
@@ -293,27 +317,38 @@ def get_certified_participants(filters=None, start=0, page_length=100):
|
||||
or_filters["course_title"] = ["like", f"%{category}%"]
|
||||
or_filters["batch_title"] = ["like", f"%{category}%"]
|
||||
|
||||
participants = frappe.db.get_all(
|
||||
"LMS Certificate",
|
||||
filters=filters,
|
||||
or_filters=or_filters,
|
||||
fields=["member", "issue_date"],
|
||||
group_by="member",
|
||||
order_by="issue_date desc",
|
||||
start=start,
|
||||
page_length=page_length,
|
||||
)
|
||||
if filters.get("open_to_opportunities"):
|
||||
del filters["open_to_opportunities"]
|
||||
open_to_opportunities = True
|
||||
|
||||
for participant in participants:
|
||||
count = frappe.db.count("LMS Certificate", {"member": participant.member})
|
||||
details = frappe.db.get_value(
|
||||
"User",
|
||||
participant.member,
|
||||
["full_name", "user_image", "username", "country", "headline", "looking_for_job"],
|
||||
as_dict=1,
|
||||
)
|
||||
details["certificate_count"] = count
|
||||
participant.update(details)
|
||||
if filters.get("hiring"):
|
||||
del filters["hiring"]
|
||||
hiring = True
|
||||
|
||||
return filters, or_filters, open_to_opportunities, hiring
|
||||
|
||||
|
||||
def get_certified_participant_details(member):
|
||||
count = frappe.db.count("LMS Certificate", {"member": member})
|
||||
details = frappe.db.get_value(
|
||||
"User",
|
||||
member,
|
||||
["full_name", "user_image", "username", "country", "headline", "open_to"],
|
||||
as_dict=1,
|
||||
)
|
||||
details["certificate_count"] = count
|
||||
return details
|
||||
|
||||
|
||||
def filter_by_open_to_criteria(participants, open_to_opportunities, hiring):
|
||||
if not open_to_opportunities and not hiring:
|
||||
return participants
|
||||
|
||||
if open_to_opportunities:
|
||||
participants = [participant for participant in participants if participant.open_to == "Opportunities"]
|
||||
|
||||
if hiring:
|
||||
participants = [participant for participant in participants if participant.open_to == "Hiring"]
|
||||
|
||||
return participants
|
||||
|
||||
@@ -1635,7 +1670,7 @@ def get_profile_details(username):
|
||||
"headline",
|
||||
"language",
|
||||
"cover_image",
|
||||
"looking_for_job",
|
||||
"open_to",
|
||||
"linkedin",
|
||||
"github",
|
||||
"twitter",
|
||||
|
||||
@@ -52,8 +52,14 @@ class TestCourseEvaluator(UnitTestCase):
|
||||
return first_date
|
||||
|
||||
def calculated_last_date_of_schedule(self, first_date):
|
||||
last_date = add_days(first_date, 56) # 8 weeks course
|
||||
return last_date
|
||||
last_day = add_days(first_date, 56)
|
||||
offset_monday = (0 - last_day.weekday() + 7) % 7 # 0 for Monday
|
||||
offset_wednesday = (2 - last_day.weekday() + 7) % 7 # 2 for Wednesday
|
||||
if offset_monday > offset_wednesday and offset_monday < 4:
|
||||
last_day = add_days(last_day, offset_monday)
|
||||
else:
|
||||
last_day = add_days(last_day, offset_wednesday)
|
||||
return last_day
|
||||
|
||||
def test_unavailability_dates(self):
|
||||
unavailable_from = getdate(self.evaluator.unavailable_from)
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"payment_for_document",
|
||||
"payment_received",
|
||||
"payment_for_certificate",
|
||||
"member_consent",
|
||||
"payment_details_section",
|
||||
"original_amount",
|
||||
"discount_amount",
|
||||
@@ -181,6 +182,12 @@
|
||||
"fieldtype": "Currency",
|
||||
"label": "Original Amount",
|
||||
"options": "currency"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "member_consent",
|
||||
"fieldtype": "Check",
|
||||
"label": "Member Consent"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
@@ -194,7 +201,7 @@
|
||||
"link_fieldname": "payment"
|
||||
}
|
||||
],
|
||||
"modified": "2025-11-12 12:39:52.466297",
|
||||
"modified": "2025-12-19 17:55:25.968384",
|
||||
"modified_by": "sayali@frappe.io",
|
||||
"module": "LMS",
|
||||
"name": "LMS Payment",
|
||||
|
||||
@@ -121,6 +121,7 @@ def record_payment(
|
||||
"payment_for_document_type": doctype,
|
||||
"payment_for_document": docname,
|
||||
"payment_for_certificate": payment_for_certificate,
|
||||
"member_consent": address.member_consent,
|
||||
}
|
||||
)
|
||||
if coupon_code:
|
||||
|
||||
@@ -2,6 +2,7 @@ import frappe
|
||||
from frappe.tests import UnitTestCase
|
||||
from frappe.utils import add_days, nowdate
|
||||
|
||||
from lms.lms.api import get_certified_participants
|
||||
from lms.lms.doctype.lms_certificate.lms_certificate import get_default_certificate_template, is_certified
|
||||
|
||||
from .utils import (
|
||||
@@ -147,6 +148,7 @@ class TestUtils(UnitTestCase):
|
||||
certificate.member = member
|
||||
certificate.issue_date = frappe.utils.nowdate()
|
||||
certificate.template = get_default_certificate_template()
|
||||
certificate.published = 1
|
||||
certificate.save()
|
||||
return certificate
|
||||
|
||||
@@ -265,6 +267,36 @@ class TestUtils(UnitTestCase):
|
||||
self.assertIsNone(is_certified(self.course.name))
|
||||
frappe.session.user = "Administrator"
|
||||
|
||||
def test_certified_participants_with_category(self):
|
||||
filters = {"category": "Utility Course"}
|
||||
certified_participants = get_certified_participants(filters=filters)
|
||||
self.assertEqual(len(certified_participants), 1)
|
||||
self.assertEqual(certified_participants[0].member, self.student1.email)
|
||||
|
||||
filters = {"category": "Nonexistent Category"}
|
||||
certified_participants_no_match = get_certified_participants(filters=filters)
|
||||
self.assertEqual(len(certified_participants_no_match), 0)
|
||||
|
||||
def test_certified_participants_with_open_to_opportunities(self):
|
||||
filters = {"open_to_opportunities": 1}
|
||||
certified_participants_open_to_oppo = get_certified_participants(filters=filters)
|
||||
self.assertEqual(len(certified_participants_open_to_oppo), 0)
|
||||
|
||||
frappe.db.set_value("User", self.student1.email, "open_to", "Opportunities")
|
||||
certified_participants_open_to_oppo = get_certified_participants(filters=filters)
|
||||
self.assertEqual(len(certified_participants_open_to_oppo), 1)
|
||||
frappe.db.set_value("User", self.student1.email, "open_to", "")
|
||||
|
||||
def test_certified_participants_with_open_to_hiring(self):
|
||||
filters = {"hiring": 1}
|
||||
certified_participants_hiring = get_certified_participants(filters=filters)
|
||||
self.assertEqual(len(certified_participants_hiring), 0)
|
||||
|
||||
frappe.db.set_value("User", self.student1.email, "open_to", "Hiring")
|
||||
certified_participants_hiring = get_certified_participants(filters=filters)
|
||||
self.assertEqual(len(certified_participants_hiring), 1)
|
||||
frappe.db.set_value("User", self.student1.email, "open_to", "")
|
||||
|
||||
def test_rating_validation(self):
|
||||
student3 = self.create_user("student3@example.com", "Emily", "Cooper", ["LMS Student"])
|
||||
with self.assertRaises(frappe.exceptions.ValidationError):
|
||||
|
||||
+44
-44
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: frappe\n"
|
||||
"Report-Msgid-Bugs-To: jannat@frappe.io\n"
|
||||
"POT-Creation-Date: 2025-12-19 16:05+0000\n"
|
||||
"PO-Revision-Date: 2025-12-23 23:25\n"
|
||||
"PO-Revision-Date: 2025-12-29 11:09\n"
|
||||
"Last-Translator: jannat@frappe.io\n"
|
||||
"Language-Team: Persian\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -24,11 +24,11 @@ msgstr " لطفا آن را ارزیابی و نمره دهید."
|
||||
|
||||
#: frontend/src/pages/Programs/ProgramEnrollment.vue:32
|
||||
msgid " designed as a learning path to guide your progress. You may take the courses in any order that suits you. "
|
||||
msgstr ""
|
||||
msgstr " به عنوان یک مسیر یادگیری برای هدایت پیشرفت شما طراحی شده است. شما میتوانید دورهها را به هر ترتیبی که برای شما مناسب است، بگذرانید. "
|
||||
|
||||
#: frontend/src/pages/Programs/ProgramEnrollment.vue:25
|
||||
msgid " designed as a structured learning path to guide your progress. Courses in this program must be taken in order, and each course will unlock as you complete the previous one. "
|
||||
msgstr ""
|
||||
msgstr " به عنوان یک مسیر یادگیری ساختاریافته برای هدایت پیشرفت شما طراحی شده است. دورههای این برنامه باید به ترتیب گذرانده شوند و هر دوره با تکمیل دوره قبلی، قفل آن باز میشود. "
|
||||
|
||||
#: frontend/src/pages/Home/Streak.vue:21
|
||||
msgid " you are on a"
|
||||
@@ -37,7 +37,7 @@ msgstr " شما در حال حاضر در یک"
|
||||
#. Paragraph text in the LMS Workspace
|
||||
#: lms/lms/workspace/lms/lms.json
|
||||
msgid "<a href=\"/app/lms-settings/LMS%20Settings\">LMS Settings</a>"
|
||||
msgstr ""
|
||||
msgstr "<a href=\"/app/lms-settings/LMS%20Settings\">تنظیمات LMS</a>"
|
||||
|
||||
#. Paragraph text in the LMS Workspace
|
||||
#: lms/lms/workspace/lms/lms.json
|
||||
@@ -52,12 +52,12 @@ msgstr ""
|
||||
#. Paragraph text in the LMS Workspace
|
||||
#: lms/lms/workspace/lms/lms.json
|
||||
msgid "<a href=\"/lms/courses/new/edit\">Create a Course</a>"
|
||||
msgstr ""
|
||||
msgstr "<a href=\"/lms/courses/new/edit\">ایجاد یک دوره</a>"
|
||||
|
||||
#. Paragraph text in the LMS Workspace
|
||||
#: lms/lms/workspace/lms/lms.json
|
||||
msgid "<a href=\"https://docs.frappe.io/learning\">Documentation</a>"
|
||||
msgstr ""
|
||||
msgstr "<a href=\"https://docs.frappe.io/learning\">مستندات</a>"
|
||||
|
||||
#: frontend/src/components/Modals/EmailTemplateModal.vue:50
|
||||
msgid "<p>Dear {{ member_name }},</p>\\n\\n<p>You have been enrolled in our upcoming batch {{ batch_name }}.</p>\\n\\n<p>Thanks,</p>\\n<p>Frappe Learning</p>"
|
||||
@@ -124,7 +124,7 @@ msgstr "دستاوردها"
|
||||
|
||||
#: frontend/src/pages/Statistics.vue:16
|
||||
msgid "Active Members"
|
||||
msgstr ""
|
||||
msgstr "اعضای فعال"
|
||||
|
||||
#: frontend/src/components/Assessments.vue:11
|
||||
#: frontend/src/components/BatchCourses.vue:11
|
||||
@@ -148,7 +148,7 @@ msgstr "افزودن فصل"
|
||||
|
||||
#: frontend/src/pages/Programs/ProgramForm.vue:176
|
||||
msgid "Add Course to Program"
|
||||
msgstr ""
|
||||
msgstr "افزودن دوره به برنامه"
|
||||
|
||||
#: frontend/src/components/Settings/Evaluators.vue:91
|
||||
msgid "Add Evaluator"
|
||||
@@ -189,7 +189,7 @@ msgstr "افزودن دانشآموز"
|
||||
|
||||
#: frontend/src/components/Sidebar/AppSidebar.vue:523
|
||||
msgid "Add a chapter"
|
||||
msgstr ""
|
||||
msgstr "افزودن یک فصل"
|
||||
|
||||
#: frontend/src/components/Modals/BatchCourseModal.vue:5
|
||||
msgid "Add a course"
|
||||
@@ -201,16 +201,16 @@ msgstr "یک کلمه کلیدی اضافه کنید و سپس اینتر را
|
||||
|
||||
#: frontend/src/components/Sidebar/AppSidebar.vue:524
|
||||
msgid "Add a lesson"
|
||||
msgstr ""
|
||||
msgstr "افزودن درس"
|
||||
|
||||
#: frontend/src/components/Settings/Members.vue:88
|
||||
msgid "Add a new member"
|
||||
msgstr ""
|
||||
msgstr "افزودن یک عضو جدید"
|
||||
|
||||
#: frontend/src/components/Modals/Question.vue:167
|
||||
#: frontend/src/pages/QuizForm.vue:200
|
||||
msgid "Add a new question"
|
||||
msgstr ""
|
||||
msgstr "افزودن سؤال جدید"
|
||||
|
||||
#: frontend/src/components/Sidebar/AppSidebar.vue:538
|
||||
msgid "Add a program"
|
||||
@@ -308,7 +308,7 @@ msgstr "همه دوره ها"
|
||||
|
||||
#: frontend/src/pages/Programs/StudentPrograms.vue:5
|
||||
msgid "All Programs"
|
||||
msgstr ""
|
||||
msgstr "همه برنامهها"
|
||||
|
||||
#: lms/lms/doctype/lms_quiz/lms_quiz.py:42
|
||||
msgid "All questions should have the same marks if the limit is set."
|
||||
@@ -419,7 +419,7 @@ msgstr ""
|
||||
|
||||
#: frontend/src/components/Settings/Coupons/CouponDetails.vue:71
|
||||
msgid "Applicable For"
|
||||
msgstr ""
|
||||
msgstr "قابل استفاده برای"
|
||||
|
||||
#. Label of the applicable_items (Table) field in DocType 'LMS Coupon'
|
||||
#: lms/lms/doctype/lms_coupon/lms_coupon.json
|
||||
@@ -535,7 +535,7 @@ msgstr "ارزیابی ها"
|
||||
|
||||
#: lms/lms/doctype/lms_badge/lms_badge.js:48
|
||||
msgid "Assign"
|
||||
msgstr "اختصاص دهید"
|
||||
msgstr "اختصاص دادن"
|
||||
|
||||
#: frontend/src/components/Settings/BadgeForm.vue:28
|
||||
msgid "Assign For"
|
||||
@@ -1676,7 +1676,7 @@ msgstr "طرح کلی دوره"
|
||||
#: frontend/src/components/Modals/CourseProgressSummary.vue:5
|
||||
#: lms/lms/report/course_progress_summary/course_progress_summary.json
|
||||
msgid "Course Progress Summary"
|
||||
msgstr ""
|
||||
msgstr "خلاصه پیشرفت دوره"
|
||||
|
||||
#. Label of the section_break_7 (Section Break) field in DocType 'LMS Course'
|
||||
#: lms/lms/doctype/lms_course/lms_course.json
|
||||
@@ -1811,7 +1811,7 @@ msgstr ""
|
||||
|
||||
#: frontend/src/pages/Quizzes.vue:101
|
||||
msgid "Create a Quiz"
|
||||
msgstr ""
|
||||
msgstr "ایجاد یک آزمون"
|
||||
|
||||
#: frontend/src/components/Sidebar/AppSidebar.vue:531
|
||||
msgid "Create a batch"
|
||||
@@ -2014,7 +2014,7 @@ msgstr "این درس حذف شود؟"
|
||||
|
||||
#: frontend/src/pages/CourseForm.vue:617
|
||||
msgid "Deleting the course will also delete all its chapters and lessons. Are you sure you want to delete this course?"
|
||||
msgstr ""
|
||||
msgstr "حذف دوره، تمام فصلها و درسهای آن را نیز حذف خواهد کرد. آیا از حذف این دوره مطمئن هستید؟"
|
||||
|
||||
#: frontend/src/pages/BatchForm.vue:577
|
||||
msgid "Deleting this batch will also delete all its data including enrolled students, linked courses, assessments, feedback and discussions. Are you sure you want to continue?"
|
||||
@@ -2887,7 +2887,7 @@ msgstr ""
|
||||
|
||||
#: frontend/src/components/Modals/EditProfile.vue:84
|
||||
msgid "GitHub ID"
|
||||
msgstr ""
|
||||
msgstr "شناسه گیتهاب"
|
||||
|
||||
#. Label of the github (Data) field in DocType 'User'
|
||||
#: lms/fixtures/custom_field.json
|
||||
@@ -2944,7 +2944,7 @@ msgstr "سبز"
|
||||
|
||||
#: lms/templates/signup-form.html:56
|
||||
msgid "Have an account? Login"
|
||||
msgstr "حساب کاربری دارید؟ وارد شدن"
|
||||
msgstr "حساب کاربری دارید؟ ورود"
|
||||
|
||||
#. Label of the headline (Data) field in DocType 'User'
|
||||
#: frontend/src/components/Modals/EditProfile.vue:78
|
||||
@@ -3225,7 +3225,7 @@ msgstr ""
|
||||
|
||||
#: lms/lms/doctype/course_lesson/course_lesson.py:23
|
||||
msgid "Invalid Quiz ID"
|
||||
msgstr ""
|
||||
msgstr "شناسه آزمون نامعتبر"
|
||||
|
||||
#: lms/lms/doctype/course_lesson/course_lesson.py:37
|
||||
msgid "Invalid Quiz ID in content"
|
||||
@@ -3859,7 +3859,7 @@ msgstr ""
|
||||
#: frontend/src/components/QuizBlock.vue:9 frontend/src/pages/Batch.vue:213
|
||||
#: frontend/src/pages/Lesson.vue:103
|
||||
msgid "Login"
|
||||
msgstr "وارد شدن"
|
||||
msgstr "ورود"
|
||||
|
||||
#: frontend/src/components/Sidebar/UserDropdown.vue:190
|
||||
msgid "Login to Frappe Cloud?"
|
||||
@@ -4370,7 +4370,7 @@ msgstr ""
|
||||
|
||||
#: frontend/src/pages/QuizForm.vue:406 frontend/src/pages/QuizForm.vue:414
|
||||
msgid "New Quiz"
|
||||
msgstr ""
|
||||
msgstr "آزمون جدید"
|
||||
|
||||
#: lms/www/new-sign-up.html:3
|
||||
msgid "New Sign Up"
|
||||
@@ -4416,7 +4416,7 @@ msgstr ""
|
||||
|
||||
#: frontend/src/pages/Quizzes.vue:19
|
||||
msgid "No Quizzes"
|
||||
msgstr ""
|
||||
msgstr "بدون آزمون"
|
||||
|
||||
#. Option for the 'Auto Recording' (Select) field in DocType 'LMS Live Class'
|
||||
#: lms/lms/doctype/lms_live_class/lms_live_class.json
|
||||
@@ -5281,7 +5281,7 @@ msgstr "تصویر نمایه"
|
||||
|
||||
#: frontend/src/pages/Programs/Programs.vue:18
|
||||
msgid "Program"
|
||||
msgstr ""
|
||||
msgstr "برنامه"
|
||||
|
||||
#. Label of the program_courses (Table) field in DocType 'LMS Program'
|
||||
#: lms/lms/doctype/lms_program/lms_program.json
|
||||
@@ -5373,7 +5373,7 @@ msgstr ""
|
||||
#: frontend/src/components/CourseCardOverlay.vue:99
|
||||
#: frontend/src/pages/Programs/ProgramForm.vue:125
|
||||
msgid "Progress Summary"
|
||||
msgstr ""
|
||||
msgstr "خلاصه پیشرفت"
|
||||
|
||||
#: frontend/src/pages/Programs/ProgramProgressSummary.vue:5
|
||||
msgid "Progress Summary for {0}"
|
||||
@@ -5518,12 +5518,12 @@ msgstr ""
|
||||
|
||||
#: frontend/src/components/Quiz.vue:251
|
||||
msgid "Quiz Summary"
|
||||
msgstr ""
|
||||
msgstr "خلاصه آزمون"
|
||||
|
||||
#. Label of the quiz_title (Data) field in DocType 'LMS Quiz Submission'
|
||||
#: lms/lms/doctype/lms_quiz_submission/lms_quiz_submission.json
|
||||
msgid "Quiz Title"
|
||||
msgstr ""
|
||||
msgstr "عنوان آزمون"
|
||||
|
||||
#: frontend/src/pages/Quizzes.vue:211
|
||||
msgid "Quiz created successfully"
|
||||
@@ -5546,7 +5546,7 @@ msgstr "تکلیف زیر درس نشان داده میشود."
|
||||
#: frontend/src/pages/QuizForm.vue:398 frontend/src/pages/Quizzes.vue:285
|
||||
#: frontend/src/pages/Quizzes.vue:295 lms/www/lms.py:250
|
||||
msgid "Quizzes"
|
||||
msgstr ""
|
||||
msgstr "آزمونها"
|
||||
|
||||
#: frontend/src/pages/Quizzes.vue:233
|
||||
msgid "Quizzes deleted successfully"
|
||||
@@ -5745,7 +5745,7 @@ msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_quiz/lms_quiz.py:33
|
||||
msgid "Rows {0} have the duplicate questions."
|
||||
msgstr ""
|
||||
msgstr "ردیفهای {0} سوالات تکراری دارند."
|
||||
|
||||
#: frontend/src/pages/ProgrammingExercises/ProgrammingExerciseSubmission.vue:56
|
||||
msgid "Run"
|
||||
@@ -5930,7 +5930,7 @@ msgstr ""
|
||||
|
||||
#: frontend/src/components/AssessmentPlugin.vue:28
|
||||
msgid "Select a quiz"
|
||||
msgstr ""
|
||||
msgstr "انتخاب یک آزمون"
|
||||
|
||||
#: frontend/src/components/AssessmentPlugin.vue:35
|
||||
msgid "Select an assignment"
|
||||
@@ -6192,7 +6192,7 @@ msgstr ""
|
||||
|
||||
#: frontend/src/components/Quiz.vue:81
|
||||
msgid "Start the Quiz"
|
||||
msgstr ""
|
||||
msgstr "شروع آزمون"
|
||||
|
||||
#. Option for the 'Company Type' (Select) field in DocType 'User'
|
||||
#: lms/fixtures/custom_field.json
|
||||
@@ -6326,7 +6326,7 @@ msgstr "ارسال و ادامه"
|
||||
|
||||
#: frontend/src/components/Modals/JobApplicationModal.vue:23
|
||||
msgid "Submit your resume to proceed with your application for this position. Upon submission, it will be shared with the job poster."
|
||||
msgstr ""
|
||||
msgstr "برای ادامه درخواست برای این موقعیت شغلی، رزومه خود را ارسال کنید. پس از ارسال، رزومه با آگهیدهنده به اشتراک گذاشته خواهد شد."
|
||||
|
||||
#: frontend/src/pages/Programs/ProgramEnrollment.vue:145
|
||||
msgid "Successfully enrolled in program"
|
||||
@@ -6451,7 +6451,7 @@ msgstr ""
|
||||
|
||||
#: frontend/src/pages/QuizForm.vue:23
|
||||
msgid "Test Quiz"
|
||||
msgstr ""
|
||||
msgstr "آزمون آزمایشی"
|
||||
|
||||
#: frontend/src/pages/ProgrammingExercises/ProgrammingExerciseForm.vue:82
|
||||
msgid "Test this Exercise"
|
||||
@@ -6723,7 +6723,7 @@ msgstr "منطقه زمانی"
|
||||
|
||||
#: lms/lms/doctype/lms_course/lms_course.py:70
|
||||
msgid "Timezone is required for paid certificates."
|
||||
msgstr ""
|
||||
msgstr "منطقه زمانی برای گواهینامههای پولی الزامی است."
|
||||
|
||||
#: lms/templates/emails/batch_confirmation.html:21
|
||||
#: lms/templates/emails/batch_start_reminder.html:16
|
||||
@@ -6840,7 +6840,7 @@ msgstr "توییتر"
|
||||
#: frontend/src/components/Modals/EditProfile.vue:87
|
||||
#: lms/fixtures/custom_field.json
|
||||
msgid "Twitter ID"
|
||||
msgstr ""
|
||||
msgstr "شناسه توییتر"
|
||||
|
||||
#. Label of the type (Select) field in DocType 'Job Opportunity'
|
||||
#. Label of a field in the job-opportunity Web Form
|
||||
@@ -6976,7 +6976,7 @@ msgstr "در حال آپلود {0}%"
|
||||
#: frontend/src/components/Settings/Coupons/CouponList.vue:182
|
||||
#: lms/lms/doctype/lms_coupon/lms_coupon.json
|
||||
msgid "Usage Limit"
|
||||
msgstr ""
|
||||
msgstr "محدودیت استفاده"
|
||||
|
||||
#: lms/lms/doctype/lms_coupon/lms_coupon.py:31
|
||||
msgid "Usage limit cannot be negative"
|
||||
@@ -6984,7 +6984,7 @@ msgstr "محدودیت استفاده نمیتواند منفی باشد"
|
||||
|
||||
#: frontend/src/components/Modals/EmailTemplateModal.vue:38
|
||||
msgid "Use HTML"
|
||||
msgstr ""
|
||||
msgstr "استفاده از HTML"
|
||||
|
||||
#. Label of the user (Link) field in DocType 'LMS Job Application'
|
||||
#. Label of the user (Link) field in DocType 'LMS Course Interest'
|
||||
@@ -7366,7 +7366,7 @@ msgstr ""
|
||||
|
||||
#: frontend/src/pages/ProfileCertificates.vue:26
|
||||
msgid "You have not received any certificates yet."
|
||||
msgstr ""
|
||||
msgstr "شما هنوز هیچ گواهی دریافت نکردهاید."
|
||||
|
||||
#: lms/lms/widgets/NoPreviewModal.html:12
|
||||
msgid "You have opted to be notified for this course. You will receive an email when the course becomes available."
|
||||
@@ -7483,7 +7483,7 @@ msgstr ""
|
||||
|
||||
#: frontend/src/components/Quiz.vue:258
|
||||
msgid "Your submission has been successfully saved. The instructor will review and grade it shortly, and you'll be notified of your final result."
|
||||
msgstr ""
|
||||
msgstr "ارسال شما با موفقیت ذخیره شد. مدرس به زودی آن را بررسی و نمرهدهی خواهد کرد و نتیجه نهایی به شما اطلاع داده خواهد شد."
|
||||
|
||||
#: frontend/src/pages/Lesson.vue:8
|
||||
msgid "Zen Mode"
|
||||
@@ -7662,15 +7662,15 @@ msgstr "دانشآموزان"
|
||||
|
||||
#: frontend/src/components/CommandPalette/CommandPalette.vue:59
|
||||
msgid "to close"
|
||||
msgstr ""
|
||||
msgstr "برای بستن"
|
||||
|
||||
#: frontend/src/components/CommandPalette/CommandPalette.vue:45
|
||||
msgid "to navigate"
|
||||
msgstr ""
|
||||
msgstr "برای پیمایش"
|
||||
|
||||
#: frontend/src/components/CommandPalette/CommandPalette.vue:53
|
||||
msgid "to select"
|
||||
msgstr ""
|
||||
msgstr "برای انتخاب"
|
||||
|
||||
#: frontend/src/components/BatchFeedback.vue:12
|
||||
msgid "to view your feedback."
|
||||
@@ -7698,7 +7698,7 @@ msgstr ""
|
||||
|
||||
#: frontend/src/pages/Quizzes.vue:18
|
||||
msgid "{0} Quizzes"
|
||||
msgstr ""
|
||||
msgstr "{0} آزمون"
|
||||
|
||||
#: lms/lms/api.py:722 lms/lms/api.py:730
|
||||
msgid "{0} Settings not found"
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: frappe\n"
|
||||
"Report-Msgid-Bugs-To: jannat@frappe.io\n"
|
||||
"POT-Creation-Date: 2025-12-19 16:05+0000\n"
|
||||
"PO-Revision-Date: 2025-12-23 23:25\n"
|
||||
"PO-Revision-Date: 2025-12-24 23:25\n"
|
||||
"Last-Translator: jannat@frappe.io\n"
|
||||
"Language-Team: Portuguese, Brazilian\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -7750,7 +7750,7 @@ msgstr ""
|
||||
|
||||
#: lms/lms/api.py:771
|
||||
msgid "{0} not found"
|
||||
msgstr ""
|
||||
msgstr "{0} não encontrado"
|
||||
|
||||
#. Count format of shortcut in the LMS Workspace
|
||||
#: lms/lms/workspace/lms/lms.json
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: frappe\n"
|
||||
"Report-Msgid-Bugs-To: jannat@frappe.io\n"
|
||||
"POT-Creation-Date: 2025-12-19 16:05+0000\n"
|
||||
"PO-Revision-Date: 2025-12-23 23:25\n"
|
||||
"PO-Revision-Date: 2025-12-24 23:25\n"
|
||||
"Last-Translator: jannat@frappe.io\n"
|
||||
"Language-Team: Russian\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -7750,7 +7750,7 @@ msgstr "{0} упомянул вас в комментарии в {1}"
|
||||
|
||||
#: lms/lms/api.py:771
|
||||
msgid "{0} not found"
|
||||
msgstr "{0} не найдено"
|
||||
msgstr "{0} не найден"
|
||||
|
||||
#. Count format of shortcut in the LMS Workspace
|
||||
#: lms/lms/workspace/lms/lms.json
|
||||
|
||||
+16
-16
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: frappe\n"
|
||||
"Report-Msgid-Bugs-To: jannat@frappe.io\n"
|
||||
"POT-Creation-Date: 2025-12-19 16:05+0000\n"
|
||||
"PO-Revision-Date: 2025-12-23 23:25\n"
|
||||
"PO-Revision-Date: 2025-12-25 23:32\n"
|
||||
"Last-Translator: jannat@frappe.io\n"
|
||||
"Language-Team: Serbian (Cyrillic)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -664,7 +664,7 @@ msgstr "Доступност је успешно ажурирана"
|
||||
|
||||
#: frontend/src/components/Modals/EvaluationModal.vue:26
|
||||
msgid "Available Slots"
|
||||
msgstr ""
|
||||
msgstr "Доступни термини"
|
||||
|
||||
#: frontend/src/components/BatchFeedback.vue:43
|
||||
msgid "Average Feedback Received"
|
||||
@@ -2887,7 +2887,7 @@ msgstr "Преузми апликацију на свом иПхоне уређ
|
||||
|
||||
#: frontend/src/components/Modals/EditProfile.vue:84
|
||||
msgid "GitHub ID"
|
||||
msgstr ""
|
||||
msgstr "GitHub ID"
|
||||
|
||||
#. Label of the github (Data) field in DocType 'User'
|
||||
#: lms/fixtures/custom_field.json
|
||||
@@ -3387,7 +3387,7 @@ msgstr "Приступ остварен у"
|
||||
#: frontend/src/components/CommandPalette/CommandPalette.vue:132
|
||||
#: frontend/src/components/CommandPalette/CommandPalette.vue:229
|
||||
msgid "Jump to"
|
||||
msgstr ""
|
||||
msgstr "Иди на"
|
||||
|
||||
#: frontend/src/pages/Home/Streak.vue:18
|
||||
msgid "Keep going,"
|
||||
@@ -4503,7 +4503,7 @@ msgstr "Нема резултата"
|
||||
|
||||
#: frontend/src/components/Modals/EvaluationModal.vue:59
|
||||
msgid "No slots available for the selected course."
|
||||
msgstr ""
|
||||
msgstr "Нема доступних термина за одабрану обуку."
|
||||
|
||||
#: frontend/src/components/Modals/VideoStatistics.vue:86
|
||||
msgid "No statistics available for this video."
|
||||
@@ -4665,7 +4665,7 @@ msgstr "Отвори "
|
||||
#: frontend/src/components/UserAvatar.vue:11 frontend/src/pages/Profile.vue:61
|
||||
#: lms/fixtures/custom_field.json
|
||||
msgid "Open to Opportunities"
|
||||
msgstr ""
|
||||
msgstr "Отворен за прилике"
|
||||
|
||||
#. Label of the option (Data) field in DocType 'LMS Option'
|
||||
#: frontend/src/components/Modals/Question.vue:70
|
||||
@@ -5077,7 +5077,7 @@ msgstr "Молимо Вас да се пријавите да бисте се у
|
||||
|
||||
#: frontend/src/pages/Batch.vue:158
|
||||
msgid "Please make sure to schedule your evaluation before this date."
|
||||
msgstr ""
|
||||
msgstr "Молимо Вас да закажете своје оцењивање пре овог датума."
|
||||
|
||||
#: lms/lms/notification/certificate_request_reminder/certificate_request_reminder.html:7
|
||||
#: lms/templates/emails/certificate_request_notification.html:7
|
||||
@@ -5114,7 +5114,7 @@ msgstr "Молимо Вас да изаберете квиз"
|
||||
|
||||
#: frontend/src/components/Modals/EvaluationModal.vue:109
|
||||
msgid "Please select a slot for your evaluation."
|
||||
msgstr ""
|
||||
msgstr "Молимо Вас да одаберете термин за Ваше оцењивање."
|
||||
|
||||
#: frontend/src/components/Modals/LiveClassModal.vue:192
|
||||
msgid "Please select a time."
|
||||
@@ -5214,7 +5214,7 @@ msgstr "Пожељна локација"
|
||||
|
||||
#: frontend/src/pages/Search/Search.vue:41
|
||||
msgid "Press enter to search"
|
||||
msgstr ""
|
||||
msgstr "Притисните ентер за претрагу"
|
||||
|
||||
#. Label of the prevent_skipping_videos (Check) field in DocType 'LMS Settings'
|
||||
#: lms/lms/doctype/lms_settings/lms_settings.json
|
||||
@@ -5832,7 +5832,7 @@ msgstr "Закажите оцењивање да бисте добили сер
|
||||
|
||||
#: frontend/src/components/Modals/EvaluationModal.vue:5
|
||||
msgid "Schedule your evaluation"
|
||||
msgstr ""
|
||||
msgstr "Закажите своје оцењивање"
|
||||
|
||||
#. Name of a DocType
|
||||
#: lms/lms/doctype/scheduled_flow/scheduled_flow.json
|
||||
@@ -6048,7 +6048,7 @@ msgstr "Прикажи онлајн предавања"
|
||||
|
||||
#: frontend/src/components/Modals/EditProfile.vue:61
|
||||
msgid "Show recruiters and others that you are open to work."
|
||||
msgstr ""
|
||||
msgstr "Покажите рекрутерима и другима да сте отворени за посао."
|
||||
|
||||
#. Label of the shuffle_questions (Check) field in DocType 'LMS Quiz'
|
||||
#: frontend/src/pages/QuizForm.vue:105 lms/lms/doctype/lms_quiz/lms_quiz.json
|
||||
@@ -6501,7 +6501,7 @@ msgstr "Особа која оцењује ову обуку није досту
|
||||
|
||||
#: frontend/src/pages/Batch.vue:151
|
||||
msgid "The last day to schedule your evaluations is "
|
||||
msgstr ""
|
||||
msgstr "Последњи дан за заказивање Вашег оцењивања је "
|
||||
|
||||
#: lms/lms/utils.py:2083
|
||||
msgid "The lesson does not exist."
|
||||
@@ -6840,7 +6840,7 @@ msgstr "Twitter"
|
||||
#: frontend/src/components/Modals/EditProfile.vue:87
|
||||
#: lms/fixtures/custom_field.json
|
||||
msgid "Twitter ID"
|
||||
msgstr ""
|
||||
msgstr "Twitter ID"
|
||||
|
||||
#. Label of the type (Select) field in DocType 'Job Opportunity'
|
||||
#. Label of a field in the job-opportunity Web Form
|
||||
@@ -7394,7 +7394,7 @@ msgstr "Морате бити модератор да бисте додељив
|
||||
|
||||
#: lms/lms/doctype/lms_course_review/lms_course_review.py:18
|
||||
msgid "You must be enrolled in the course to submit a review"
|
||||
msgstr ""
|
||||
msgstr "Морате бити уписани на обуку да бисте поднели рецензију"
|
||||
|
||||
#: lms/lms/doctype/lms_enrollment/lms_enrollment.py:47
|
||||
msgid "You need to complete the payment for this course before enrolling."
|
||||
@@ -7620,11 +7620,11 @@ msgstr "онлајн предавања"
|
||||
|
||||
#: frontend/src/pages/Search/Search.vue:38
|
||||
msgid "match"
|
||||
msgstr ""
|
||||
msgstr "резултат"
|
||||
|
||||
#: frontend/src/pages/Search/Search.vue:38
|
||||
msgid "matches"
|
||||
msgstr ""
|
||||
msgstr "резултати"
|
||||
|
||||
#: frontend/src/pages/Programs/Programs.vue:42
|
||||
#: frontend/src/pages/Programs/StudentPrograms.vue:36
|
||||
|
||||
+16
-16
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: frappe\n"
|
||||
"Report-Msgid-Bugs-To: jannat@frappe.io\n"
|
||||
"POT-Creation-Date: 2025-12-19 16:05+0000\n"
|
||||
"PO-Revision-Date: 2025-12-23 23:25\n"
|
||||
"PO-Revision-Date: 2025-12-25 23:32\n"
|
||||
"Last-Translator: jannat@frappe.io\n"
|
||||
"Language-Team: Serbian (Latin)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -664,7 +664,7 @@ msgstr "Dostupnost je uspešno ažurirana"
|
||||
|
||||
#: frontend/src/components/Modals/EvaluationModal.vue:26
|
||||
msgid "Available Slots"
|
||||
msgstr ""
|
||||
msgstr "Dostupni termini"
|
||||
|
||||
#: frontend/src/components/BatchFeedback.vue:43
|
||||
msgid "Average Feedback Received"
|
||||
@@ -2887,7 +2887,7 @@ msgstr "Preuzmi aplikaciju na svom iPhone uređaju za lakši pristup i bolje kor
|
||||
|
||||
#: frontend/src/components/Modals/EditProfile.vue:84
|
||||
msgid "GitHub ID"
|
||||
msgstr ""
|
||||
msgstr "GitHub ID"
|
||||
|
||||
#. Label of the github (Data) field in DocType 'User'
|
||||
#: lms/fixtures/custom_field.json
|
||||
@@ -3387,7 +3387,7 @@ msgstr "Pristup ostvaren u"
|
||||
#: frontend/src/components/CommandPalette/CommandPalette.vue:132
|
||||
#: frontend/src/components/CommandPalette/CommandPalette.vue:229
|
||||
msgid "Jump to"
|
||||
msgstr ""
|
||||
msgstr "Idi na"
|
||||
|
||||
#: frontend/src/pages/Home/Streak.vue:18
|
||||
msgid "Keep going,"
|
||||
@@ -4503,7 +4503,7 @@ msgstr "Nema rezultata"
|
||||
|
||||
#: frontend/src/components/Modals/EvaluationModal.vue:59
|
||||
msgid "No slots available for the selected course."
|
||||
msgstr ""
|
||||
msgstr "Nema dostupnih termina za odabranu obuku."
|
||||
|
||||
#: frontend/src/components/Modals/VideoStatistics.vue:86
|
||||
msgid "No statistics available for this video."
|
||||
@@ -4665,7 +4665,7 @@ msgstr "Otvori "
|
||||
#: frontend/src/components/UserAvatar.vue:11 frontend/src/pages/Profile.vue:61
|
||||
#: lms/fixtures/custom_field.json
|
||||
msgid "Open to Opportunities"
|
||||
msgstr ""
|
||||
msgstr "Otvoren za prilike"
|
||||
|
||||
#. Label of the option (Data) field in DocType 'LMS Option'
|
||||
#: frontend/src/components/Modals/Question.vue:70
|
||||
@@ -5077,7 +5077,7 @@ msgstr "Molimo Vas da se prijavite da biste se upisali u program."
|
||||
|
||||
#: frontend/src/pages/Batch.vue:158
|
||||
msgid "Please make sure to schedule your evaluation before this date."
|
||||
msgstr ""
|
||||
msgstr "Molimo Vas da zakažete svoje ocenjivanje pre ovog datuma."
|
||||
|
||||
#: lms/lms/notification/certificate_request_reminder/certificate_request_reminder.html:7
|
||||
#: lms/templates/emails/certificate_request_notification.html:7
|
||||
@@ -5114,7 +5114,7 @@ msgstr "Molimo Vas da izaberete kviz"
|
||||
|
||||
#: frontend/src/components/Modals/EvaluationModal.vue:109
|
||||
msgid "Please select a slot for your evaluation."
|
||||
msgstr ""
|
||||
msgstr "Molimo Vas da odaberete termin za Vaše ocenjivanje."
|
||||
|
||||
#: frontend/src/components/Modals/LiveClassModal.vue:192
|
||||
msgid "Please select a time."
|
||||
@@ -5214,7 +5214,7 @@ msgstr "Poželjna lokacija"
|
||||
|
||||
#: frontend/src/pages/Search/Search.vue:41
|
||||
msgid "Press enter to search"
|
||||
msgstr ""
|
||||
msgstr "Pritisnite enter za pretragu"
|
||||
|
||||
#. Label of the prevent_skipping_videos (Check) field in DocType 'LMS Settings'
|
||||
#: lms/lms/doctype/lms_settings/lms_settings.json
|
||||
@@ -5832,7 +5832,7 @@ msgstr "Zakažite ocenjivanje da biste dobili sertifikat."
|
||||
|
||||
#: frontend/src/components/Modals/EvaluationModal.vue:5
|
||||
msgid "Schedule your evaluation"
|
||||
msgstr ""
|
||||
msgstr "Zakažite svoje ocenjivanje"
|
||||
|
||||
#. Name of a DocType
|
||||
#: lms/lms/doctype/scheduled_flow/scheduled_flow.json
|
||||
@@ -6048,7 +6048,7 @@ msgstr "Prikaži onlajn predavanja"
|
||||
|
||||
#: frontend/src/components/Modals/EditProfile.vue:61
|
||||
msgid "Show recruiters and others that you are open to work."
|
||||
msgstr ""
|
||||
msgstr "Pokažite rekruterima i drugima da ste otvoreni za posao."
|
||||
|
||||
#. Label of the shuffle_questions (Check) field in DocType 'LMS Quiz'
|
||||
#: frontend/src/pages/QuizForm.vue:105 lms/lms/doctype/lms_quiz/lms_quiz.json
|
||||
@@ -6501,7 +6501,7 @@ msgstr "Osoba koja ocenjuje ovu obuku nije dostupna u periodu od {0} do {1}. Mol
|
||||
|
||||
#: frontend/src/pages/Batch.vue:151
|
||||
msgid "The last day to schedule your evaluations is "
|
||||
msgstr ""
|
||||
msgstr "Poslednji dan za zakazivanje Vašeg ocenjivanja je "
|
||||
|
||||
#: lms/lms/utils.py:2083
|
||||
msgid "The lesson does not exist."
|
||||
@@ -6840,7 +6840,7 @@ msgstr "Twitter"
|
||||
#: frontend/src/components/Modals/EditProfile.vue:87
|
||||
#: lms/fixtures/custom_field.json
|
||||
msgid "Twitter ID"
|
||||
msgstr ""
|
||||
msgstr "Twitter ID"
|
||||
|
||||
#. Label of the type (Select) field in DocType 'Job Opportunity'
|
||||
#. Label of a field in the job-opportunity Web Form
|
||||
@@ -7394,7 +7394,7 @@ msgstr "Morate biti moderator da biste dodeljivali bedževe korisnicima."
|
||||
|
||||
#: lms/lms/doctype/lms_course_review/lms_course_review.py:18
|
||||
msgid "You must be enrolled in the course to submit a review"
|
||||
msgstr ""
|
||||
msgstr "Morate biti upisani na obuku da biste podneli recenziju"
|
||||
|
||||
#: lms/lms/doctype/lms_enrollment/lms_enrollment.py:47
|
||||
msgid "You need to complete the payment for this course before enrolling."
|
||||
@@ -7620,11 +7620,11 @@ msgstr "onlajn predavanja"
|
||||
|
||||
#: frontend/src/pages/Search/Search.vue:38
|
||||
msgid "match"
|
||||
msgstr ""
|
||||
msgstr "rezultat"
|
||||
|
||||
#: frontend/src/pages/Search/Search.vue:38
|
||||
msgid "matches"
|
||||
msgstr ""
|
||||
msgstr "rezultati"
|
||||
|
||||
#: frontend/src/pages/Programs/Programs.vue:42
|
||||
#: frontend/src/pages/Programs/StudentPrograms.vue:36
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: frappe\n"
|
||||
"Report-Msgid-Bugs-To: jannat@frappe.io\n"
|
||||
"POT-Creation-Date: 2025-12-19 16:05+0000\n"
|
||||
"PO-Revision-Date: 2025-12-23 23:25\n"
|
||||
"PO-Revision-Date: 2025-12-24 23:25\n"
|
||||
"Last-Translator: jannat@frappe.io\n"
|
||||
"Language-Team: Turkish\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -7750,7 +7750,7 @@ msgstr ""
|
||||
|
||||
#: lms/lms/api.py:771
|
||||
msgid "{0} not found"
|
||||
msgstr "{0} bulunamadı"
|
||||
msgstr ""
|
||||
|
||||
#. Count format of shortcut in the LMS Workspace
|
||||
#: lms/lms/workspace/lms/lms.json
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: frappe\n"
|
||||
"Report-Msgid-Bugs-To: jannat@frappe.io\n"
|
||||
"POT-Creation-Date: 2025-12-19 16:05+0000\n"
|
||||
"PO-Revision-Date: 2025-12-23 23:25\n"
|
||||
"PO-Revision-Date: 2025-12-24 23:25\n"
|
||||
"Last-Translator: jannat@frappe.io\n"
|
||||
"Language-Team: Chinese Simplified\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -7750,7 +7750,7 @@ msgstr "{0}在{1}的评论中提及您"
|
||||
|
||||
#: lms/lms/api.py:771
|
||||
msgid "{0} not found"
|
||||
msgstr "{0}未找到"
|
||||
msgstr "未找到{0}"
|
||||
|
||||
#. Count format of shortcut in the LMS Workspace
|
||||
#: lms/lms/workspace/lms/lms.json
|
||||
|
||||
+2
-1
@@ -113,4 +113,5 @@ lms.patches.v2_0.enable_programming_exercises_in_sidebar
|
||||
lms.patches.v2_0.count_in_program
|
||||
lms.patches.v2_0.fix_scorm_lesson_reference_idx #02-09-2025
|
||||
lms.patches.v2_0.certified_members_to_certifications #05-10-2025
|
||||
lms.patches.v2_0.fix_job_application_resume_urls
|
||||
lms.patches.v2_0.fix_job_application_resume_urls
|
||||
lms.patches.v2_0.open_to_opportunities
|
||||
@@ -0,0 +1,10 @@
|
||||
import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
looking_for_job = frappe.get_all("User", {"looking_for_job": 1}, ["name"])
|
||||
|
||||
for user in looking_for_job:
|
||||
frappe.db.set_value("User", user.name, "open_to", "Opportunities")
|
||||
|
||||
frappe.db.delete("Custom Field", {"dt": "User", "fieldname": "looking_for_job"})
|
||||
+1
-1
@@ -12,7 +12,7 @@ dependencies = [
|
||||
"websocket_client~=1.6.4",
|
||||
"markdown~=3.5.1",
|
||||
"beautifulsoup4>=4.12,<4.14",
|
||||
"lxml~=4.9.3",
|
||||
"lxml~=6.0.2",
|
||||
"cairocffi==1.5.1",
|
||||
"razorpay~=1.4.1",
|
||||
"fuzzywuzzy~=0.18.0",
|
||||
|
||||
Reference in New Issue
Block a user