Skip to content

Commit 923177c

Browse files
authored
Merge pull request #14419 from akolson/hid-mark-attendance-button
Hide mark attendance button when no learners are enrolled
2 parents ecd7836 + 9a05f33 commit 923177c

File tree

3 files changed

+54
-7
lines changed

3 files changed

+54
-7
lines changed

kolibri/plugins/coach/frontend/views/home/HomePage/AttendanceBlock.vue

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
{{ attendanceLabel$() }}
1010
</template>
1111

12-
<div>
12+
<div v-if="learners.length">
1313
<KRouterLink
1414
:text="markAttendanceAction$()"
1515
:primary="true"
@@ -20,9 +20,14 @@
2020

2121
<KCircularLoader v-if="loading" />
2222

23-
<p v-else-if="sessions.length === 0">
24-
{{ noSessionsMessage$() }}
25-
</p>
23+
<div v-else-if="sessions.length === 0">
24+
<p v-if="learners.length">
25+
{{ noSessionsMessage$() }}
26+
</p>
27+
<p v-else>
28+
{{ noSessionsEnrollMessage$() }}
29+
</p>
30+
</div>
2631

2732
<template v-else>
2833
<BlockItem
@@ -100,6 +105,7 @@
100105
noSessionsMessage$,
101106
presentCount$,
102107
absentCount$,
108+
noSessionsEnrollMessage$,
103109
} = attendanceStrings;
104110
105111
const loading = ref(true);
@@ -108,6 +114,8 @@
108114
const presentColor = palette.green.v_500;
109115
const absentColor = palette.red.v_500;
110116
117+
const learners = computed(() => store.getters['classSummary/learners']);
118+
111119
onMounted(() => {
112120
fetchRecentSessions(classId.value)
113121
.catch(error => {
@@ -152,6 +160,8 @@
152160
presentCount$,
153161
absentCount$,
154162
PageNames,
163+
learners,
164+
noSessionsEnrollMessage$,
155165
};
156166
},
157167
};

kolibri/plugins/coach/frontend/views/home/HomePage/__tests__/AttendanceBlock.spec.js

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import VueRouter from 'vue-router';
33
import { ref } from 'vue';
44
import store from 'kolibri/store';
55
import makeStore from '../../../../__tests__/utils/makeStore';
6+
import classSummaryModule from '../../../../modules/classSummary';
67
// eslint-disable-next-line import/named
78
import { useAttendance, useAttendanceMock } from '../../../../composables/useAttendance';
89
import AttendanceBlock from '../AttendanceBlock.vue';
@@ -69,7 +70,17 @@ const STUBS = {
6970
KLabeledIcon: { name: 'KLabeledIcon', template: '<span><slot /></span>' },
7071
};
7172

72-
function makeWrapper({ sessions = [], pendingFetch = false, rejectWith = null } = {}) {
73+
const MOCK_LEARNERS = [
74+
{ id: 'learner-1', name: 'Learner One', username: 'learner1' },
75+
{ id: 'learner-2', name: 'Learner Two', username: 'learner2' },
76+
];
77+
78+
function makeWrapper({
79+
sessions = [],
80+
learners = MOCK_LEARNERS,
81+
pendingFetch = false,
82+
rejectWith = null,
83+
} = {}) {
7384
const recentSessions = ref(sessions);
7485
let fetchRecentSessions;
7586
if (rejectWith) {
@@ -88,6 +99,19 @@ function makeWrapper({ sessions = [], pendingFetch = false, rejectWith = null }
8899

89100
const testStore = makeStore();
90101

102+
// Populate learnerMap so the component's learners computed property works
103+
const learnerMap = {};
104+
learners.forEach(l => {
105+
learnerMap[l.id] = l;
106+
});
107+
testStore.state.classSummary.learnerMap = learnerMap;
108+
109+
// Ensure the classSummary module is registered on the singleton store
110+
// so that store.getters['classSummary/learners'] is available
111+
if (!store.hasModule('classSummary')) {
112+
store.registerModule('classSummary', classSummaryModule);
113+
}
114+
91115
// Point the kolibri/store singleton state at the test store state
92116
store.replaceState(testStore.state);
93117

@@ -122,10 +146,18 @@ describe('AttendanceBlock', () => {
122146
expect(wrapper.find('[data-test="loader"]').exists()).toBe(true);
123147
});
124148

125-
it('renders empty state when no sessions exist', async () => {
126-
const { wrapper } = makeWrapper({ sessions: [] });
149+
it('renders empty state when no sessions exist but learners are enrolled', async () => {
150+
const { wrapper } = makeWrapper({ sessions: [], learners: MOCK_LEARNERS });
151+
await global.flushPromises();
152+
expect(wrapper.text()).toContain('No attendance sessions yet');
153+
expect(wrapper.text()).not.toContain('Enroll learners');
154+
});
155+
156+
it('renders enroll message when no sessions and no learners are enrolled', async () => {
157+
const { wrapper } = makeWrapper({ sessions: [], learners: [] });
127158
await global.flushPromises();
128159
expect(wrapper.text()).toContain('No attendance sessions yet');
160+
expect(wrapper.text()).toContain('Enroll learners to mark attendance');
129161
});
130162

131163
it('renders sessions with present and absent counts', async () => {

packages/kolibri-common/strings/attendanceStrings.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,4 +174,9 @@ export const attendanceStrings = createTranslator('AttendanceStrings', {
174174
message: 'Absent',
175175
context: 'Column header for the count of learners absent',
176176
},
177+
noSessionsEnrollMessage: {
178+
message: 'No attendance sessions yet. Enroll learners to mark attendance',
179+
context:
180+
'Empty state message when no attendance sessions exist and no learners are enrolled in the class',
181+
},
177182
});

0 commit comments

Comments
 (0)