Skip to content

Commit d154b9f

Browse files
Merge pull request #7356 from christianbeeznest/GH-7214
Attendance: make an attendance complete if participating in 1 date only - refs #7214
2 parents 4ef1041 + 81e968d commit d154b9f

6 files changed

Lines changed: 246 additions & 130 deletions

File tree

assets/vue/components/attendance/AttendanceForm.vue

Lines changed: 71 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -18,43 +18,56 @@
1818
editor-id="attendance_description"
1919
/>
2020

21-
<!-- Advanced Settings (only in creation mode) -->
22-
<BaseAdvancedSettingsButton
23-
v-if="!isEditMode"
24-
v-model="showAdvancedSettings"
25-
>
26-
<div class="flex flex-row mb-4">
27-
<label class="font-semibold w-28">{{ t("Gradebook options") }}:</label>
21+
<!-- Advanced Settings (create + edit) -->
22+
<BaseAdvancedSettingsButton v-model="showAdvancedSettings">
23+
<!-- Require unique presence -->
24+
<div class="flex flex-col gap-2 mb-4">
2825
<BaseCheckbox
29-
id="attendance_qualify_gradebook"
30-
v-model="formData.qualifyGradebook"
31-
:label="t('Qualify attendance gradebook')"
32-
name="attendance_qualify_gradebook"
33-
@change="toggleGradebookOptions"
26+
id="attendance_require_unique"
27+
v-model="formData.requireUnique"
28+
:label="t('Require unique presence')"
29+
name="attendance_require_unique"
3430
/>
31+
<p class="text-xs text-gray-500">
32+
{{ t("If enabled, the gradebook will give 100% when the learner was present at least once.") }}
33+
</p>
3534
</div>
3635

37-
<div
38-
v-if="formData.qualifyGradebook"
39-
class="ml-6"
40-
>
41-
<BaseSelect
42-
v-model="formData.gradebookOption"
43-
:label="t('Select gradebook option')"
44-
:options="gradebookOptions"
45-
/>
46-
47-
<BaseInputText
48-
v-model="formData.gradebookTitle"
49-
:label="t('Gradebook column title')"
50-
/>
51-
52-
<BaseInputNumber
53-
v-model="formData.gradeWeight"
54-
:label="t('Grade weight')"
55-
:min="0"
56-
:step="0.01"
57-
/>
36+
<!-- Gradebook options (only in creation mode) -->
37+
<div v-if="!isEditMode">
38+
<div class="flex flex-row mb-4">
39+
<label class="font-semibold w-28">{{ t("Gradebook options") }}:</label>
40+
<BaseCheckbox
41+
id="attendance_qualify_gradebook"
42+
v-model="formData.qualifyGradebook"
43+
:label="t('Qualify attendance gradebook')"
44+
name="attendance_qualify_gradebook"
45+
@change="toggleGradebookOptions"
46+
/>
47+
</div>
48+
49+
<div
50+
v-if="formData.qualifyGradebook"
51+
class="ml-6"
52+
>
53+
<BaseSelect
54+
v-model="formData.gradebookOption"
55+
:label="t('Select gradebook option')"
56+
:options="gradebookOptions"
57+
/>
58+
59+
<BaseInputText
60+
v-model="formData.gradebookTitle"
61+
:label="t('Gradebook column title')"
62+
/>
63+
64+
<BaseInputNumber
65+
v-model="formData.gradeWeight"
66+
:label="t('Grade weight')"
67+
:min="0"
68+
:step="0.01"
69+
/>
70+
</div>
5871
</div>
5972
</BaseAdvancedSettingsButton>
6073

