feat: batch feedback for moderators
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<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"
|
||||
@@ -15,7 +15,7 @@
|
||||
</Button>
|
||||
</div>
|
||||
<div class="space-y-8">
|
||||
<div class="grid grid-cols-4 gap-5">
|
||||
<div class="flex items-center justify-between">
|
||||
<Rating
|
||||
v-model="feedback.content"
|
||||
:label="__('Content')"
|
||||
@@ -46,21 +46,103 @@
|
||||
/>
|
||||
</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 { inject, reactive, ref } from 'vue'
|
||||
import { Button, createListResource, FormControl, Rating } from 'frappe-ui'
|
||||
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 feedback = reactive({
|
||||
content: 0,
|
||||
delivery: 0,
|
||||
instructors: 0,
|
||||
value: 0,
|
||||
feedback: '',
|
||||
})
|
||||
const average = reactive({})
|
||||
const feedback = reactive({})
|
||||
|
||||
const props = defineProps({
|
||||
batch: {
|
||||
@@ -69,25 +151,64 @@ const props = defineProps({
|
||||
},
|
||||
})
|
||||
|
||||
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,
|
||||
member: user.data?.name,
|
||||
},
|
||||
fields: ['content', 'delivery', 'instructors', 'value', 'feedback', 'name'],
|
||||
fields: [
|
||||
'content',
|
||||
'delivery',
|
||||
'instructors',
|
||||
'value',
|
||||
'feedback',
|
||||
'name',
|
||||
'member',
|
||||
'member_name',
|
||||
'member_image',
|
||||
],
|
||||
cache: ['feedbackList', props.batch, user.data?.name],
|
||||
auto: true,
|
||||
onSuccess(data) {
|
||||
if (data.length) {
|
||||
})
|
||||
|
||||
watch(
|
||||
() => feedbackList.data,
|
||||
() => {
|
||||
if (feedbackList.data.length) {
|
||||
let data = feedbackList.data
|
||||
readOnly.value = true
|
||||
Object.keys(feedback).forEach((key) => {
|
||||
if (ratingKeys.includes(key)) feedback[key] = data[0][key] * 5
|
||||
else feedback[key] = data[0][key]
|
||||
|
||||
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) => {
|
||||
@@ -106,4 +227,39 @@ const submitFeedback = () => {
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
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>
|
||||
|
||||
@@ -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"
|
||||
@@ -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'">
|
||||
@@ -81,7 +81,7 @@
|
||||
:title="__('Discussions')"
|
||||
:key="batch.data.name"
|
||||
:singleThread="true"
|
||||
:scrollToBottom="true"
|
||||
:scrollToBottom="false"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="tab.label == 'Feedback'">
|
||||
@@ -274,7 +274,7 @@ const tabs = computed(() => {
|
||||
})
|
||||
|
||||
batchTabs.push({
|
||||
label: 'Live Class',
|
||||
label: 'Classes',
|
||||
icon: Laptop,
|
||||
})
|
||||
|
||||
@@ -295,12 +295,10 @@ const tabs = computed(() => {
|
||||
icon: MessageCircle,
|
||||
})
|
||||
|
||||
if (isStudent.value) {
|
||||
batchTabs.push({
|
||||
label: 'Feedback',
|
||||
icon: ClipboardPen,
|
||||
})
|
||||
}
|
||||
batchTabs.push({
|
||||
label: 'Feedback',
|
||||
icon: ClipboardPen,
|
||||
})
|
||||
return batchTabs
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user