Skip to content

Commit

Permalink
Merge pull request #1241 from pateljannat/batch-feedback
Browse files Browse the repository at this point in the history
feat: batch feedback
  • Loading branch information
pateljannat authored Jan 13, 2025
2 parents 4869bba + 8fe0b62 commit c096c17
Show file tree
Hide file tree
Showing 10 changed files with 453 additions and 11 deletions.
265 changes: 265 additions & 0 deletions frontend/src/components/BatchFeedback.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
<template>
<div v-if="user.data?.is_student">
<div
v-if="feedbackList.data?.length"
class="bg-blue-100 text-blue-700 p-2 rounded-md mb-5"
>
{{ __('Thank you for providing your feedback!') }}
</div>
<div v-else class="flex justify-between items-center mb-5">
<div class="text-lg font-semibold">
{{ __('Help Us Improve') }}
</div>
<Button @click="submitFeedback()">
{{ __('Submit') }}
</Button>
</div>
<div class="space-y-8">
<div class="flex items-center justify-between">
<Rating
v-model="feedback.content"
:label="__('Content')"
:readonly="readOnly"
/>
<Rating
v-model="feedback.delivery"
:label="__('Delivery')"
:readonly="readOnly"
/>
<Rating
v-model="feedback.instructors"
:label="__('Instructors')"
:readonly="readOnly"
/>
<Rating
v-model="feedback.value"
:label="__('Value')"
:readonly="readOnly"
/>
</div>
<FormControl
v-model="feedback.feedback"
type="textarea"
:label="__('Feedback')"
:rows="7"
:readonly="readOnly"
/>
</div>
</div>

<div v-else-if="feedbackList.data?.length">
<div class="text-lg font-semibold mb-5">
{{ __('Average of Feedback Received') }}
</div>

<div class="flex items-center justify-between mb-10">
<Rating
v-model="average.content"
:label="__('Content')"
:readonly="true"
/>
<Rating
v-model="average.delivery"
:label="__('Delivery')"
:readonly="true"
/>
<Rating
v-model="average.instructors"
:label="__('Instructors')"
:readonly="true"
/>
<Rating v-model="average.value" :label="__('Value')" :readonly="true" />
</div>

<div class="text-lg font-semibold mb-5">
{{ __('All Feedback') }}
</div>
<ListView
:columns="feedbackColumns"
:rows="feedbackList.data"
row-key="name"
:options="{
showTooltip: false,
rowHeight: 'h-16',
selectable: false,
}"
>
<ListHeader
class="mb-2 grid items-center space-x-4 rounded bg-gray-100 p-2"
></ListHeader>
<ListRows>
<ListRow
:row="row"
v-for="row in feedbackList.data"
class="group cursor-pointer"
>
<template #default="{ column, item }">
<ListRowItem
:item="row[column.key]"
:align="column.align"
class="text-sm"
>
<template #prefix>
<div v-if="column.key == 'member_name'">
<Avatar
class="flex items-center"
:image="row['member_image']"
:label="item"
size="sm"
/>
</div>
</template>
<div v-if="ratingKeys.includes(column.key)">
<Rating v-model="row[column.key]" :readonly="true" />
</div>
<div v-else class="leading-5">
{{ row[column.key] }}
</div>
</ListRowItem>
</template>
</ListRow>
</ListRows>
</ListView>
</div>
</template>
<script setup>
import { computed, inject, onMounted, reactive, ref, watch } from 'vue'
import {
Avatar,
Button,
createListResource,
FormControl,
ListView,
ListHeader,
ListHeaderItem,
ListRows,
ListRow,
ListRowItem,
Rating,
} from 'frappe-ui'
const user = inject('$user')
const ratingKeys = ['content', 'delivery', 'instructors', 'value']
const readOnly = ref(false)
const average = reactive({})
const feedback = reactive({})
const props = defineProps({
batch: {
type: String,
required: true,
},
})
onMounted(() => {
let filters = {
batch: props.batch,
}
if (user.data?.is_student) {
filters['member'] = user.data?.name
}
feedbackList.update({
filters: filters,
})
feedbackList.reload()
})
const feedbackList = createListResource({
doctype: 'LMS Batch Feedback',
filters: {
batch: props.batch,
},
fields: [
'content',
'delivery',
'instructors',
'value',
'feedback',
'name',
'member',
'member_name',
'member_image',
],
cache: ['feedbackList', props.batch, user.data?.name],
})
watch(
() => feedbackList.data,
() => {
if (feedbackList.data.length) {
let data = feedbackList.data
readOnly.value = true
ratingKeys.forEach((key) => {
average[key] = 0
})
data.forEach((row) => {
Object.keys(row).forEach((key) => {
if (ratingKeys.includes(key)) row[key] = row[key] * 5
feedback[key] = row[key]
})
ratingKeys.forEach((key) => {
average[key] += row[key]
})
})
Object.keys(average).forEach((key) => {
average[key] = average[key] / data.length
})
}
}
)
const submitFeedback = () => {
ratingKeys.forEach((key) => {
feedback[key] = feedback[key] / 5
})
feedbackList.insert.submit(
{
member: user.data?.name,
batch: props.batch,
...feedback,
},
{
onSuccess: () => {
feedbackList.reload()
},
}
)
}
const feedbackColumns = computed(() => {
return [
{
label: 'Member',
key: 'member_name',
width: '10rem',
},
{
label: 'Feedback',
key: 'feedback',
width: '15rem',
},
{
label: 'Content',
key: 'content',
width: '10rem',
},
{
label: 'Delivery',
key: 'delivery',
width: '10rem',
},
{
label: 'Instructors',
key: 'instructors',
width: '10rem',
},
{
label: 'Value',
key: 'value',
width: '10rem',
},
]
})
</script>
2 changes: 1 addition & 1 deletion frontend/src/components/BatchStudents.vue
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ const getChartOptions = (categories) => {
},
rotate: 0,
formatter: function (value) {
return value.length > 30 ? `${value.substring(0, 30)}...` : value // Trim long labels
return value.length > 30 ? `${value.substring(0, 30)}...` : value
},
},
},
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/JobCard.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<div class="flex rounded p-1 lg:px-2 lg:py-2.5 hover:bg-gray-100">
<div class="flex rounded p-1 lg:px-2 lg:py-4 hover:bg-gray-100">
<div class="flex w-3/5 md:w-2/5">
<img
:src="job.company_logo"
Expand Down
20 changes: 14 additions & 6 deletions frontend/src/pages/Batch.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
</Button>
</div>
</header>
<div v-if="batch.data" class="grid grid-cols-[70%,30%] h-screen">
<div v-if="batch.data" class="grid grid-cols-[75%,25%] h-screen">
<div class="border-r">
<Tabs
v-model="tabIndex"
Expand Down Expand Up @@ -65,7 +65,7 @@
<div v-else-if="tab.label == 'Dashboard'">
<BatchStudents :batch="batch.data" />
</div>
<div v-else-if="tab.label == 'Live Class'">
<div v-else-if="tab.label == 'Classes'">
<LiveClass :batch="batch.data.name" />
</div>
<div v-else-if="tab.label == 'Assessments'">
Expand All @@ -81,9 +81,12 @@
:title="__('Discussions')"
:key="batch.data.name"
:singleThread="true"
:scrollToBottom="true"
:scrollToBottom="false"
/>
</div>
<div v-else-if="tab.label == 'Feedback'">
<BatchFeedback :batch="batch.data.name" />
</div>
</div>
</template>
</Tabs>
Expand Down Expand Up @@ -190,12 +193,11 @@ import {
BookOpen,
Laptop,
BookOpenCheck,
Contact2,
Mail,
SendIcon,
MessageCircle,
Globe,
ShieldCheck,
ClipboardPen,
} from 'lucide-vue-next'
import { formatTime, updateDocumentTitle } from '@/utils'
import BatchDashboard from '@/components/BatchDashboard.vue'
Expand All @@ -208,6 +210,7 @@ import AnnouncementModal from '@/components/Modals/AnnouncementModal.vue'
import Discussions from '@/components/Discussions.vue'
import DateRange from '@/components/Common/DateRange.vue'
import BulkCertificates from '@/components/Modals/BulkCertificates.vue'
import BatchFeedback from '@/components/BatchFeedback.vue'
const user = inject('$user')
const showAnnouncementModal = ref(false)
Expand Down Expand Up @@ -271,7 +274,7 @@ const tabs = computed(() => {
})
batchTabs.push({
label: 'Live Class',
label: 'Classes',
icon: Laptop,
})
Expand All @@ -291,6 +294,11 @@ const tabs = computed(() => {
label: 'Discussions',
icon: MessageCircle,
})
batchTabs.push({
label: 'Feedback',
icon: ClipboardPen,
})
return batchTabs
})
Expand Down
10 changes: 7 additions & 3 deletions lms/lms/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -603,9 +603,13 @@ def get_categories(doctype, filters):
@frappe.whitelist()
def get_members(start=0, search=""):
"""Get members for the given search term and start index.
Args: start (int): Start index for the query.
search (str): Search term to filter the results.
Returns: List of members.
Args: start (int): Start index for the query.
<<<<<<< HEAD
search (str): Search term to filter the results.
=======
search (str): Search term to filter the results.
>>>>>>> 4869bba7bbb2fb38477d6fc29fb3b5838e075577
Returns: List of members.
"""

filters = {"enabled": 1, "name": ["not in", ["Administrator", "Guest"]]}
Expand Down
Empty file.
8 changes: 8 additions & 0 deletions lms/lms/doctype/lms_batch_feedback/lms_batch_feedback.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright (c) 2025, Frappe and contributors
// For license information, please see license.txt

// frappe.ui.form.on("LMS Batch Feedback", {
// refresh(frm) {

// },
// });
Loading

0 comments on commit c096c17

Please sign in to comment.