@@ -124,6 +137,7 @@ const formData = reactive({
124137
gradebookOption: null,
125138
gradebookTitle: "",
126139
gradeWeight: 0.0,
140+
requireUnique: false,
127141
})
128142
129143
const gradebookOptions = ref([])
@@ -135,12 +149,11 @@ const rules = {
135149
gradebookOption: {},
136150
gradebookTitle: {},
137151
gradeWeight: {},
152+
requireUnique: {},
138153
}
139154
140155
const v$ = useVuelidate(rules, formData)
141-
142156
const showAdvancedSettings = ref(false)
143-
144157
const isEditMode = computed(() => !!props.initialData?.id)
145158
146159
onMounted(async () => {
@@ -182,41 +195,42 @@ const submitForm = async () => {
182195
const postData = {
183196
title: formData.title,
184197
description: formData.description,
185-
parentResourceNodeId: parentResourceNodeId.value,
186-
resourceLinkList: resourceLinkList.value,
187198
sid: route.query.sid || null,
188199
cid: route.query.cid || null,
189200
attendanceQualifyTitle: formData.gradebookTitle,
190201
attendanceWeight: formData.gradeWeight,
202+
requireUnique: !!formData.requireUnique,
203+
}
204+
205+
// Only send these on create (safer)
206+
if (!props.initialData?.id) {
207+
postData.parentResourceNodeId = parentResourceNodeId.value
208+
postData.resourceLinkList = resourceLinkList.value
191209
}
192210
193211
try {
194212
if (props.initialData?.id) {
195213
await attendanceService.updateAttendance(props.initialData.id, postData)
196214
emit("backPressed", route.query)
197-
} else {
198-
const created = await attendanceService.createAttendance(postData)
199-
router.push({
200-
name: "AttendanceAddCalendarEvent",
201-
params: {
202-
node: getNodeId(created.resourceNode),
203-
id: created.iid,
204-
},
205-
query: {
206-
cid: route.query.cid,
207-
sid: route.query.sid,
208-
gid: route.query.gid,
209-
},
210-
})
215+
return
211216
}
217+
218+
const created = await attendanceService.createAttendance(postData)
219+
220+
router.push({
221+
name: "AttendanceAddCalendarEvent",
222+
params: {
223+
node: String(route.params.node),
224+
id: created.iid,
225+
},
226+
query: {
227+
cid: route.query.cid,
228+
sid: route.query.sid,
229+
gid: route.query.gid,
230+
},
231+
})
212232
} catch (error) {
213233
console.error("Error submitting attendance:", error)
214234
}
215235
}
216-
217-
function getNodeId(resourceNode) {
218-
if (!resourceNode || !resourceNode["@id"]) return 0
219-
const parts = resourceNode["@id"].split("/")
220-
return parseInt(parts[parts.length - 1])
221-
}
222236
</script>

assets/vue/views/attendance/AttendanceCreate.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const route = useRoute()
2323
const goBack = (query = {}) => {
2424
router.push({
2525
name: "AttendanceList",
26+
params: { node: String(route.params.node) },
2627
query: {
2728
...route.query,
2829
...query,

assets/vue/views/attendance/AttendanceEdit.vue

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,7 @@ import { useI18n } from "vue-i18n"
2626
import LayoutFormGeneric from "../../components/layout/LayoutFormGeneric.vue"
2727
import BaseIcon from "../../components/basecomponents/BaseIcon.vue"
2828
import DOMPurify from "dompurify"
29-
import { useCidReqStore } from "../../store/cidReq"
30-
import { storeToRefs } from "pinia"
3129
32-
const cidReqStore = useCidReqStore()
33-
const { course } = storeToRefs(cidReqStore)
3430
const { t } = useI18n()
3531
const router = useRouter()
3632
const route = useRoute()
@@ -41,7 +37,7 @@ const loading = ref(true)
4137
const goBack = (query = {}) => {
4238
router.push({
4339
name: "AttendanceList",
44-
params: { node: String(course.value?.resourceNode?.id) },
40+
params: { node: String(route.params.node) },
4541
query: { ...route.query, ...query },
4642
})
4743
}
@@ -65,6 +61,7 @@ const fetchAttendance = async () => {
6561
gradebookOption: fetchedData.gradebookOption || null,
6662
gradebookTitle: fetchedData.attendanceQualifyTitle || "",
6763
gradeWeight: fetchedData.attendanceWeight || 0.0,
64+
requireUnique: !!fetchedData.requireUnique,
6865
}
6966
} catch (error) {
7067
console.error("Error fetching attendance:", error)

assets/vue/views/attendance/AttendanceList.vue

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,20 @@ const { sid, cid, gid } = useCidReq()
7272
const parentResourceNodeId = ref(Number(route.params.node))
7373
7474
const redirectToCreateAttendance = () => {
75-
router.push({ name: "CreateAttendance", query: { cid, sid, gid } })
75+
router.push({
76+
name: "CreateAttendance",
77+
params: { node: String(route.params.node) },
78+
query: { cid, sid, gid },
79+
})
7680
}
7781
7882
const redirectToEditAttendance = (attendance) => {
7983
router.push({
8084
name: "AttendanceEditAttendance",
81-
params: { node: getNodeId(attendance.resourceNode), id: attendance.id },
85+
params: {
86+
node: String(route.params.node),
87+
id: attendance.id,
88+
},
8289
query: { cid, sid, gid },
8390
})
8491
}
@@ -145,13 +152,6 @@ const fetchAttendances = async ({ page = 1, rows = 10 } = {}) => {
145152
isLoading.value = false
146153
}
147154
}
148-
149-
function getNodeId(resourceNode) {
150-
if (!resourceNode || !resourceNode["@id"]) return 0
151-
const parts = resourceNode["@id"].split("/")
152-
return parseInt(parts[parts.length - 1])
153-
}
154-
155155
function onStudentViewChange() {
156156
fetchAttendances()
157157
}

0 commit comments

Comments
 (0)