Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions client/src/mixins/formValidationMixin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/**
* Mixin providing common form validation helper methods for Vuelidate.
*
* This mixin centralizes the repeated validation and form reset patterns used
* across multiple components that handle new/edit forms.
*
* Usage:
* 1. Import and include in your component's mixins array
* 2. Define your form state properties (e.g., newFormState, editFormState)
* 3. Define your validations object as usual
* 4. Use the helper methods in your template and component logic
*
* Example:
* import formValidationMixin from '@/mixins/formValidationMixin';
*
* export default {
* mixins: [formValidationMixin],
* data() {
* return {
* newFormState: { name: '' },
* editFormState: { id: null, name: '' },
* };
* },
* validations: {
* newFormState: { name: { required } },
* editFormState: { name: { required } },
* },
* methods: {
* resetNewForm() {
* this.resetForm('newFormState', { name: '' });
* },
* },
* };
*/
export default {
methods: {
/**
* Get Bootstrap validation state for a form field.
*
* @param {string} formStateKey - The validation group key (e.g., 'newFormState', 'editFormState')
* @param {string} fieldName - The field name within the validation group
* @returns {boolean|null} - true if valid, false if invalid, null if pristine
*/
getValidationState(formStateKey, fieldName) {
const field = this.$v[formStateKey][fieldName];
if (!field) {
return null;
}
const { $dirty, $error } = field;
return $dirty ? !$error : null;
},

/**
* Reset a form to its initial state and reset validation.
*
* @param {string} formStateKey - The data property key (e.g., 'newFormState')
* @param {Object} initialState - The initial state object to reset to
*/
resetForm(formStateKey, initialState) {
this[formStateKey] = { ...initialState };
this.$nextTick(() => {
this.$v.$reset();
});
},

/**
* Check if a form has validation errors after touching all fields.
*
* @param {string} formStateKey - The validation group key
* @returns {boolean} - true if the form has any validation errors
*/
hasFormErrors(formStateKey) {
this.$v[formStateKey].$touch();
return this.$v[formStateKey].$anyError;
},
},
};
34 changes: 10 additions & 24 deletions client/src/views/show/config/ConfigCast.vue
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
id="new-first-name-input"
v-model="$v.newFormState.firstName.$model"
name="new-first-name-input"
:state="validateNewState('firstName')"
:state="getValidationState('newFormState', 'firstName')"
aria-describedby="new-first-name-feedback"
/>
<b-form-invalid-feedback id="new-first-name-feedback">
Expand All @@ -87,7 +87,7 @@
id="new-last-name-input"
v-model="$v.newFormState.lastName.$model"
name="new-last-name-input"
:state="validateNewState('lastName')"
:state="getValidationState('newFormState', 'lastName')"
aria-describedby="new-last-name-feedback"
/>
<b-form-invalid-feedback id="new-last-name-feedback">
Expand Down Expand Up @@ -115,7 +115,7 @@
id="edit-first-name-input"
v-model="$v.editFormState.firstName.$model"
name="edit-first-name-input"
:state="validateEditState('firstName')"
:state="getValidationState('editFormState', 'firstName')"
aria-describedby="edit-first-name-feedback"
/>
<b-form-invalid-feedback id="edit-first-name-feedback">
Expand All @@ -131,7 +131,7 @@
id="edit-last-name-input"
v-model="$v.editFormState.lastName.$model"
name="edit-last-name-input"
:state="validateEditState('lastName')"
:state="getValidationState('editFormState', 'lastName')"
aria-describedby="edit-last-name-feedback"
/>
<b-form-invalid-feedback id="edit-last-name-feedback">
Expand All @@ -148,10 +148,12 @@ import { required } from 'vuelidate/lib/validators';
import { mapGetters, mapActions } from 'vuex';
import CastLineStats from '@/vue_components/show/config/cast/CastLineStats.vue';
import log from 'loglevel';
import formValidationMixin from '@/mixins/formValidationMixin';

