I am rather perplexed as to why whenever trying to use a select box, it simply freezes and does not generate the event which should trigger the function making a call to the server.
Here is the code:
<div>
<div class="grid">
<div class="sub-navigation">
<div class="filters">
<div class="form-row">
<h3>Filter Interactions</h3>
</div>
<div class="form-row">
<label>Search</label><input type="text" v-model.lazy="filterText" @input="debounceFilterText" placeholder="Search" id="filterText" />
</div>
<div class="form-row">
<select v-bind:value="semesterID" @change="setSemester($event.currentTarget.value)">
<option value="">Select a Semester</option>
<option v-for="s in availableSemesters" :value="s.SemesterID">{{semesterNames[s.SemesterID]}}</option>
</select>
</div>
<div class="form-row">
<input type="checkbox" v-model="onlyMyInteractions" /> <small>Only show my interactions</small>
</div>
<hr />
<div class="form-row">
<new-interaction v-bind="{ semesterID, semesterIds, currentEducatorUserID, schoolID: sessionSchool }"></new-interaction>
</div>
<div class="form-row">
<button class="btn btn--full" @click.prevent="bulkAddInteraction">Bulk add Students/Interactions</button>
</div>
<div class="form-row">
<button :disabled="!previousSemester" class="btn btn--full" @click.prevent="toggleAddInteractionFromLastSemesterModal">Add from last semester</button>
<Modal :show="show" :requestClose="() => show = false" title="Add From Last Semester" width="800px">
<template slot-scope="{requestClose}">
<div>
<AddFromLastSemester
:currentSemester="semesterID"
:previousSemester="previousSemester"
:semesterIds="semesterIds"
@submitAddFromLastSemester="submitAddFromLastSemester"
></AddFromLastSemester>
</div>
</template>
</Modal>
</div>
<hr />
<div class="form-row scrolls">
<new-semester :schoolID="sessionSchool"></new-semester>
</div>
<hr />
<div class="form-row">
<button class="btn btn--full" @click.prevent="exportInteractions">Export Interactions</button>
</div>
<hr />
<div class="form-row">
<button :disabled="!canDeleteInteractions" class="btn btn--full" @click.prevent="deleteInteractions">Delete Selected Interactions</button>
</div>
</div>
</div>
<div class="interactions-container">
<Spinner v-if="loading"></Spinner>
<div class="interactions-container-inner">
<h1>Interactions <small>{{semesterString}}</small></h1>
<Handsontable :data="gridData" :settings="settings" ref="interactionsTable"></Handsontable>
</div>
</div>
</div>
</div>
</template>
<script>
import Handsontable from '@/shared/components/ui/Handsontable';
import map from 'lodash/map';
import each from 'lodash/each';
import orderBy from 'lodash/orderBy';
import filter from 'lodash/filter';
import some from 'lodash/some';
import Modal from '@/shared/components/ui/modal';
import NewSemester from '@/pages/Educators/Interactions/NewSemester';
import NewInteraction from './NewInteraction';
import AddFromLastSemester from './AddFromLastSemester';
import debounce from 'debounce';
import lang from '@/shared/lib/lang';
import { sqlDate } from '@/shared/lib/formatters';
import { normalize, schema } from 'normalizr';
import { eSeason, eLearnOrProg, eRelationType, eRelationWith } from '@/shared/lib/enums';
import { Interaction, StudentView } from '@/shared//lib/valueObjects';
import Spinner from '@/shared/components/ui/Spinner';
import isEqual from 'lodash/isEqual';
import { ApolloClient } from '@/main';
let _this;
export default {
'name': 'Interactions',
components: {
Handsontable,
Modal,
NewSemester,
NewInteraction,
AddFromLastSemester,
Spinner
},
props: ['currentUser', 'semestersData', 'modals', 'toggleInteractionModal', 'addingInteraction', 'sessionSchool', 'updateInteraction', 'isAdminMode', 'getInteractionsBySemesters', 'createInteraction', 'deleteInteraction'],
data () {
return {
show: false,
loading: false,
interactions: [],
sessionSemester: null,
savedEntity: {},
settings: {
colHeaders: ['InteractionID', '', 'First Name', 'Last Name', 'Student ID', 'Learning Type', 'Program Type', 'High Impact Trip', '# Shabbat Meals', '# Halachic Counsel', '# Personal Counsel', 'Leadership Development', 'Relationship Type', 'Relationship With', 'Chavruta', 'Notes'],
columns: [
{ data: 'InteractionID', type: 'text', readOnly: true },
{ data: 'Selected', type: 'checkbox' },
{ data: 'StudentView.FirstName', type: 'text', readOnly: true },
{ data: 'StudentView.LastName', type: 'text', readOnly: true },
{ data: 'StudentID', readOnly: true },
{
data: 'LearningType',
type: 'dropdown',
source: [ '', 'None', 'Regular', 'Occasional' ]
},
{
data: 'ProgramType',
type: 'dropdown',
source: [ '', 'None', 'Regular', 'Occasional' ]
},
{ data: 'IsHighImpactTrip', type: 'checkbox' },
{ data: 'ShabbatMeals', type: 'numeric' },
{ data: 'HalachicCounsel', type: 'numeric' },
{ data: 'PersonalCounsel', type: 'numeric' },
{ data: 'IsLeadershipDevelopment', type: 'checkbox' },
{
data: 'RelationshipType',
type: 'dropdown',
source: [ 'Casual', 'Significant', 'Regular' ]
},
{
data: 'RelationshipWith',
type: 'dropdown',
source: [ 'None', 'Male', 'Female', 'Both' ]
},
{ data: 'IsChavruta', type: 'checkbox' },
{ data: 'Notes', type: 'text' }
],
afterChange: function (change, source) {
if (source === 'loadData') {
return; // don't save this change
}
_this.handleChange(change);
},
hiddenColumns: {
columns: [0, 4],
indicators: false
},
fixedColumnsLeft: 4,
cells (row, col, prop) {
if (_this) {
var cellProperties = {};
// check if the row has a educator unit id not equal to what we have
var educatorUnitID = (_this.gridData[row] || {}).EducatorUnitID;
cellProperties.readOnly = false;
if (_this.currentEducatorUserID !== educatorUnitID) cellProperties.readOnly = true;
return cellProperties;
}
},
columnSorting: true
},
filterText: '',
filterTextDebounced: '',
onlyMyInteractions: true,
selectedInteractionIds: []
};
},
mounted () {
_this = this;
if (this.currentSemesterIndex === -1 && this.availableSemesterIDs.length > 0) {
var newSemester = (orderBy(this.availableSemesters, ['EndDate', 'Season'], ['desc', 'desc']) || []).shift();
this.sessionSemester = newSemester.SemesterID
}
this.filterText = '';
this.filterTextDebounced = '';
this.onlyMyInteractions = true;
this.selectedInteractionIds = [];
},
computed: {
semesterIds () {
return (this.semestersData || []).map(x => x.SemesterID)
},
semesterID () {
if (this.sessionSemester) return this.sessionSemester;
var newSemester = (orderBy(this.availableSemesters, ['EndDate', 'Season'], ['desc', 'desc']) || []).shift();
return (newSemester || {}).SemesterID || -1;
},
currentEducatorUserID () {
return this.currentUser.educatorUnitId
},
seasons () {
return eSeason.enums;
},
learnOrProg () {
return eLearnOrProg.enums;
},
relationType () {
return eRelationType.enums;
},
relationWith () {
return eRelationWith.enums;
},
interactionsViewModel () {
return this.interactions = this.normalizeInteractions((this.getInteractionsBySemesters || [] ).map(i => ({ ...i, StudentID: i.StudentView.StudentID, LearningType: this.learnOrProg[i.LearningType], ProgramType: this.learnOrProg[i.ProgramType] , RelationshipType: this.relationType[i.RelationshipType], RelationshipWith: this.relationWith[i.RelationshipWith]})));
},
semesters () {
return this.normalizeSemesters((this.semestersData || []).map(semester => ({ ...semester, StartDate: sqlDate(semester['StartDate']), EndDate: sqlDate(semester['StartDate']), Season: this.seasons[semester.Season]})))
},
gridData () {
var interactions = this.filteredInteractions;
each(interactions, i => {
i['Selected'] = this.selectedInteractionIds.indexOf(i.InteractionID) >= 0;
});
if (this.onlyMyInteractions) {
interactions = filter(interactions, i => this.currentEducatorUserID == i.EducatorUnitID);
}
if (this.filterTextDebounced) {
var ft = this.filterTextDebounced.toUpperCase();
interactions = filter(interactions, i => {
return (
i.StudentView && (
(i.StudentView.FirstName.toUpperCase().indexOf(ft) !== -1) ||
(i.StudentView.LastName.toUpperCase().indexOf(ft) !== -1)
)
);
});
}
return interactions;
},
filteredInteractions () {
if (!this.interactionsViewModel) return [];
return filter(this.interactionsViewModel, i => parseInt(i.SemesterID) === parseInt(this.semesterID));
},
semesterString () {
return this.semesterNames[`${this.semesterID}`];
},
availableSemesters () {
if (!this.semesters) return [];
return filter(this.semesters, s => s.SchoolID === this.sessionSchool).sort((a, b) => {
var dateA = new Date(a.StartDate);
var dateB = new Date(b.StartDate);
return dateB - dateA;
});
},
semesterNames () {
if (!this.availableSemesters) return {};
let semesters = {};
each(this.availableSemesters, s => {
let sYear = s.StartDate.slice(0, 4);
semesters[s.SemesterID] = `${s.Season} ${sYear}`;
});
return semesters;
},
semesterID () {
if (this.sessionSemester) return this.sessionSemester;
var newSemester = (orderBy(this.availableSemesters, ['EndDate', 'Season'], ['desc', 'desc']) || []).shift();
return (newSemester || {}).SemesterID || -1;
},
availableSemesterIDs () {
return map(this.availableSemesters, s => s.SemesterID);
},
currentSemesterIndex () {
return this.availableSemesterIDs.indexOf(parseInt(this.sessionSemester));
},
previousSemester () {
if (this.currentSemesterIndex < 1) return null;
return this.availableSemesterIDs[this.currentSemesterIndex - 1];
},
canDeleteInteractions () {
return this.gridData.some(x => x.Selected);
}
},
methods: {
setSemester (semesterID) {
this.sessionSemester = semesterID;
},
normalizeInteractions(interactions) {
const interactionsEntity = new schema.Entity('interactions', {}, { idAttribute: 'InteractionID' });
const userListSchema = new schema.Array(interactionsEntity);
const normalizedData = normalize(interactions, userListSchema);
return normalizedData.entities.interactions
},
normalizeSemesters(semesters) {
const semestersEntity = new schema.Entity('semesters', {}, { idAttribute: 'SemesterID' });
const userListSchema = new schema.Array(semestersEntity);
const normalizedData = normalize(semesters, userListSchema);
return normalizedData.entities.semesters
},
setSemester (semesterID) {
this.sessionSemester = semesterID;
},
bulkAddInteraction () {
this.$router.push('interactions/bulkadd');
},
exportInteractions () {
this.$refs.interactionsTable.export();
},
toggleAddInteractionFromLastSemesterModal () {
this.show = !this.show
},
handleChange (change) {
var idx = change[0][0];
if (!this.gridData[idx]) return;
var field = change[0][1];
var oldVal = change[0][2];
var val = change[0][3];
if (val === oldVal) return;
var iid = this.gridData[idx].InteractionID;
var entity = {...this.interactions[iid]};
entity[field] = val;
if (field === 'Selected') {
if (val) {
this.selectedInteractionIds.push(iid);
}
else {
var index = this.selectedInteractionIds.indexOf(iid);
this.selectedInteractionIds.splice(index, 1);
}
return;
}
this.updateInteraction({ ...new Interaction(entity) });
},
submitAddFromLastSemester (selectedStudents) {
var studentIDs = selectedStudents.map(x => x.StudentView.StudentID);
if (studentIDs.length < 1) return;
var interactions = [];
const entities = selectedStudents.map(x => ({
optimisticResponse: {
...x,
InteractionID: Math.floor((Math.random() * -100) + 1),
SemesterID: this.semesterID,
IsHighImpactTrip: false,
IsLeadershipDevelopment: false,
IsChavruta: false,
HalachicCounsel: '0',
PersonalCounsel: '0',
ShabbatMeals: 0,
EducatorUnitID: this.currentEducatorUserID
},
interaction: {
StudentID: x.StudentID,
SemesterID: this.semesterID,
IsHighImpactTrip: false,
IsLeadershipDevelopment: false,
IsChavruta: false,
HalachicCounsel: '0',
PersonalCounsel: '0',
ShabbatMeals: 0,
EducatorUnitID: this.currentEducatorUserID
}
}))
entities.map(i => this.createInteraction({ interaction: i.interaction, optimisticResponse: i.optimisticResponse }))
this.toggleAddInteractionFromLastSemesterModal();
},
deleteInteractions () {
if (!this.canDeleteInteractions) return;
// only allow deletion of the interactions with the users edu unit
let selected = map(this.selectedInteractionIds, i => this.interactions[i]).filter(Boolean);
if (some(selected, x => x.EducatorUnitID !== this.currentEducatorUserID)) {
window.alert('You can only delete interactions for your own educator unit.');
return;
}
if (!window.confirm('Delete selected interactions?')) {
return;
}
if (this.selectedInteractionIds.length) this.selectedInteractionIds.map(x => this.deleteInteraction(x, this.semesterID));
},
debounceFilterText: debounce(e => {
_this.filterTextDebounced = e.target.value;
_this.filterText = _this.filterTextDebounced;
}, 300)
}
};
</script>
<style scoped>
.grid {
display: grid;
grid-template-columns: 270px 1fr;
}
h1{
font-size: 28px;
padding: 10px;
font-weight: bold;
}
h1 small {
color: #999999;
font-size: 20px;
text-transform: capitalize;
margin-left: 10px;
}
h3 {
font-weight: bold;
border-bottom: 1px solid #efefef;
}
</style>