1
1
<template >
2
- <div class =" flex items-center justify-between mb-4" >
3
- <div class =" text-lg font-semibold" >
4
- {{ __('Students') }}
2
+ <div >
3
+ <!-- <Bar v-if="chartData" :data="chartData" :options="chartOptions" /> -->
4
+ <ApexChart
5
+ v-if =" chartData"
6
+ :options =" chartOptions"
7
+ :series =" chartData"
8
+ type =" bar"
9
+ height =" 350"
10
+ />
11
+ <div class =" flex items-center justify-between mb-4" >
12
+ <div class =" text-lg font-semibold" >
13
+ {{ __('Students') }}
14
+ </div >
15
+ <Button @click =" openStudentModal()" >
16
+ <template #prefix >
17
+ <Plus class =" h-4 w-4" />
18
+ </template >
19
+ {{ __('Add') }}
20
+ </Button >
5
21
</div >
6
- <Button @click =" openStudentModal()" >
7
- <template #prefix >
8
- <Plus class =" h-4 w-4" />
9
- </template >
10
- {{ __('Add') }}
11
- </Button >
12
22
</div >
13
23
<div v-if =" students.data?.length" >
14
24
<ListView
15
25
:columns =" getStudentColumns()"
16
26
:rows =" students.data"
17
27
row-key =" name"
18
- :options =" { showTooltip: false }"
28
+ :options =" {
29
+ showTooltip: false,
30
+ onRowClick: (row) => {
31
+ openStudentProgressModal(row)
32
+ },
33
+ }"
19
34
>
20
35
<ListHeader
21
36
class =" mb-2 grid items-center space-x-4 rounded bg-gray-100 p-2"
48
63
/>
49
64
</div >
50
65
</template >
51
- <div v-if =" column.key == 'courses'" >
66
+ <div
67
+ v-if =" column.key == 'progress'"
68
+ class =" flex items-center space-x-4 w-full"
69
+ >
70
+ <ProgressBar :progress =" row[column.key]" size =" sm" />
71
+ </div >
72
+ <div v-else >
52
73
{{ row[column.key] }}
53
74
</div >
54
- <div v-else-if =" column.icon == 'book-open'" >
75
+ <!-- < div v-else-if="column.icon == 'book-open'">
55
76
{{ Math.ceil(row.courses[column.key]) }}
56
77
</div>
57
78
<div v-else-if="column.icon == 'help-circle'">
63
84
{{ row.assessments[column.key] }}
64
85
</Badge>
65
86
<div v-else>{{ parseInt(row.assessments[column.key]) }}</div>
66
- </div >
87
+ </div> -->
67
88
</ListRowItem >
68
89
</template >
69
90
</ListRow >
90
111
v-model =" showStudentModal"
91
112
v-model:reloadStudents =" students"
92
113
/>
114
+ <BatchStudentProgress
115
+ :student =" selectedStudent"
116
+ v-model =" showStudentProgressModal"
117
+ />
93
118
</template >
94
119
<script setup>
95
120
import {
96
121
Avatar ,
97
- Badge ,
98
122
Button ,
99
123
createResource ,
100
124
FeatherIcon ,
@@ -107,11 +131,38 @@ import {
107
131
ListRowItem ,
108
132
} from ' frappe-ui'
109
133
import { Trash2 , Plus } from ' lucide-vue-next'
110
- import { ref } from ' vue'
134
+ import { computed , ref } from ' vue'
111
135
import StudentModal from ' @/components/Modals/StudentModal.vue'
112
136
import { showToast } from ' @/utils'
137
+ import ProgressBar from ' @/components/ProgressBar.vue'
138
+ import BatchStudentProgress from ' @/components/Modals/BatchStudentProgress.vue'
139
+ import { Bar } from ' vue-chartjs'
140
+ import {
141
+ Chart as ChartJS ,
142
+ Title ,
143
+ Tooltip ,
144
+ Legend ,
145
+ BarElement ,
146
+ CategoryScale ,
147
+ LinearScale ,
148
+ Filler ,
149
+ } from ' chart.js'
150
+ ChartJS .register (
151
+ Title,
152
+ Tooltip,
153
+ Legend,
154
+ BarElement,
155
+ CategoryScale,
156
+ LinearScale,
157
+ Filler
158
+ )
159
+ import ApexChart from ' vue3-apexcharts'
113
160
114
161
const showStudentModal = ref (false )
162
+ const showStudentProgressModal = ref (false )
163
+ const selectedStudent = ref (null )
164
+ const chartData = ref (null )
165
+ const chartOptions = ref (null )
115
166
116
167
const props = defineProps ({
117
168
batch: {
@@ -127,50 +178,47 @@ const students = createResource({
127
178
batch: props .batch ,
128
179
},
129
180
auto: true ,
181
+ onSuccess (data ) {
182
+ chartData .value = getChartData ()
183
+ console .log (chartData .value )
184
+ },
130
185
})
131
186
132
187
const getStudentColumns = () => {
133
188
let columns = [
134
189
{
135
190
label: ' Full Name' ,
136
191
key: ' full_name' ,
192
+ width: ' 20rem' ,
193
+ icon: ' user' ,
194
+ },
195
+ {
196
+ label: ' Progress' ,
197
+ key: ' progress' ,
198
+ width: ' 10rem' ,
199
+ icon: ' activity' ,
200
+ },
201
+ {
202
+ label: ' Last Active' ,
203
+ key: ' last_active' ,
137
204
width: ' 15rem' ,
205
+ align: ' center' ,
206
+ icon: ' clock' ,
138
207
},
139
208
]
140
209
141
- if (students .data ? .[0 ].assessments ) {
142
- Object .keys (students .data ? .[0 ].assessments ).forEach ((assessment ) => {
143
- columns .push ({
144
- label: assessment,
145
- key: assessment,
146
- width: ' 10rem' ,
147
- icon: ' help-circle' ,
148
- align: isAssignment (students .data ? .[0 ].assessments [assessment])
149
- ? ' left'
150
- : ' center' ,
151
- })
152
- })
153
- }
154
-
155
- if (students .data ? .[0 ].courses ) {
156
- Object .keys (students .data ? .[0 ].courses ).forEach ((course ) => {
157
- columns .push ({
158
- label: course,
159
- key: course,
160
- width: ' 10rem' ,
161
- icon: ' book-open' ,
162
- align: ' center' ,
163
- })
164
- })
165
- }
166
-
167
210
return columns
168
211
}
169
212
170
213
const openStudentModal = () => {
171
214
showStudentModal .value = true
172
215
}
173
216
217
+ const openStudentProgressModal = (row ) => {
218
+ showStudentProgressModal .value = true
219
+ selectedStudent .value = row
220
+ }
221
+
174
222
const deleteStudents = createResource ({
175
223
url: ' lms.lms.api.delete_documents' ,
176
224
makeParams (values ) {
@@ -196,17 +244,141 @@ const removeStudents = (selections, unselectAll) => {
196
244
)
197
245
}
198
246
199
- const getStatusTheme = (status ) => {
200
- if (status === ' Pass' ) {
201
- return ' green'
202
- } else if (status == ' Not Graded' ) {
203
- return ' orange'
204
- } else {
205
- return ' red'
206
- }
247
+ const getChartData = () => {
248
+ console .log (' called' )
249
+
250
+ let categories = {}
251
+
252
+ // Initialize categories with categories
253
+ Object .keys (students .data ? .[0 ].courses ).forEach ((course ) => {
254
+ categories[course] = {
255
+ value: 0 ,
256
+ type: ' course' ,
257
+ }
258
+ })
259
+
260
+ Object .keys (students .data ? .[0 ].assessments ).forEach ((assessment ) => {
261
+ categories[assessment] = {
262
+ value: 0 ,
263
+ type: ' assessment' ,
264
+ }
265
+ })
266
+
267
+ // Populate data
268
+ students .data .forEach ((student ) => {
269
+ Object .keys (student .courses ).forEach ((course ) => {
270
+ if (student .courses [course] === 100 ) {
271
+ categories[course].value += 1
272
+ }
273
+ })
274
+
275
+ Object .keys (student .assessments ).forEach ((assessment ) => {
276
+ if (student .assessments [assessment] === 100 ) {
277
+ categories[assessment].value += 1
278
+ }
279
+ })
280
+ })
281
+
282
+ // Transform data for ApexCharts
283
+ console .log (Object .values (categories).map ((item ) => item .value ))
284
+ chartOptions .value = getChartOptions (categories)
285
+ return [
286
+ {
287
+ name: __ (' Student Progress' ),
288
+ data: Object .values (categories).map ((item ) => item .value ),
289
+ /* colors: Object.values(categories).map(item =>
290
+ item.type === 'course' ? courseColor : assessmentColor
291
+ ), */
292
+ },
293
+ ]
207
294
}
208
295
209
- const isAssignment = (value ) => {
210
- return isNaN (value)
296
+ /* const chartOptions = computed(() => {
297
+ return {
298
+ responsive: true,
299
+ fill: true,
300
+ scales: {
301
+ x: {
302
+ ticks: {
303
+ maxRotation: 0,
304
+ minRotation: 0,
305
+ autoSkip: false,
306
+ }
307
+ },
308
+ y: {
309
+ beginAtZero: true,
310
+ max: students.data?.length,
311
+ ticks: {
312
+ stepSize: 5,
313
+ },
314
+ },
315
+ },
316
+ plugins: {
317
+ legends: {
318
+ display: false,
319
+ title: {
320
+ text: __("Student Progress 1111"),
321
+ }
322
+ },
323
+ title: {
324
+ display: true,
325
+ text: __("Student Progress"),
326
+ font: {
327
+ size: 14,
328
+ weight: '500',
329
+ },
330
+ color: '#171717',
331
+ }
332
+ }
333
+ }
334
+ }) */
335
+
336
+ const chartSeries = ref ([
337
+ {
338
+ name: ' Courses' ,
339
+ data: [20 , 30 , 50 ], // Example data for courses
340
+ },
341
+ {
342
+ name: ' Assessments' ,
343
+ data: [10 , 40 , 60 ], // Example data for assessments
344
+ },
345
+ ])
346
+
347
+ const getChartOptions = (categories ) => {
348
+ const courseColor = ' #3498db' // Blue for courses
349
+ const assessmentColor = ' #e74c3c' // Red for assessments
350
+ return {
351
+ chart: {
352
+ type: ' bar' ,
353
+ height: 350 ,
354
+ },
355
+ plotOptions: {
356
+ bar: {
357
+ distributed: true , // Allows individual bar colors
358
+ borderRadius: 0 ,
359
+ horizontal: false , // Set to true for horizontal bars
360
+ columnWidth: ' 30%' ,
361
+ },
362
+ },
363
+ colors: Object .values (categories).map ((item ) =>
364
+ item .type === ' course' ? courseColor : assessmentColor
365
+ ),
366
+ legends: {
367
+ show: true ,
368
+ },
369
+ xaxis: {
370
+ categories: Object .keys (categories),
371
+ labels: {
372
+ style: {
373
+ fontSize: ' 10px' ,
374
+ },
375
+ rotate: 0 ,
376
+ formatter : function (value ) {
377
+ console .log (value)
378
+ return value .length > 20 ? ` ${ value .substring (0 , 20 )} ...` : value // Trim long labels
379
+ },
380
+ },
381
+ },
382
+ }
211
383
}
212
384
< / script>
0 commit comments