export default {
name: 'ConfigCast',
components: { CastLineStats },
mixins: [formValidationMixin],
data() {
return {
castFields: ['first_name', 'last_name', { key: 'btn', label: '' }],
Expand Down Expand Up @@ -198,15 +200,11 @@ export default {
},
methods: {
resetNewForm() {
this.newFormState = {
this.resetForm('newFormState', {
firstName: '',
lastName: '',
};
this.submittingNewCast = false;

this.$nextTick(() => {
this.$v.$reset();
});
this.submittingNewCast = false;
},
async onSubmitNew(event) {
this.$v.newFormState.$touch();
Expand All @@ -227,10 +225,6 @@ export default {
this.submittingNewCast = false;
}
},
validateNewState(name) {
const { $dirty, $error } = this.$v.newFormState[name];
return $dirty ? !$error : null;
},
openEditForm(castMember) {
if (castMember != null) {
this.editFormState.id = castMember.item.id;
Expand All @@ -241,18 +235,14 @@ export default {
}
},
resetEditForm() {
this.editFormState = {
this.resetForm('editFormState', {
id: null,
showID: null,
firstName: '',
lastName: '',
};
});
this.submittingEditCast = false;
this.deletingCast = false;

this.$nextTick(() => {
this.$v.$reset();
});
},
async onSubmitEdit(event) {
this.$v.editFormState.$touch();
Expand All @@ -273,10 +263,6 @@ export default {
this.submittingEditCast = false;
}
},
validateEditState(name) {
const { $dirty, $error } = this.$v.editFormState[name];
return $dirty ? !$error : null;
},
async deleteCastMember(castMember) {
if (this.deletingCast) {
return;
Expand Down
38 changes: 12 additions & 26 deletions client/src/views/show/config/ConfigCharacters.vue
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
id="new-name-input"
v-model="$v.newFormState.name.$model"
name="new-name-input"
:state="validateNewState('name')"
:state="getValidationState('newFormState', 'name')"
aria-describedby="new-name-feedback"
/>
<b-form-invalid-feedback id="new-name-feedback">
Expand All @@ -94,7 +94,7 @@
id="new-description-input"
v-model="$v.newFormState.description.$model"
name="new-description-input"
:state="validateNewState('description')"
:state="getValidationState('newFormState', 'description')"
/>
</b-form-group>
<b-form-group
Expand All @@ -106,7 +106,7 @@
id="new-played-by-input"
v-model="$v.newFormState.played_by.$model"
:options="castOptions"
:state="validateNewState('played_by')"
:state="getValidationState('newFormState', 'played_by')"
/>
</b-form-group>
</b-form>
Expand All @@ -126,7 +126,7 @@
id="edit-name-input"
v-model="$v.editFormState.name.$model"
name="edit-name-input"
:state="validateEditState('name')"
:state="getValidationState('editFormState', 'name')"
aria-describedby="edit-name-feedback"
/>
<b-form-invalid-feedback id="edit-name-feedback">
Expand All @@ -142,7 +142,7 @@
id="edit-description-input"
v-model="$v.editFormState.description.$model"
name="edit-description-input"
:state="validateEditState('description')"
:state="getValidationState('editFormState', 'description')"
/>
</b-form-group>
<b-form-group
Expand All @@ -154,7 +154,7 @@
id="edit-played-by-input"
v-model="$v.editFormState.played_by.$model"
:options="castOptions"
:state="validateEditState('played_by')"
:state="getValidationState('editFormState', 'played_by')"
/>
</b-form-group>
</b-form>
Expand All @@ -168,10 +168,12 @@ import { mapGetters, mapActions } from 'vuex';
import CharacterLineStats from '@/vue_components/show/config/characters/CharacterLineStats.vue';
import log from 'loglevel';
import CharacterGroups from '@/vue_components/show/config/characters/CharacterGroups.vue';
import formValidationMixin from '@/mixins/formValidationMixin';

export default {
name: 'ConfigCharacters',
components: { CharacterGroups, CharacterLineStats },
mixins: [formValidationMixin],
data() {
return {
rowsPerPage: 15,
Expand Down Expand Up @@ -232,16 +234,12 @@ export default {
},
methods: {
resetNewForm() {
this.newFormState = {
this.resetForm('newFormState', {
name: '',
description: '',
played_by: null,
};
this.submittingNewCharacter = false;

this.$nextTick(() => {
this.$v.$reset();
});
this.submittingNewCharacter = false;
},
async onSubmitNew(event) {
this.$v.newFormState.$touch();
Expand All @@ -262,10 +260,6 @@ export default {
this.submittingNewCharacter = false;
}
},
validateNewState(name) {
const { $dirty, $error } = this.$v.newFormState[name];
return $dirty ? !$error : null;
},
openEditForm(character) {
if (character != null) {
this.editFormState.id = character.item.id;
Expand All @@ -277,19 +271,15 @@ export default {
}
},
resetEditForm() {
this.editFormState = {
this.resetForm('editFormState', {
id: null,
showID: null,
name: '',
description: '',
played_by: null,
};
});
this.submittingEditCharacter = false;
this.deletingCharacter = false;

this.$nextTick(() => {
this.$v.$reset();
});
},
async onSubmitEdit(event) {
this.$v.editFormState.$touch();
Expand All @@ -310,10 +300,6 @@ export default {
this.submittingEditCharacter = false;
}
},
validateEditState(name) {
const { $dirty, $error } = this.$v.editFormState[name];
return $dirty ? !$error : null;
},
async deleteCharacter(character) {
if (this.deletingCharacter) {
return;
Expand Down
Loading
Loading