feat: translations
This commit is contained in:
@@ -10,11 +10,12 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"feather-icons": "^4.28.0",
|
"feather-icons": "^4.28.0",
|
||||||
"frappe-ui": "^0.1.16",
|
"frappe-ui": "^0.1.16",
|
||||||
"vue": "^3.2.25",
|
|
||||||
"vue-router": "^4.0.12",
|
|
||||||
"tailwindcss": "^3.2.7",
|
|
||||||
"lucide-vue-next": "^0.259.0",
|
"lucide-vue-next": "^0.259.0",
|
||||||
"pinia": "^2.0.33"
|
"pinia": "^2.0.33",
|
||||||
|
"qalendar": "^3.6.1",
|
||||||
|
"tailwindcss": "^3.2.7",
|
||||||
|
"vue": "^3.2.25",
|
||||||
|
"vue-router": "^4.0.12"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^2.0.0",
|
"@vitejs/plugin-vue": "^2.0.0",
|
||||||
|
|||||||
@@ -1,67 +1,67 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col border border-gray-200 h-full rounded-md shadow-sm" style="min-height: 320px;">
|
<div class="flex flex-col border border-gray-200 h-full rounded-md shadow-sm text-base overflow-auto" style="min-height: 320px;">
|
||||||
<div class="course-image" :class="{'default-image': !course.image}" :style="{ backgroundImage: 'url(' + encodeURI(course.image) + ')' }">
|
<div class="course-image" :class="{ 'default-image': !course.image }" :style="{ backgroundImage: 'url(' + encodeURI(course.image) + ')' }">
|
||||||
<div class="flex relative top-4 left-4">
|
<div class="flex relative top-4 left-4 w-fit">
|
||||||
<div class="course-card-pills rounded-md border border-gray-200" v-for="tag in course.tags">
|
<div class="course-card-pills rounded-md border border-gray-200" v-for="tag in course.tags">
|
||||||
{{ tag }}
|
{{ tag }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!course.image" class="image-placeholder">{{ course.title[0] }}</div>
|
<div v-if="!course.image" class="image-placeholder">{{ course.title[0] }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col flex-auto p-4">
|
<div class="flex flex-col flex-auto p-4">
|
||||||
<div class="flex text-base items-center justify-between mb-2">
|
<div class="flex items-center justify-between mb-2">
|
||||||
<div v-if="course.lesson_count" class="flex items-center space-x-1 py-1">
|
<div v-if="course.lesson_count" class="flex items-center space-x-1 py-1">
|
||||||
<BookOpen class="h-4 w-4 text-gray-700" />
|
<BookOpen class="h-4 w-4 text-gray-700" />
|
||||||
<span> {{ course.lesson_count }} </span>
|
<span> {{ course.lesson_count }} </span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="course.enrollment_count" class="flex items-center space-x-1 py-1">
|
<div v-if="course.enrollment_count" class="flex items-center space-x-1 py-1">
|
||||||
<Users class="h-4 w-4 text-gray-700" />
|
<Users class="h-4 w-4 text-gray-700" />
|
||||||
<span> {{ course.enrollment_count }} </span>
|
<span> {{ course.enrollment_count }} </span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="course.avg_rating" class="flex items-center space-x-1 py-1">
|
<div v-if="course.avg_rating" class="flex items-center space-x-1 py-1">
|
||||||
<Star class="h-4 w-4 text-gray-700" />
|
<Star class="h-4 w-4 text-gray-700" />
|
||||||
<span> {{ course.avg_rating }} </span>
|
<span> {{ course.avg_rating }} </span>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="text-xl font-semibold">
|
|
||||||
{{ course.title }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="short-introduction text-base">
|
|
||||||
{{ course.short_introduction }}
|
|
||||||
</div>
|
|
||||||
<div v-if="user && course.membership" class="w-full bg-gray-200 rounded-full h-1 mb-2">
|
|
||||||
<div class="bg-gray-900 h-1 rounded-full" :style="{ width: Math.ceil(course.membership.progress) + '%' }"></div>
|
|
||||||
</div>
|
|
||||||
<div v-if="user && course.membership" class="text-sm mb-4">
|
|
||||||
{{ Math.ceil(course.membership.progress) }}% completed
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex items-center justify-between text-base mt-auto">
|
|
||||||
<div class="flex avatar-group overlap">
|
|
||||||
<div class="mr-1" :class="{'avatar-group overlap': course.instructors.length > 1}">
|
|
||||||
<UserAvatar v-for="instructor in course.instructors" :user="instructor"/>
|
|
||||||
</div>
|
</div>
|
||||||
<span v-if="course.instructors.length == 1">
|
|
||||||
{{ course.instructors[0].full_name }}
|
|
||||||
</span>
|
|
||||||
<span v-if="course.instructors.length == 2">
|
|
||||||
{{ course.instructors[0].first_name }} and {{ course.instructors[1].first_name }}
|
|
||||||
</span>
|
|
||||||
<span v-if="course.instructors.length > 2">
|
|
||||||
{{ course.instructors[0].first_name }} and {{ course.instructors.length - 1 }} others
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-base font-semibold">
|
<div class="text-xl font-semibold">
|
||||||
{{ course.price }}
|
{{ course.title }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="short-introduction">
|
||||||
|
{{ course.short_introduction }}
|
||||||
|
</div>
|
||||||
|
<div v-if="user && course.membership" class="w-full bg-gray-200 rounded-full h-1 mb-2">
|
||||||
|
<div class="bg-gray-900 h-1 rounded-full" :style="{ width: Math.ceil(course.membership.progress) + '%' }"></div>
|
||||||
|
</div>
|
||||||
|
<div v-if="user && course.membership" class="text-sm mb-4">
|
||||||
|
{{ Math.ceil(course.membership.progress) }}% completed
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center justify-between mt-auto">
|
||||||
|
<div class="flex avatar-group overlap">
|
||||||
|
<div class="mr-1" :class="{ 'avatar-group overlap': course.instructors.length > 1 }">
|
||||||
|
<UserAvatar v-for="instructor in course.instructors" :user="instructor"/>
|
||||||
|
</div>
|
||||||
|
<span v-if="course.instructors.length == 1">
|
||||||
|
{{ course.instructors[0].full_name }}
|
||||||
|
</span>
|
||||||
|
<span v-if="course.instructors.length == 2">
|
||||||
|
{{ course.instructors[0].first_name }} and {{ course.instructors[1].first_name }}
|
||||||
|
</span>
|
||||||
|
<span v-if="course.instructors.length > 2">
|
||||||
|
{{ course.instructors[0].first_name }} and {{ course.instructors.length - 1 }} others
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="font-semibold">
|
||||||
|
{{ course.price }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { BookOpen, Users, Star } from 'lucide-vue-next'
|
import { BookOpen, Users, Star } from 'lucide-vue-next'
|
||||||
@@ -139,6 +139,7 @@ const props = defineProps({
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin-bottom: 1.25rem;
|
margin: 0.25rem 0 1.25rem;
|
||||||
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
frappeRequest,
|
frappeRequest,
|
||||||
resourcesPlugin,
|
resourcesPlugin,
|
||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
|
import translationPlugin from './translation'
|
||||||
|
|
||||||
// create a pinia instance
|
// create a pinia instance
|
||||||
let pinia = createPinia()
|
let pinia = createPinia()
|
||||||
@@ -24,6 +25,7 @@ app.use(FrappeUI)
|
|||||||
app.use(pinia)
|
app.use(pinia)
|
||||||
app.use(router)
|
app.use(router)
|
||||||
app.use(resourcesPlugin)
|
app.use(resourcesPlugin)
|
||||||
|
app.use(translationPlugin)
|
||||||
|
|
||||||
app.component('Button', Button)
|
app.component('Button', Button)
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
|||||||
7
frontend/src/pages/CourseDetail.vue
Normal file
7
frontend/src/pages/CourseDetail.vue
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
Course Detail
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
</script>
|
||||||
@@ -4,24 +4,31 @@
|
|||||||
>
|
>
|
||||||
<Breadcrumbs
|
<Breadcrumbs
|
||||||
class="h-7"
|
class="h-7"
|
||||||
:items="[{ label: 'All Courses', route: { name: 'Courses' } }]"
|
:items="[{ label: __('All Courses'), route: { name: 'Courses' } }]"
|
||||||
/>
|
/>
|
||||||
<Button variant="solid">
|
<div class="flex">
|
||||||
<template #prefix>
|
<Select class="mr-2"
|
||||||
<Plus class="h-4 w-4" />
|
:options="orderOptions"
|
||||||
</template>
|
v-model="orderBy"
|
||||||
New Course
|
/>
|
||||||
</Button>
|
<Button variant="solid">
|
||||||
|
<template #prefix>
|
||||||
|
<Plus class="h-4 w-4" />
|
||||||
|
</template>
|
||||||
|
{{ __("New Course") }}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div class="mx-5 my-10">
|
<div class="mx-5 my-10">
|
||||||
<Tabs class="overflow-hidden" v-model="tabIndex" :tabs="tabs">
|
<Tabs class="overflow-hidden" v-model="tabIndex" :tabs="tabs">
|
||||||
<template #tab="{ tab, selected }">
|
<template #tab="{ tab, selected }">
|
||||||
|
<div>
|
||||||
<button
|
<button
|
||||||
class="group -mb-px flex items-center gap-2 border-b border-transparent py-2.5 text-base text-gray-600 duration-300 ease-in-out hover:border-gray-400 hover:text-gray-900"
|
class="group -mb-px flex items-center gap-2 border-b border-transparent py-2.5 text-base text-gray-600 duration-300 ease-in-out hover:border-gray-400 hover:text-gray-900"
|
||||||
:class="{ 'text-gray-900': selected }"
|
:class="{ 'text-gray-900': selected }"
|
||||||
>
|
>
|
||||||
<component v-if="tab.icon" :is="tab.icon" class="h-5" />
|
<component v-if="tab.icon" :is="tab.icon" class="h-5" />
|
||||||
{{ tab.label }}
|
{{ __(tab.label) }}
|
||||||
<Badge
|
<Badge
|
||||||
class="group-hover:bg-gray-900"
|
class="group-hover:bg-gray-900"
|
||||||
:class="[selected ? 'bg-gray-900' : 'bg-gray-600']"
|
:class="[selected ? 'bg-gray-900' : 'bg-gray-600']"
|
||||||
@@ -32,28 +39,35 @@
|
|||||||
{{ tab.count }}
|
{{ tab.count }}
|
||||||
</Badge>
|
</Badge>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</div>
|
||||||
<template #default="{ tab }">
|
</template>
|
||||||
<div class="grid grid-cols-3 gap-8 mt-5" v-if="tab.courses && tab.courses.value.length">
|
<template #default="{ tab }">
|
||||||
<div v-for="course in tab.courses.value">
|
<div class="grid grid-cols-3 gap-8 mt-5" v-if="tab.courses && tab.courses.value.length && 0">
|
||||||
<CourseCard :course="course" />
|
<router-link v-for="course in tab.courses.value" :to="{ name: 'CourseDetail', params: { course: course.name } }">
|
||||||
</div>
|
<CourseCard :course="course" />
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
<div v-else class="grid flex-1 place-items-center text-xl font-medium text-gray-500">
|
||||||
|
<div class="flex flex-col items-center justify-center mt-4">
|
||||||
|
<div>{{ __('No {0} courses found').format(tab.label.toLowerCase()) }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="grid flex-1 place-items-center text-xl font-medium text-gray-500">
|
</div>
|
||||||
<div class="flex flex-col items-center justify-center mt-4">
|
</template>
|
||||||
<div>No {{ tab.label.toLowerCase() }} courses found</div>
|
</Tabs>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</Tabs>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { createListResource, Breadcrumbs, Tabs, Badge } from 'frappe-ui';
|
import { createListResource, Breadcrumbs, Tabs, Badge, Select } from 'frappe-ui';
|
||||||
import CourseCard from '@/components/CourseCard.vue';
|
import CourseCard from '@/components/CourseCard.vue';
|
||||||
import { Plus } from 'lucide-vue-next'
|
import { Plus } from 'lucide-vue-next'
|
||||||
import { ref, computed, watchEffect } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
|
import { usersStore } from '@/stores/user'
|
||||||
|
import { sessionStore } from '@/stores/session'
|
||||||
|
|
||||||
|
const { isLoggedIn } = sessionStore()
|
||||||
|
const { getUser } = usersStore()
|
||||||
|
const user = computed(() => isLoggedIn && getUser())
|
||||||
|
|
||||||
const courses = createListResource({
|
const courses = createListResource({
|
||||||
type: 'list',
|
type: 'list',
|
||||||
@@ -61,36 +75,75 @@ const courses = createListResource({
|
|||||||
doctype: 'LMS Course',
|
doctype: 'LMS Course',
|
||||||
url: "lms.lms.utils.get_courses",
|
url: "lms.lms.utils.get_courses",
|
||||||
auto: true,
|
auto: true,
|
||||||
})
|
});
|
||||||
|
|
||||||
const tabIndex = ref(0)
|
const tabIndex = ref(0)
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{
|
{
|
||||||
label: 'Live',
|
label: 'Live',
|
||||||
courses: computed(() => {
|
courses: computed(() => courses.data?.live || []),
|
||||||
return courses.data?.live || []
|
|
||||||
}),
|
|
||||||
count: computed(() => courses.data?.live?.length),
|
count: computed(() => courses.data?.live?.length),
|
||||||
|
show: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Upcoming',
|
label: 'Upcoming',
|
||||||
courses: computed(() => courses.data?.upcoming),
|
courses: computed(() => courses.data?.upcoming),
|
||||||
count: computed(() => courses.data?.upcoming?.length),
|
count: computed(() => courses.data?.upcoming?.length),
|
||||||
|
show: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Enrolled',
|
label: 'Enrolled',
|
||||||
courses: computed(() => courses.data?.enrolled),
|
courses: computed(() => courses.data?.enrolled),
|
||||||
count: computed(() => courses.data?.enrolled?.length),
|
count: computed(() => courses.data?.enrolled?.length),
|
||||||
|
show: user
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Created',
|
label: 'Created',
|
||||||
courses: computed(() => courses.data?.created),
|
courses: computed(() => courses.data?.created),
|
||||||
count: computed(() => courses.data?.created?.length),
|
count: computed(() => courses.data?.created?.length),
|
||||||
|
show: computed(() => user && (user.roles.includes('Course Creator') || user.roles.includes('Moderator')))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Under Review',
|
label: 'Under Review',
|
||||||
courses: computed(() => courses.data?.under_review),
|
courses: computed(() => courses.data?.under_review),
|
||||||
count: computed(() => courses.data?.under_review?.length),
|
count: computed(() => courses.data?.under_review?.length),
|
||||||
|
show: computed(() => user && user.roles.includes('Moderator'))
|
||||||
},
|
},
|
||||||
]
|
];
|
||||||
|
|
||||||
|
const orderOptions = [
|
||||||
|
{
|
||||||
|
label: "Sort By",
|
||||||
|
disabled: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Most Popular",
|
||||||
|
value: "enrollment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Highest Rated",
|
||||||
|
value: "rating"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Newest",
|
||||||
|
value: "creation"
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const orderBy = 'enrollment';
|
||||||
|
|
||||||
|
function sort_courses(order) {
|
||||||
|
const categories = ['live', 'upcoming', 'enrolled', 'created', 'under_review'];
|
||||||
|
categories.forEach(category => {
|
||||||
|
console.log(courses.data)
|
||||||
|
courses.data[category] = courses.data[category].sort((a, b) => {
|
||||||
|
if (order === 'enrollment') {
|
||||||
|
return b.enrollment_count - a.enrollment_count;
|
||||||
|
} else if (order === 'rating') {
|
||||||
|
return b.avg_rating - a.avg_rating;
|
||||||
|
} else if (order === 'newest') {
|
||||||
|
return new Date(b.creation).getTime() - new Date(a.creation).getTime();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -13,6 +13,12 @@ const routes = [
|
|||||||
name: 'Courses',
|
name: 'Courses',
|
||||||
component: () => import('@/pages/Courses.vue'),
|
component: () => import('@/pages/Courses.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/courses/:course',
|
||||||
|
name: 'CourseDetail',
|
||||||
|
component: () => import('@/pages/CourseDetail.vue'),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
let router = createRouter({
|
let router = createRouter({
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ export const usersStore = defineStore('lms-users', () => {
|
|||||||
email: email,
|
email: email,
|
||||||
full_name: email.split('@')[0],
|
full_name: email.split('@')[0],
|
||||||
user_image: null,
|
user_image: null,
|
||||||
|
roles: ['LMS Student'],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return usersByName[email]
|
return usersByName[email]
|
||||||
|
|||||||
53
frontend/src/translation.js
Normal file
53
frontend/src/translation.js
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import { createResource } from 'frappe-ui'
|
||||||
|
export default function translationPlugin(app) {
|
||||||
|
app.config.globalProperties.__ = translate
|
||||||
|
// fetch translations
|
||||||
|
|
||||||
|
if (!window.translatedMessages)
|
||||||
|
fetchTranslations().then((translations) => {
|
||||||
|
window.translatedMessages = translations
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function translate(message) {
|
||||||
|
let lang = window.lang || 'hi'
|
||||||
|
let translatedMessage = /* window.translatedMessages[message] || */ message
|
||||||
|
const hasPlaceholders = /{\d+}/.test(message)
|
||||||
|
|
||||||
|
console.log(translatedMessage)
|
||||||
|
console.log(hasPlaceholders)
|
||||||
|
if (!hasPlaceholders) {
|
||||||
|
return translatedMessage
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
format: function (...args) {
|
||||||
|
return translatedMessage.replace(
|
||||||
|
/{(\d+)}/g,
|
||||||
|
function (match, number) {
|
||||||
|
console.log(match, number)
|
||||||
|
console.log(args[number])
|
||||||
|
|
||||||
|
return typeof args[number] != 'undefined'
|
||||||
|
? args[number]
|
||||||
|
: match
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchTranslations() {
|
||||||
|
let lang = window.lang || 'hi'
|
||||||
|
let translations = await createResource({
|
||||||
|
url: 'lms.lms.api.get_translations',
|
||||||
|
cache: 'translations',
|
||||||
|
auto: true,
|
||||||
|
})
|
||||||
|
let translatedMessages = {}
|
||||||
|
console.log(translations.data)
|
||||||
|
translations.forEach((translation) => {
|
||||||
|
translatedMessages[translation.source_text] =
|
||||||
|
translation.translated_text
|
||||||
|
})
|
||||||
|
window.translatedMessages = translatedMessages
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,7 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
|
from frappe.translate import get_all_translations
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@@ -143,7 +144,6 @@ def add_mentor_to_subgroup(subgroup, email):
|
|||||||
|
|
||||||
@frappe.whitelist(allow_guest=True)
|
@frappe.whitelist(allow_guest=True)
|
||||||
def get_user_info(user=None):
|
def get_user_info(user=None):
|
||||||
print(user)
|
|
||||||
if frappe.session.user == "Guest":
|
if frappe.session.user == "Guest":
|
||||||
frappe.throw("Authentication failed", exc=frappe.AuthenticationError)
|
frappe.throw("Authentication failed", exc=frappe.AuthenticationError)
|
||||||
filters = {}
|
filters = {}
|
||||||
@@ -157,5 +157,16 @@ def get_user_info(user=None):
|
|||||||
order_by="full_name asc",
|
order_by="full_name asc",
|
||||||
distinct=True,
|
distinct=True,
|
||||||
).run(as_dict=1)
|
).run(as_dict=1)
|
||||||
print(users)
|
user["roles"] = frappe.get_roles(user.name)
|
||||||
return users
|
return users
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_translations():
|
||||||
|
if frappe.session.user != "Guest":
|
||||||
|
language = frappe.db.get_value("User", frappe.session.user, "language")
|
||||||
|
else:
|
||||||
|
language = frappe.db.get_single_value("System Settings", "language")
|
||||||
|
print("language", language)
|
||||||
|
print(get_all_translations(language))
|
||||||
|
return get_all_translations(language)
|
||||||
|
|||||||
@@ -1166,7 +1166,6 @@ def get_courses():
|
|||||||
"course_price",
|
"course_price",
|
||||||
"currency",
|
"currency",
|
||||||
],
|
],
|
||||||
filters={"published": True},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
courses = get_course_details(courses)
|
courses = get_course_details(courses)
|
||||||
@@ -1212,16 +1211,21 @@ def get_categorized_courses(courses):
|
|||||||
live, upcoming, enrolled, created, under_review = [], [], [], [], []
|
live, upcoming, enrolled, created, under_review = [], [], [], [], []
|
||||||
|
|
||||||
for course in courses:
|
for course in courses:
|
||||||
if course.upcoming:
|
if course.status == "Under Review":
|
||||||
|
under_review.append(course)
|
||||||
|
elif course.published and course.upcoming:
|
||||||
upcoming.append(course)
|
upcoming.append(course)
|
||||||
elif course.published:
|
elif course.published:
|
||||||
live.append(course)
|
live.append(course)
|
||||||
elif course.membership:
|
|
||||||
|
if course.membership and course.published:
|
||||||
enrolled.append(course)
|
enrolled.append(course)
|
||||||
elif course.is_instructor:
|
elif course.is_instructor:
|
||||||
created.append(course)
|
created.append(course)
|
||||||
elif course.status == "Under Review":
|
|
||||||
under_review.append(course)
|
categories = [live, upcoming, enrolled, created, under_review]
|
||||||
|
for category in categories:
|
||||||
|
category.sort(key=lambda x: x.enrollment_count, reverse=True)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"live": live,
|
"live": live,
|
||||||
|
|||||||
Reference in New Issue
Block a user