Standardize on continent list component

This commit is contained in:
Daniel J. Summers 2021-08-14 17:34:13 -04:00
parent 837ab35da5
commit 227713fa26
5 changed files with 23 additions and 62 deletions

View File

@ -2,14 +2,8 @@
<form class="container"> <form class="container">
<div class="row"> <div class="row">
<div class="col col-xs-12 col-sm-6 col-md-4 col-lg-3"> <div class="col col-xs-12 col-sm-6 col-md-4 col-lg-3">
<div class="form-floating"> <continent-list v-model="criteria.continentId" topLabel="Any"
<select id="continentId" class="form-select" :value="criteria.continentId" @update:modelValue="(c) => updateValue('continentId', c)" />
@change="updateValue('continentId', $event.target.value)">
<option value="">&ndash; Any &ndash;</option>
<option v-for="c in continents" :key="c.id" :value="c.id">{{c.name}}</option>
</select>
<label for="continentId">Continent</label>
</div>
</div> </div>
<div class="col col-xs-12 col-sm-6 col-md-4 col-lg-3"> <div class="col col-xs-12 col-sm-6 col-md-4 col-lg-3">
<div class="form-floating"> <div class="form-floating">
@ -56,12 +50,13 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, onMounted, ref, Ref } from 'vue' import { defineComponent, ref, Ref } from 'vue'
import { PublicSearch } from '@/api' import { PublicSearch } from '@/api'
import { useStore } from '@/store' import ContinentList from '../ContinentList.vue'
export default defineComponent({ export default defineComponent({
name: 'ProfilePublicSearchForm', name: 'ProfilePublicSearchForm',
components: { ContinentList },
props: { props: {
modelValue: { modelValue: {
type: Object, type: Object,
@ -70,17 +65,11 @@ export default defineComponent({
}, },
emits: ['search', 'update:modelValue'], emits: ['search', 'update:modelValue'],
setup (props, { emit }) { setup (props, { emit }) {
const store = useStore()
/** The initial search criteria passed; this is what we'll update and emit when data changes */ /** The initial search criteria passed; this is what we'll update and emit when data changes */
const criteria : Ref<PublicSearch> = ref({ ...props.modelValue as PublicSearch }) const criteria : Ref<PublicSearch> = ref({ ...props.modelValue as PublicSearch })
/** Make sure we have continents */
onMounted(async () => await store.dispatch('ensureContinents'))
return { return {
criteria, criteria,
continents: computed(() => store.state.continents),
updateValue: (key : string, value : string) => { updateValue: (key : string, value : string) => {
criteria.value = { ...criteria.value, [key]: value } criteria.value = { ...criteria.value, [key]: value }
emit('update:modelValue', criteria.value) emit('update:modelValue', criteria.value)

View File

@ -2,14 +2,8 @@
<form class="container"> <form class="container">
<div class="row"> <div class="row">
<div class="col col-xs-12 col-sm-6 col-md-4 col-lg-3"> <div class="col col-xs-12 col-sm-6 col-md-4 col-lg-3">
<div class="form-floating"> <continent-list v-model="criteria.continentId" topLabel="Any"
<select id="continentId" class="form-select" @update:modelValue="(c) => updateValue('continentId', c)" />
:value="criteria.continentId" @change="updateValue('continentId', $event.target.value)">
<option value="">&ndash; Any &ndash;</option>
<option v-for="c in continents" :key="c.id" :value="c.id">{{c.name}}</option>
</select>
<label for="continentId">Continent</label>
</div>
</div> </div>
<div class="col col-xs-12 col-sm-6 col-offset-md-2 col-lg-3 col-offset-lg-0"> <div class="col col-xs-12 col-sm-6 col-offset-md-2 col-lg-3 col-offset-lg-0">
<label class="jjj-label">Seeking Remote Work?</label><br> <label class="jjj-label">Seeking Remote Work?</label><br>
@ -56,12 +50,13 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, onMounted, Ref, ref } from 'vue' import { defineComponent, Ref, ref } from 'vue'
import { ProfileSearch } from '@/api' import { ProfileSearch } from '@/api'
import { useStore } from '@/store' import ContinentList from '../ContinentList.vue'
export default defineComponent({ export default defineComponent({
name: 'ProfileSearchForm', name: 'ProfileSearchForm',
components: { ContinentList },
props: { props: {
modelValue: { modelValue: {
type: Object, type: Object,
@ -70,17 +65,11 @@ export default defineComponent({
}, },
emits: ['search', 'update:modelValue'], emits: ['search', 'update:modelValue'],
setup (props, { emit }) { setup (props, { emit }) {
const store = useStore()
/** The initial search criteria passed; this is what we'll update and emit when data changes */ /** The initial search criteria passed; this is what we'll update and emit when data changes */
const criteria : Ref<ProfileSearch> = ref({ ...props.modelValue as ProfileSearch }) const criteria : Ref<ProfileSearch> = ref({ ...props.modelValue as ProfileSearch })
/** Make sure we have continents */
onMounted(async () => await store.dispatch('ensureContinents'))
return { return {
criteria, criteria,
continents: computed(() => store.state.continents),
updateValue: (key : string, value : string) => { updateValue: (key : string, value : string) => {
criteria.value = { ...criteria.value, [key]: value } criteria.value = { ...criteria.value, [key]: value }
emit('update:modelValue', criteria.value) emit('update:modelValue', criteria.value)

View File

@ -23,14 +23,8 @@
</p> </p>
</div> </div>
<div class="col-12 col-sm-6 col-md-4"> <div class="col-12 col-sm-6 col-md-4">
<div class="form-floating"> <continent-list v-model="v$.continentId.$model" :isInvalid="v$.continentId.$error"
<select id="continentId" :class="{ 'form-select': true, 'is-invalid': v$.continentId.$error }" @touch="v$.continentId.$touch() || true" />
:value="v$.continentId.$model" @change="continentChanged">
<option v-for="c in continents" :key="c.id" :value="c.id">{{c.name}}</option>
</select>
<label for="continentId" class="jjj-required">Continent</label>
</div>
<div class="invalid-feedback">Please select a continent</div>
</div> </div>
<div class="col-12 col-sm-6 col-md-8"> <div class="col-12 col-sm-6 col-md-8">
<div class="form-floating"> <div class="form-floating">
@ -116,6 +110,7 @@ import api, { Citizen, LogOnSuccess, Profile, ProfileForm } from '@/api'
import { toastError, toastSuccess } from '@/components/layout/AppToaster.vue' import { toastError, toastSuccess } from '@/components/layout/AppToaster.vue'
import { useStore } from '@/store' import { useStore } from '@/store'
import ContinentList from '@/components/ContinentList.vue'
import LoadData from '@/components/LoadData.vue' import LoadData from '@/components/LoadData.vue'
import MarkdownEditor from '@/components/MarkdownEditor.vue' import MarkdownEditor from '@/components/MarkdownEditor.vue'
import MaybeSave from '@/components/MaybeSave.vue' import MaybeSave from '@/components/MaybeSave.vue'
@ -124,6 +119,7 @@ import ProfileSkillEdit from '@/components/profile/SkillEdit.vue'
export default defineComponent({ export default defineComponent({
name: 'EditProfile', name: 'EditProfile',
components: { components: {
ContinentList,
LoadData, LoadData,
MarkdownEditor, MarkdownEditor,
MaybeSave, MaybeSave,
@ -175,7 +171,6 @@ export default defineComponent({
/** Retrieve the user's profile and their real name */ /** Retrieve the user's profile and their real name */
const retrieveData = async (errors : string[]) => { const retrieveData = async (errors : string[]) => {
await store.dispatch('ensureContinents')
const profileResult = await api.profile.retreive(undefined, user) const profileResult = await api.profile.retreive(undefined, user)
if (typeof profileResult === 'string') { if (typeof profileResult === 'string') {
errors.push(profileResult) errors.push(profileResult)
@ -201,21 +196,6 @@ export default defineComponent({
profile.realName = typeof nameResult !== 'undefined' ? (nameResult as Citizen).realName || '' : '' profile.realName = typeof nameResult !== 'undefined' ? (nameResult as Citizen).realName || '' : ''
} }
/**
* Mark the continent field as changed
*
* (This works around a really strange sequence where, if the "touch" call is directly wired up to the onChange
* event, the first time a value is selected, it doesn't stick (although the field is marked as touched). On second
* and subsequent times, it worked. The solution here is to grab the value and update the reactive source for the
* form, then manually set the field to touched; this restores the expected behavior. This is probably why the
* library doesn't hook into the onChange event to begin with...)
*/
const continentChanged = (e : Event) : boolean => {
profile.continentId = (e.target as HTMLSelectElement).value
v$.value.continentId.$touch()
return true
}
/** The ID for new skills */ /** The ID for new skills */
let newSkillId = 0 let newSkillId = 0
@ -266,8 +246,6 @@ export default defineComponent({
user, user,
isNew, isNew,
profile, profile,
continents: computed(() => store.state.continents),
continentChanged,
addSkill, addSkill,
removeSkill, removeSkill,
saveProfile, saveProfile,

View File

@ -77,7 +77,7 @@ export default defineComponent({
/** An empty set of search criteria */ /** An empty set of search criteria */
const emptyCriteria = { const emptyCriteria = {
continentId: undefined, continentId: '',
skill: undefined, skill: undefined,
bioExperience: undefined, bioExperience: undefined,
remoteWork: '' remoteWork: ''
@ -98,8 +98,10 @@ export default defineComponent({
searched.value = true searched.value = true
try { try {
searching.value = true searching.value = true
// Hold variable for ensuring continent ID is not undefined here, but excluded from search payload
const contId = queryValue(route, 'continentId')
const searchParams : ProfileSearch = { const searchParams : ProfileSearch = {
continentId: queryValue(route, 'continentId'), continentId: contId === '' ? undefined : contId,
skill: queryValue(route, 'skill'), skill: queryValue(route, 'skill'),
bioExperience: queryValue(route, 'bioExperience'), bioExperience: queryValue(route, 'bioExperience'),
remoteWork: queryValue(route, 'remoteWork') || '' remoteWork: queryValue(route, 'remoteWork') || ''
@ -111,6 +113,7 @@ export default defineComponent({
errors.value.push('The server returned a "Not Found" response (this should not happen)') errors.value.push('The server returned a "Not Found" response (this should not happen)')
} else { } else {
results.value = searchResult results.value = searchResult
searchParams.continentId = searchParams.continentId || ''
criteria.value = searchParams criteria.value = searchParams
} }
} finally { } finally {

View File

@ -76,7 +76,7 @@ export default defineComponent({
/** An empty set of search criteria */ /** An empty set of search criteria */
const emptyCriteria = { const emptyCriteria = {
continentId: undefined, continentId: '',
region: undefined, region: undefined,
skill: undefined, skill: undefined,
remoteWork: '' remoteWork: ''
@ -97,8 +97,9 @@ export default defineComponent({
searched.value = true searched.value = true
try { try {
searching.value = true searching.value = true
const contId = queryValue(route, 'continentId')
const searchParams : PublicSearch = { const searchParams : PublicSearch = {
continentId: queryValue(route, 'continentId'), continentId: contId === '' ? undefined : contId,
region: queryValue(route, 'region'), region: queryValue(route, 'region'),
skill: queryValue(route, 'skill'), skill: queryValue(route, 'skill'),
remoteWork: queryValue(route, 'remoteWork') || '' remoteWork: queryValue(route, 'remoteWork') || ''
@ -110,6 +111,7 @@ export default defineComponent({
errors.value.push('The server returned a "Not Found" response (this should not happen)') errors.value.push('The server returned a "Not Found" response (this should not happen)')
} else { } else {
results.value = searchResult results.value = searchResult
searchParams.continentId = searchParams.continentId || ''
criteria.value = searchParams criteria.value = searchParams
} }
} finally { } finally {