Skip to content

Commit

Permalink
dodano komentarze
Browse files Browse the repository at this point in the history
  • Loading branch information
Acors24 committed Dec 23, 2024
1 parent 5e52060 commit 984c7c2
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 1 deletion.
22 changes: 21 additions & 1 deletion zapisy/apps/theses/assets/components/StudentFilter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ export default Vue.extend({
students: [] as MultiselectFilterData<number>,
};
},
mounted: async function () {
/**
* Load assigned students, if there are any
*/
mounted: function () {
const djangoField = document.getElementById(
"id_students"
) as HTMLSelectElement | null;
Expand All @@ -31,17 +34,21 @@ export default Vue.extend({
const options = djangoField.options;
if (options.length === 0) {
// No one assigned
return;
}
// Map each student from <select> data to MultiSelectFilter data
const assigned_students: MultiselectFilterData<number> = Array.from(
options
).map((element) => ({
value: Number(element.value),
label: element.text,
}));
// Store the assigned students to display them after MultiSelectFilter gains focus
this.students = assigned_students;
// Mark the students as selected in MultiSelectFilter
const filter = this.$refs["student-filter"] as Vue &
MultiSelectFilterWithSelected;
if (filter) {
Expand All @@ -50,12 +57,15 @@ export default Vue.extend({
},
methods: {
onSelect: function (selectedIds: number[]) {
// Update the server <select>
this.updateDjangoField(selectedIds);
},
clearData: function () {
const filter = this.$refs["student-filter"] as Vue &
MultiSelectFilterWithSelected;
if (filter) {
// Leave only assigned students in the dropdown list
this.students = Array.from(filter.selected);
}
},
Expand All @@ -68,13 +78,16 @@ export default Vue.extend({
}
const optionArray = Array.from(djangoField.options);
// Find the newly-selected item
const newId = selectedIds.find((id) =>
optionArray.every((option) => option.value !== String(id))
);
// Find the unselected item
const removedIndex = optionArray.findIndex(
(option) => !selectedIds.includes(Number(option.value))
);
// If a new item was selected, add it to the server <select>
if (newId !== undefined) {
const newOption = document.createElement("option");
newOption.value = newId.toString();
Expand All @@ -83,6 +96,7 @@ export default Vue.extend({
djangoField.options.add(newOption);
}
// If an item was unselected, remove it from the server <select>
if (removedIndex !== -1) {
djangoField.options.remove(removedIndex);
}
Expand All @@ -98,27 +112,33 @@ export default Vue.extend({
throw new Error("#ajax-url not found.");
}
// Get the endpoint URI
const ajaxUrl = ajaxUrlInput.value;
const urlSafeSubstring = encodeURIComponent(substring);
// Fetch students matching the query
const response = await fetch(`${ajaxUrl}/${urlSafeSubstring}`);
return response.json();
},
onSearchChange: debounce(function (
this: { updateStudents: (search: string) => void },
search: string
) {
// Update the dropdown list after changing the search input
return this.updateStudents(search);
},
300),
updateStudents: async function (search: string) {
// Remove unselected students from the dropdown
this.clearData();
if (search.length === 0) {
return;
}
// Fetch students matching the query
const { students: fetchedStudents } = await this.fetchStudents(search);
// Add only the students which are not selected to avoid duplication
const notSelectedStudents = fetchedStudents.filter((fetchedStudent) =>
this.students.every((s) => s.value !== fetchedStudent.value)
);
Expand Down
4 changes: 4 additions & 0 deletions zapisy/apps/theses/assets/student-filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,13 @@ const store = new Vuex.Store({
},
});

// Get the server field
const djangoField = document.getElementById("id_students");

// Replace it with the placeholder for the custom MultiSelectFilter
const multiselectPlaceholder = document.getElementById("student-filter");
djangoField.before(multiselectPlaceholder);
djangoField.style.display = "none";

// Create the custom MultiSelectFilter
new Vue({ el: "#student-filter", render: (h) => h(StudentFilter), store });
7 changes: 7 additions & 0 deletions zapisy/apps/theses/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,17 @@ def __init__(self, user, *args, **kwargs):

def clean(self):
super().clean()
# Handle the mess caused by django not recognizing the selected students
# (line 47: `queryset=Student.objects.none(),`)
if 'students' in self.data:
if 'students' in self.errors:
# No error, trust me bro
self.errors.pop('students')
# Handle the mess caused by a different data structure
# appearing in tests for some reason
# QueryDict in normal use; Python dict in tests; wtf
ids_or_students = self.data.getlist('students') if 'getlist' in dir(self.data) else self.data['students']
# Help django find out the students actually exist
if len(ids_or_students) != 0 and isinstance(ids_or_students[0], str):
self.cleaned_data['students'] = Student.objects.filter(Q(id__in=ids_or_students))
else:
Expand Down
1 change: 1 addition & 0 deletions zapisy/apps/theses/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ def students(request, substring):
Q(matricula__icontains=substring)
)
matching_students = Student.objects.filter(conditions)
# Return matching students in a MultiSelectFilter-friendly format
return JsonResponse({'students': [
{
'value': s.id,
Expand Down

0 comments on commit 984c7c2

Please sign in to comment.