refactor: certified participants list

This commit is contained in:
Jannat Patel
2026-04-10 18:06:30 +05:30
parent 2ebb6ca745
commit c7ccb2d1c5
5 changed files with 64 additions and 150 deletions

View File

@@ -182,6 +182,7 @@ watch([titleFilter, typeFilter], () => {
totalAssignments.update({
filters: assignmentFilter.value,
})
totalAssignments.reload()
})
const reloadAssignments = () => {

View File

@@ -12,9 +12,9 @@
</Button>
</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-8 px-3">
<div class="text-xl font-semibold text-ink-gray-9 mb-4 md:mb-0">
<div class="mx-auto w-full">
<div class="flex flex-col md:flex-row justify-between mb-5 px-5 pt-5">
<div class="text-lg font-semibold text-ink-gray-9 mb-4 md:mb-0">
{{ memberCount }} {{ __('Certified Members') }}
</div>
<div
@@ -56,67 +56,67 @@
</div>
</div>
</div>
<div v-if="participants.data?.length" class="">
<template v-for="(participant, index) in participants.data">
<router-link
:to="{
name: 'ProfileAbout',
params: {
username: participant.username,
},
}"
<div
v-if="participants.data?.length"
class="h-[76vh] overflow-y-auto mb-5 px-5"
>
<div class="grid grid-cols-4 gap-5">
<div
v-for="participant in participants.data"
class="flex flex-col border hover:border-outline-gray-3 rounded-lg p-3 text-ink-gray-9 cursor-pointer"
@click="
router.push({
name: 'ProfileAbout',
params: { username: participant.username },
})
"
>
<div class="rounded-md hover:bg-surface-gray-2 px-3">
<div
class="flex items-center w-full space-x-3 py-2"
:class="{
'border-b': index < participants.data.length - 1,
}"
>
<UserAvatar :user="participant" size="2xl" />
<div class="flex flex-col md:flex-row w-full">
<div class="flex-1">
<div class="text-base font-medium text-ink-gray-8">
{{ participant.full_name }}
</div>
<div
v-if="participant.headline"
class="mt-1.5 text-base text-ink-gray-5"
>
{{ participant.headline }}
</div>
</div>
<div
class="flex items-center space-x-3 md:space-x-24 text-sm md:text-base mt-1.5"
>
<div class="text-ink-gray-5">
{{ participant.certificate_count }}
{{
participant.certificate_count > 1
? __('certificates')
: __('certificate')
}}
</div>
<span class="text-ink-gray-4 md:hidden">·</span>
<div class="text-ink-gray-5">
{{ dayjs(participant.issue_date).format('DD MMM YYYY') }}
</div>
</div>
<div class="flex space-x-4">
<UserAvatar :user="participant" size="2xl" />
<div class="flex flex-col">
<div class="font-semibold mb-1">
{{ participant.full_name }}
</div>
<div class="text-sm leading-5 line-clamp-1 mb-4">
{{
participant.headline ||
'Joined ' + dayjs(participant.creation).fromNow()
}}
</div>
</div>
</div>
</router-link>
</template>
<div class="mt-auto space-y-2 text-ink-gray-7">
<div class="flex items-center space-x-1">
<GraduationCap class="h-4 w-4 stroke-1.5 mr-1" />
<span>
{{ participant.certificate_count }}
{{
participant.certificate_count > 1
? __('certificates')
: __('certificate')
}}
</span>
</div>
<div class="flex items-center space-x-1">
<Calendar class="h-4 w-4 stroke-1.5 mr-1" />
<span>{{
dayjs(participant.issue_date).format('DD MMM YYYY')
}}</span>
</div>
</div>
</div>
</div>
</div>
<EmptyState v-else type="Certified Members" />
<div
v-if="!participants.list.loading && participants.hasNextPage"
class="flex justify-center mt-5"
>
<Button @click="participants.next()">
<div class="flex items-center justify-end space-x-3 border-t pt-3 px-5">
<Button v-if="participants.hasNextPage" @click="participants.next()">
{{ __('Load More') }}
</Button>
<div v-if="participants.hasNextPage" class="h-8 border-l"></div>
<div class="text-ink-gray-5">
{{ participants.data?.length }} {{ __('of') }}
{{ memberCount }}
</div>
</div>
</div>
</template>
@@ -132,7 +132,7 @@ import {
usePageMeta,
} from 'frappe-ui'
import { computed, inject, onMounted, ref } from 'vue'
import { GraduationCap } from 'lucide-vue-next'
import { GraduationCap, Calendar } from 'lucide-vue-next'
import { sessionStore } from '../stores/session'
import { useRouter } from 'vue-router'
import EmptyState from '@/components/EmptyState.vue'
@@ -163,7 +163,6 @@ const participants = createListResource({
url: 'lms.lms.api.get_certified_participants',
start: 0,
cache: ['certified_participants'],
pageLength: 100,
})
const getMemberCount = () => {

View File

@@ -109,93 +109,6 @@
</div>
</div>
</div>
<!-- <div class="border-b mb-4 pb-5 px-5">
<div class="text-lg font-semibold mb-4 text-ink-gray-9">
{{ __('Job Details') }}
</div>
<div class="grid grid-cols-3 gap-5">
<FormControl
v-model="job.job_title"
:label="__('Title')"
:required="true"
/>
<FormControl
v-model="job.type"
:label="__('Type')"
type="select"
:options="jobTypes"
:required="true"
/>
<FormControl
v-model="job.work_mode"
:label="__('Work Mode')"
type="select"
:options="workModes"
:required="true"
/>
<FormControl
v-model="job.location"
:label="__('City')"
:required="true"
/>
<Link
v-model="job.country"
doctype="Country"
:label="__('Country')"
:required="true"
/>
<FormControl
v-if="jobName != 'new'"
v-model="job.status"
:label="__('Status')"
type="select"
:options="jobStatuses"
:required="true"
/>
</div>
</div>
<div class="border-b mb-4 pb-5 px-5">
<div class="text-lg font-semibold mb-4 text-ink-gray-9">
{{ __('Company Details') }}
</div>
<div class="grid grid-cols-3 gap-5">
<FormControl
v-model="job.company_name"
:label="__('Company Name')"
class="mb-4"
:required="true"
/>
<FormControl
v-model="job.company_website"
:label="__('Company Website')"
:required="true"
/>
<FormControl
v-model="job.company_email_address"
:label="__('Company Email Address')"
class="mb-4"
:required="true"
/>
<Uploader
v-model="job.company_logo"
:label="__('Company Logo')"
:required="true"
/>
</div>
</div>
<div class="px-5 mt-4">
<label class="block text-ink-gray-5 text-xs mb-1">
{{ __('Description') }}
<span class="text-ink-red-3">*</span>
</label>
<TextEditor
:content="job.description"
@change="(val) => (job.description = val)"
:editable="true"
:fixedMenu="true"
editorClass="prose-sm max-w-none border-b border-x border-outline-gray-modals bg-surface-gray-2 rounded-b-md py-1 px-2 min-h-[7rem] max-h-[20rem] overflow-y-auto mb-4"
/>
</div> -->
</div>
</div>
</template>

View File

@@ -203,6 +203,7 @@ const updateList = () => {
totalExercises.update({
filters: filters,
})
totalExercises.reload()
}
const getFilters = () => {

View File

@@ -344,11 +344,11 @@ def get_evaluator_details(evaluator: str):
@frappe.whitelist()
def get_certified_participants(filters: dict = None, start: int = 0, page_length: int = 100):
def get_certified_participants(filters: dict = None, start: int = 0, page_length: int = 40):
query = get_certification_query(filters)
query = query.orderby("issue_date", order=frappe.qb.desc).offset(start).limit(page_length)
participants = query.run(as_dict=True)
print(participants)
for participant in participants:
details = get_certified_participant_details(participant.member)
participant.update(details)
@@ -361,7 +361,7 @@ def get_certified_participant_details(member: str):
details = frappe.db.get_value(
"User",
member,
["full_name", "user_image", "username", "country", "headline", "open_to"],
["full_name", "user_image", "username", "creation", "headline", "open_to"],
as_dict=1,
)
details["certificate_count"] = count
@@ -374,12 +374,12 @@ def get_certification_query(filters: dict = None):
query = (
frappe.qb.from_(Certificate)
.select(Certificate.member, Certificate.issue_date)
.distinct()
.select(Certificate.member, fn.Max(Certificate.issue_date).as_("issue_date"))
.join(User)
.on(Certificate.member == User.name)
.where(Certificate.published == 1)
.where(User.enabled == 1)
.groupby(Certificate.member)
)
if filters: