fix: achievements position
This commit is contained in:
@@ -140,7 +140,7 @@ const coverImage = createResource({
|
|||||||
|
|
||||||
const setActiveTab = () => {
|
const setActiveTab = () => {
|
||||||
let fragments = route.path.split('/')
|
let fragments = route.path.split('/')
|
||||||
let sections = ['certificates', 'achievements', 'roles', 'evaluations']
|
let sections = ['certificates', 'roles', 'evaluations']
|
||||||
sections.forEach((section) => {
|
sections.forEach((section) => {
|
||||||
if (fragments.includes(section)) {
|
if (fragments.includes(section)) {
|
||||||
activeTab.value = convertToTitleCase(section)
|
activeTab.value = convertToTitleCase(section)
|
||||||
@@ -154,7 +154,6 @@ watchEffect(() => {
|
|||||||
let route = {
|
let route = {
|
||||||
About: { name: 'ProfileAbout' },
|
About: { name: 'ProfileAbout' },
|
||||||
Certificates: { name: 'ProfileCertificates' },
|
Certificates: { name: 'ProfileCertificates' },
|
||||||
Achievements: { name: 'ProfileAchievements' },
|
|
||||||
Roles: { name: 'ProfileRoles' },
|
Roles: { name: 'ProfileRoles' },
|
||||||
Evaluations: { name: 'ProfileEvaluator' },
|
Evaluations: { name: 'ProfileEvaluator' },
|
||||||
}[activeTab.value]
|
}[activeTab.value]
|
||||||
@@ -171,11 +170,7 @@ const isSessionUser = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getTabButtons = () => {
|
const getTabButtons = () => {
|
||||||
let buttons = [
|
let buttons = [{ label: 'About' }, { label: 'Certificates' }]
|
||||||
{ label: 'About' },
|
|
||||||
{ label: 'Certificates' },
|
|
||||||
{ label: 'Achievements' },
|
|
||||||
]
|
|
||||||
if ($user.data?.is_moderator) buttons.push({ label: 'Roles' })
|
if ($user.data?.is_moderator) buttons.push({ label: 'Roles' })
|
||||||
if (isSessionUser() && $user.data?.is_evaluator)
|
if (isSessionUser() && $user.data?.is_evaluator)
|
||||||
buttons.push({ label: 'Evaluations' })
|
buttons.push({ label: 'Evaluations' })
|
||||||
|
|||||||
@@ -12,12 +12,92 @@
|
|||||||
{{ __('No introduction') }}
|
{{ __('No introduction') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mt-7 mb-10">
|
||||||
|
<h2 class="mb-3 text-lg font-semibold text-gray-900">
|
||||||
|
{{ __('Achievements') }}
|
||||||
|
</h2>
|
||||||
|
<div class="grid grid-cols-5 gap-4">
|
||||||
|
<div v-if="badges.data" v-for="badge in badges.data">
|
||||||
|
<Popover trigger="hover">
|
||||||
|
<template #target>
|
||||||
|
<div class="relative">
|
||||||
|
<img
|
||||||
|
:src="badge.badge_image"
|
||||||
|
:alt="badge.badge"
|
||||||
|
class="h-[80px]"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
v-if="badge.count > 1"
|
||||||
|
class="flex items-end bg-gray-100 p-2 text-xs font-semibold rounded-full absolute right-0 bottom-0"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
<X class="w-3 h-3" />
|
||||||
|
</span>
|
||||||
|
{{ badge.count }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #body-main>
|
||||||
|
<div class="w-[250px] text-base">
|
||||||
|
<img
|
||||||
|
:src="badge.badge_image"
|
||||||
|
:alt="badge.badge"
|
||||||
|
class="bg-gray-100 rounded-t-md"
|
||||||
|
/>
|
||||||
|
<div class="p-5">
|
||||||
|
<div class="text-2xl font-semibold mb-2">
|
||||||
|
{{ badge.badge }}
|
||||||
|
</div>
|
||||||
|
<div class="leading-5 mb-4">
|
||||||
|
{{ badge.badge_description }}
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<span class="text-xs text-gray-700 font-medium mb-1">
|
||||||
|
{{ __('Issued on') }}:
|
||||||
|
</span>
|
||||||
|
{{ dayjs(badge.issued_on).format('DD MMM YYYY') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { inject } from 'vue'
|
||||||
|
import { createResource, Popover } from 'frappe-ui'
|
||||||
|
import { X } from 'lucide-vue-next'
|
||||||
|
|
||||||
|
const dayjs = inject('$dayjs')
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
profile: {
|
profile: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const badges = createResource({
|
||||||
|
url: 'frappe.client.get_list',
|
||||||
|
params: {
|
||||||
|
doctype: 'LMS Badge Assignment',
|
||||||
|
fields: ['name', 'badge', 'badge_image', 'badge_description', 'issued_on'],
|
||||||
|
filters: {
|
||||||
|
member: props.profile.data.name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
auto: true,
|
||||||
|
transform(data) {
|
||||||
|
let finalBadges = []
|
||||||
|
let groupedBadges = Object.groupBy(data, ({ badge }) => badge)
|
||||||
|
for (let badge in groupedBadges) {
|
||||||
|
let badgeData = groupedBadges[badge][0]
|
||||||
|
badgeData.count = groupedBadges[badge].length
|
||||||
|
finalBadges.push(badgeData)
|
||||||
|
}
|
||||||
|
return finalBadges
|
||||||
|
},
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,89 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="mt-7 mb-10">
|
|
||||||
<h2 class="mb-3 text-lg font-semibold text-gray-900">
|
|
||||||
{{ __('Achievements') }}
|
|
||||||
</h2>
|
|
||||||
<div class="grid grid-cols-5 gap-4">
|
|
||||||
<div v-if="badges.data" v-for="badge in badges.data">
|
|
||||||
<Popover trigger="hover">
|
|
||||||
<template #target>
|
|
||||||
<div class="relative">
|
|
||||||
<img
|
|
||||||
:src="badge.badge_image"
|
|
||||||
:alt="badge.badge"
|
|
||||||
class="h-[80px]"
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
class="flex items-end bg-gray-100 p-2 text-xs font-semibold rounded-full absolute right-0 bottom-0"
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
<X class="w-3 h-3" />
|
|
||||||
</span>
|
|
||||||
{{ badge.count }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #body-main>
|
|
||||||
<div class="w-[250px] text-base">
|
|
||||||
<img
|
|
||||||
:src="badge.badge_image"
|
|
||||||
:alt="badge.badge"
|
|
||||||
class="bg-gray-100 rounded-t-md"
|
|
||||||
/>
|
|
||||||
<div class="p-5">
|
|
||||||
<div class="text-2xl font-semibold mb-2">
|
|
||||||
{{ badge.badge }}
|
|
||||||
</div>
|
|
||||||
<div class="leading-5 mb-4">
|
|
||||||
{{ badge.badge_description }}
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<span class="text-xs text-gray-700 font-medium mb-1">
|
|
||||||
{{ __('Issued on') }}:
|
|
||||||
</span>
|
|
||||||
{{ dayjs(badge.issued_on).format('DD MMM YYYY') }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</Popover>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script setup>
|
|
||||||
import { createResource, Popover } from 'frappe-ui'
|
|
||||||
import { inject } from 'vue'
|
|
||||||
import { X } from 'lucide-vue-next'
|
|
||||||
|
|
||||||
const dayjs = inject('$dayjs')
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
profile: {
|
|
||||||
type: Object,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const badges = createResource({
|
|
||||||
url: 'frappe.client.get_list',
|
|
||||||
params: {
|
|
||||||
doctype: 'LMS Badge Assignment',
|
|
||||||
fields: ['name', 'badge', 'badge_image', 'badge_description', 'issued_on'],
|
|
||||||
filters: {
|
|
||||||
member: props.profile.data.name,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
auto: true,
|
|
||||||
transform(data) {
|
|
||||||
let finalBadges = []
|
|
||||||
let groupedBadges = Object.groupBy(data, ({ badge }) => badge)
|
|
||||||
for (let badge in groupedBadges) {
|
|
||||||
let badgeData = groupedBadges[badge][0]
|
|
||||||
badgeData.count = groupedBadges[badge].length
|
|
||||||
finalBadges.push(badgeData)
|
|
||||||
}
|
|
||||||
return finalBadges
|
|
||||||
},
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
@@ -72,11 +72,6 @@ const routes = [
|
|||||||
path: 'certificates',
|
path: 'certificates',
|
||||||
component: () => import('@/pages/ProfileCertificates.vue'),
|
component: () => import('@/pages/ProfileCertificates.vue'),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'ProfileAchievements',
|
|
||||||
path: 'achievements',
|
|
||||||
component: () => import('@/pages/ProfileAchievements.vue'),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'ProfileRoles',
|
name: 'ProfileRoles',
|
||||||
path: 'roles',
|
path: 'roles',
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ def save_progress(lesson, course):
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
quiz_completed = get_quiz_progress(lesson)
|
quiz_completed = get_quiz_progress(lesson)
|
||||||
print(quiz_completed)
|
|
||||||
if not quiz_completed:
|
if not quiz_completed:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on("LMS Badge", {
|
frappe.ui.form.on("LMS Badge", {
|
||||||
|
refresh: (frm) => {
|
||||||
|
frm.events.set_fields_to_check(frm);
|
||||||
|
},
|
||||||
reference_doctype: (frm) => {
|
reference_doctype: (frm) => {
|
||||||
frm.events.set_fields_to_check(frm);
|
frm.events.set_fields_to_check(frm);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -13,13 +13,18 @@ class LMSBadge(Document):
|
|||||||
|
|
||||||
def rule_condition_satisfied(self, doc):
|
def rule_condition_satisfied(self, doc):
|
||||||
doc_before_save = doc.get_doc_before_save()
|
doc_before_save = doc.get_doc_before_save()
|
||||||
|
print(doc_before_save.as_dict())
|
||||||
|
print(doc.as_dict())
|
||||||
if self.event == "New" and doc_before_save != None:
|
if self.event == "New" and doc_before_save != None:
|
||||||
return False
|
return False
|
||||||
print("its new")
|
print("its new")
|
||||||
if self.event == "Value Change":
|
if self.event == "Value Change":
|
||||||
field_to_check = self.field_to_check
|
field_to_check = self.field_to_check
|
||||||
if not self.field_to_check:
|
print(field_to_check)
|
||||||
|
if not field_to_check:
|
||||||
return False
|
return False
|
||||||
|
print(doc_before_save.get(field_to_check))
|
||||||
|
print(doc.get(field_to_check))
|
||||||
if doc_before_save and doc_before_save.get(field_to_check) == doc.get(
|
if doc_before_save and doc_before_save.get(field_to_check) == doc.get(
|
||||||
field_to_check
|
field_to_check
|
||||||
):
|
):
|
||||||
|
|||||||
@@ -61,7 +61,7 @@
|
|||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-04-30 17:19:39.554248",
|
"modified": "2024-05-08 14:18:39.455213",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "LMS Badge Assignment",
|
"name": "LMS Badge Assignment",
|
||||||
@@ -103,6 +103,15 @@
|
|||||||
"role": "LMS Student",
|
"role": "LMS Student",
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "LMS Student",
|
||||||
|
"share": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"show_title_field_in_link": 1,
|
"show_title_field_in_link": 1,
|
||||||
|
|||||||
@@ -274,7 +274,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"make_attachments_public": 1,
|
"make_attachments_public": 1,
|
||||||
"modified": "2024-04-16 17:40:50.899368",
|
"modified": "2024-05-08 15:11:07.833094",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "LMS Course",
|
"name": "LMS Course",
|
||||||
@@ -305,7 +305,7 @@
|
|||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"search_fields": "title",
|
"search_fields": "title, tags",
|
||||||
"show_title_field_in_link": 1,
|
"show_title_field_in_link": 1,
|
||||||
"sort_field": "creation",
|
"sort_field": "creation",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
|
|||||||
@@ -1,9 +1,20 @@
|
|||||||
# Copyright (c) 2021, FOSS United and contributors
|
# Copyright (c) 2021, FOSS United and contributors
|
||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
# import frappe
|
import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
from lms.lms.utils import get_course_progress
|
||||||
|
|
||||||
|
|
||||||
class LMSCourseProgress(Document):
|
class LMSCourseProgress(Document):
|
||||||
pass
|
def after_delete(self):
|
||||||
|
progress = get_course_progress(self.course, self.member)
|
||||||
|
membership = frappe.db.get_value(
|
||||||
|
"LMS Enrollment",
|
||||||
|
{
|
||||||
|
"member": self.member,
|
||||||
|
"course": self.course,
|
||||||
|
},
|
||||||
|
"name",
|
||||||
|
)
|
||||||
|
frappe.db.set_value("LMS Enrollment", membership, "progress", progress)
|
||||||
|
|||||||
@@ -383,7 +383,7 @@ def get_course_progress(course, member=None):
|
|||||||
return 0
|
return 0
|
||||||
completed_lessons = frappe.db.count(
|
completed_lessons = frappe.db.count(
|
||||||
"LMS Course Progress",
|
"LMS Course Progress",
|
||||||
{"course": course, "owner": member or frappe.session.user, "status": "Complete"},
|
{"course": course, "member": member or frappe.session.user, "status": "Complete"},
|
||||||
)
|
)
|
||||||
precision = cint(frappe.db.get_default("float_precision")) or 3
|
precision = cint(frappe.db.get_default("float_precision")) or 3
|
||||||
return flt(((completed_lessons / lesson_count) * 100), precision)
|
return flt(((completed_lessons / lesson_count) * 100), precision)
|
||||||
|
|||||||
Reference in New Issue
Block a user