Streamline "maybe save" props
This commit is contained in:
parent
8e8bbb48ba
commit
34d5224c11
|
@ -3,47 +3,45 @@
|
||||||
.modal-header: h5.modal-title(id="maybeSaveLabel") Unsaved Changes
|
.modal-header: h5.modal-title(id="maybeSaveLabel") Unsaved Changes
|
||||||
.modal-body You have modified the data on this page since it was last saved. What would you like to do?
|
.modal-body You have modified the data on this page since it was last saved. What would you like to do?
|
||||||
.modal-footer
|
.modal-footer
|
||||||
button.btn.btn-secondary(type="button" @click.prevent="onStay") Stay on This Page
|
button.btn.btn-secondary(type="button" @click.prevent="close") Stay on This Page
|
||||||
button.btn.btn-primary(type="button" @click.prevent="onSave") Save Changes
|
button.btn.btn-primary(type="button" @click.prevent="save") Save Changes
|
||||||
button.btn.btn-danger(type="button" @click.prevent="onDiscard") Discard Changes
|
button.btn.btn-danger(type="button" @click.prevent="discard") Discard Changes
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref, Ref, watch } from "vue"
|
import { onMounted, ref, Ref } from "vue"
|
||||||
import { RouteLocationNormalized, useRouter } from "vue-router"
|
import { onBeforeRouteLeave, RouteLocationNormalized, useRouter } from "vue-router"
|
||||||
import { Validation } from "@vuelidate/core"
|
import { Validation } from "@vuelidate/core"
|
||||||
import { Modal } from "bootstrap"
|
import { Modal } from "bootstrap"
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
isShown: boolean
|
saveAction: () => Promise<unknown>
|
||||||
toRoute: RouteLocationNormalized
|
|
||||||
saveAction?: () => Promise<unknown>
|
|
||||||
validator?: Validation
|
validator?: Validation
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const emit = defineEmits<{
|
|
||||||
(e: "close") : void
|
|
||||||
(e: "discard") : void
|
|
||||||
(e: "cancel") : void
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
/** Reference to the modal dialog (we can't get it until the component is rendered) */
|
/** Reference to the modal dialog (we can't get it until the component is rendered) */
|
||||||
const modal : Ref<Modal | undefined> = ref(undefined)
|
const modal : Ref<Modal | undefined> = ref(undefined)
|
||||||
|
|
||||||
/** Save changes (if required) and go to the next route */
|
/** The route to which navigation was intercepted, and will be resumed */
|
||||||
const onSave = async () => {
|
let nextRoute : RouteLocationNormalized
|
||||||
if (props.saveAction) await props.saveAction()
|
|
||||||
emit("close")
|
/** Close the modal window */
|
||||||
router.push(props.toRoute)
|
const close = () => modal.value?.hide()
|
||||||
|
|
||||||
|
/** Save changes and go to the next route */
|
||||||
|
const save = async () => {
|
||||||
|
await props.saveAction()
|
||||||
|
close()
|
||||||
|
router.push(nextRoute)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Discard changes (if required) and go to the next route */
|
/** Discard changes and go to the next route */
|
||||||
const onDiscard = () => {
|
const discard = () => {
|
||||||
if (props.validator) props.validator.$reset()
|
if (props.validator) props.validator.$reset()
|
||||||
emit("close")
|
close()
|
||||||
router.push(props.toRoute)
|
router.push(nextRoute)
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
@ -51,17 +49,11 @@ onMounted(() => {
|
||||||
{ backdrop: "static", keyboard: false })
|
{ backdrop: "static", keyboard: false })
|
||||||
})
|
})
|
||||||
|
|
||||||
/** Show or hide the modal based on the property value changing */
|
/** Prompt for save if the user navigates away with unsaved changes */
|
||||||
watch(() => props.isShown, (toShow) => {
|
onBeforeRouteLeave(async (to, from) => { // eslint-disable-line
|
||||||
if (modal.value) {
|
if (!props.validator || !props.validator.$anyDirty) return true
|
||||||
if (toShow) {
|
nextRoute = to
|
||||||
modal.value.show()
|
modal.value?.show()
|
||||||
} else {
|
return false
|
||||||
modal.value.hide()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
/** Stay on this page with no changes; just close the modal */
|
|
||||||
const onStay = () => emit("close")
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -64,13 +64,11 @@ article
|
||||||
p.text-muted.fst-italic.
|
p.text-muted.fst-italic.
|
||||||
(If you want to delete your profile, or your entire account,
|
(If you want to delete your profile, or your entire account,
|
||||||
#[router-link(to="/so-long/options") see your deletion options here].)
|
#[router-link(to="/so-long/options") see your deletion options here].)
|
||||||
maybe-save(:isShown="confirmNavShown" :toRoute="nextRoute" :saveAction="saveProfile" :validator="v$"
|
maybe-save(:saveAction="saveProfile" :validator="v$")
|
||||||
@close="confirmClose")
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, reactive, Ref } from "vue"
|
import { computed, ref, reactive } from "vue"
|
||||||
import { onBeforeRouteLeave, RouteLocationNormalized } from "vue-router"
|
|
||||||
import useVuelidate from "@vuelidate/core"
|
import useVuelidate from "@vuelidate/core"
|
||||||
import { required } from "@vuelidate/validators"
|
import { required } from "@vuelidate/validators"
|
||||||
|
|
||||||
|
@ -183,21 +181,4 @@ const saveProfile = async () => {
|
||||||
v$.value.$reset()
|
v$.value.$reset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Whether the navigation confirmation is shown */
|
|
||||||
const confirmNavShown = ref(false)
|
|
||||||
|
|
||||||
/** The "next" route (will be navigated or cleared) */
|
|
||||||
const nextRoute : Ref<RouteLocationNormalized | undefined> = ref(undefined)
|
|
||||||
|
|
||||||
/** If the user has unsaved changes, give them an opportunity to save before moving on */
|
|
||||||
onBeforeRouteLeave(async (to, from) => { // eslint-disable-line
|
|
||||||
if (!v$.value.$anyDirty) return true
|
|
||||||
nextRoute.value = to
|
|
||||||
confirmNavShown.value = true
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
|
|
||||||
/** Close the navigation confirmation modal */
|
|
||||||
const confirmClose = () => { confirmNavShown.value = false }
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -32,12 +32,12 @@ article
|
||||||
.col-12
|
.col-12
|
||||||
p.text-danger(v-if="v$.$error") Please correct the errors above
|
p.text-danger(v-if="v$.$error") Please correct the errors above
|
||||||
button.btn.btn-primary(@click.prevent="saveListing(true)") #[icon(icon="content-save-outline")] Save
|
button.btn.btn-primary(@click.prevent="saveListing(true)") #[icon(icon="content-save-outline")] Save
|
||||||
maybe-save(:isShown="confirmNavShown" :toRoute="nextRoute" :saveAction="doSave" :validator="v$" @close="confirmClose")
|
maybe-save(:saveAction="doSave" :validator="v$")
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, reactive, ref, Ref } from "vue"
|
import { computed, reactive } from "vue"
|
||||||
import { onBeforeRouteLeave, RouteLocationNormalized, useRoute, useRouter } from "vue-router"
|
import { useRoute, useRouter } from "vue-router"
|
||||||
import useVuelidate from "@vuelidate/core"
|
import useVuelidate from "@vuelidate/core"
|
||||||
import { required } from "@vuelidate/validators"
|
import { required } from "@vuelidate/validators"
|
||||||
|
|
||||||
|
@ -130,23 +130,6 @@ const saveListing = async (navigate : boolean) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Whether the navigation confirmation is shown */
|
|
||||||
const confirmNavShown = ref(false)
|
|
||||||
|
|
||||||
/** The "next" route (will be navigated or cleared) */
|
|
||||||
const nextRoute : Ref<RouteLocationNormalized | undefined> = ref(undefined)
|
|
||||||
|
|
||||||
/** If the user has unsaved changes, give them an opportunity to save before moving on */
|
|
||||||
onBeforeRouteLeave(async (to, from) => { // eslint-disable-line
|
|
||||||
if (!v$.value.$anyDirty) return true
|
|
||||||
nextRoute.value = to
|
|
||||||
confirmNavShown.value = true
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
|
|
||||||
/** Parameterless save function (used to save when navigating away) */
|
/** Parameterless save function (used to save when navigating away) */
|
||||||
const doSave = async () => await saveListing(false)
|
const doSave = async () => await saveListing(false)
|
||||||
|
|
||||||
/** Close the navigation confirmation modal */
|
|
||||||
const confirmClose = () => { confirmNavShown.value = false }
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -18,12 +18,12 @@ article
|
||||||
.col-12
|
.col-12
|
||||||
button.btn.btn-primary(@click.prevent="expireListing").
|
button.btn.btn-primary(@click.prevent="expireListing").
|
||||||
#[icon(icon="text-box-remove-outline")] Expire Listing
|
#[icon(icon="text-box-remove-outline")] Expire Listing
|
||||||
maybe-save(:isShown="confirmNavShown" :toRoute="nextRoute" :saveAction="doSave" :validator="v$" @close="confirmClose")
|
maybe-save(:saveAction="doSave" :validator="v$")
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, reactive, Ref, ref } from "vue"
|
import { computed, reactive, Ref, ref } from "vue"
|
||||||
import { onBeforeRouteLeave, RouteLocationNormalized, useRoute, useRouter } from "vue-router"
|
import { useRoute, useRouter } from "vue-router"
|
||||||
import useVuelidate from "@vuelidate/core"
|
import useVuelidate from "@vuelidate/core"
|
||||||
|
|
||||||
import api, { Listing, ListingExpireForm, LogOnSuccess } from "@/api"
|
import api, { Listing, ListingExpireForm, LogOnSuccess } from "@/api"
|
||||||
|
@ -89,23 +89,6 @@ const expireListing = async (navigate : boolean) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Whether the navigation confirmation is shown */
|
|
||||||
const confirmNavShown = ref(false)
|
|
||||||
|
|
||||||
/** The "next" route (will be navigated or cleared) */
|
|
||||||
const nextRoute : Ref<RouteLocationNormalized | undefined> = ref(undefined)
|
|
||||||
|
|
||||||
/** Prompt for save if the user navigates away with unsaved changes */
|
|
||||||
onBeforeRouteLeave(async (to, from) => { // eslint-disable-line
|
|
||||||
if (!v$.value.$anyDirty) return true
|
|
||||||
nextRoute.value = to
|
|
||||||
confirmNavShown.value = true
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
|
|
||||||
/** No-parameter save function (used for save-on-navigate) */
|
/** No-parameter save function (used for save-on-navigate) */
|
||||||
const doSave = async () => await expireListing(false)
|
const doSave = async () => await expireListing(false)
|
||||||
|
|
||||||
/** Close the confirm navigation modal */
|
|
||||||
const confirmClose = () => { confirmNavShown.value = false }
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -15,12 +15,12 @@ article
|
||||||
button.btn.btn-primary(type="submit" @click.prevent="saveStory(true)").
|
button.btn.btn-primary(type="submit" @click.prevent="saveStory(true)").
|
||||||
#[icon(icon="content-save-outline")] Save
|
#[icon(icon="content-save-outline")] Save
|
||||||
p(v-if="isNew"): em (Saving this will set “Seeking Employment” to “No” on your profile.)
|
p(v-if="isNew"): em (Saving this will set “Seeking Employment” to “No” on your profile.)
|
||||||
maybe-save(:isShown="confirmNavShown" :toRoute="nextRoute" :saveAction="doSave" :validator="v$" @close="confirmClose")
|
maybe-save(:saveAction="doSave" :validator="v$")
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, reactive, ref, Ref } from "vue"
|
import { computed, reactive } from "vue"
|
||||||
import { onBeforeRouteLeave, RouteLocationNormalized, useRoute, useRouter } from "vue-router"
|
import { useRoute, useRouter } from "vue-router"
|
||||||
import useVuelidate from "@vuelidate/core"
|
import useVuelidate from "@vuelidate/core"
|
||||||
|
|
||||||
import api, { LogOnSuccess, StoryForm } from "@/api"
|
import api, { LogOnSuccess, StoryForm } from "@/api"
|
||||||
|
@ -106,23 +106,6 @@ const saveStory = async (navigate : boolean) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Whether the navigation confirmation is shown */
|
|
||||||
const confirmNavShown = ref(false)
|
|
||||||
|
|
||||||
/** The "next" route (will be navigated or cleared) */
|
|
||||||
const nextRoute : Ref<RouteLocationNormalized | undefined> = ref(undefined)
|
|
||||||
|
|
||||||
/** Prompt for save if the user navigates away with unsaved changes */
|
|
||||||
onBeforeRouteLeave(async (to, from) => { // eslint-disable-line
|
|
||||||
if (!v$.value.$anyDirty) return true
|
|
||||||
nextRoute.value = to
|
|
||||||
confirmNavShown.value = true
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
|
|
||||||
/** No-parameter save function (used for save-on-navigate) */
|
/** No-parameter save function (used for save-on-navigate) */
|
||||||
const doSave = async () => await saveStory(false)
|
const doSave = async () => await saveStory(false)
|
||||||
|
|
||||||
/** Close the confirm navigation modal */
|
|
||||||
const confirmClose = () => { confirmNavShown.value = false }
|
|
||||||
</script>
|
</script>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user