feat: route modals everywhere

This commit is contained in:
Dominik Pschenitschni 2022-11-14 19:02:28 +01:00
parent 61cdb7a91f
commit 6fc47ee8bd
Signed by: dpschen
GPG Key ID: B257AC0149F43A77
30 changed files with 155 additions and 137 deletions

View File

@ -19,7 +19,7 @@ describe('Team', () => {
.contains('Create a new team')
cy.get('input.input')
.type(newTeamName)
cy.get('.button')
cy.get('.new-team button')
.contains('Create')
.click()

View File

@ -0,0 +1,21 @@
<!-- https://github.com/vuejs/rfcs/pull/449 -->
<!-- https://github.com/vuejs/rfcs/discussions/448#discussioncomment-2769396= -->
<script lang="ts">
export default { inheritAttrs: false }
</script>
<script setup lang="ts">
import { useAttrs } from 'vue'
defineProps<{ is: any }>()
const attrs = useAttrs()
console.log(JSON.parse(JSON.stringify(attrs)))
</script>
<template>
<component v-if="is" :is="is" v-bind="$attrs">
<slot />
</component>
<slot v-else />
</template>

View File

@ -16,7 +16,7 @@
{{ currentList.title === '' ? $t('misc.loading') : getListTitle(currentList) }}
</h1>
<BaseButton :to="{name: 'list.info', params: {listId: currentList.id}}" class="info-button">
<BaseButton :to="{name: 'list.info', params: {listId: currentList.id}, state: {backdropView: $route.fullPath}}" class="info-button">
<icon icon="circle-info"/>
</BaseButton>
@ -75,7 +75,7 @@
{{ $t('keyboardShortcuts.title') }}
</dropdown-item>
<dropdown-item
:to="{name: 'about'}"
:to="{name: 'about', state: {backdropView: $route.fullPath}}"
>
{{ $t('about.title') }}
</dropdown-item>

View File

@ -38,14 +38,10 @@
</keep-alive>
</router-view>
<modal
:enabled="Boolean(currentModal)"
<component
:is="currentModal"
@close="closeModal()"
variant="scrolling"
class="task-detail-view-modal"
>
<component :is="currentModal"/>
</modal>
/>
<BaseButton
class="keyboard-shortcuts-button d-print-none"

View File

@ -10,13 +10,13 @@
<template v-if="isSavedFilter(list)">
<dropdown-item
:to="{ name: 'filter.settings.edit', params: { listId: list.id } }"
:to="{ name: 'filter.settings.edit', params: { listId: list.id }, state: {backdropView: $route.fullPath} }"
icon="pen"
>
{{ $t('menu.edit') }}
</dropdown-item>
<dropdown-item
:to="{ name: 'filter.settings.delete', params: { listId: list.id } }"
:to="{ name: 'filter.settings.delete', params: { listId: list.id }, state: {backdropView: $route.fullPath} }"
icon="trash-alt"
>
{{ $t('misc.delete') }}
@ -25,7 +25,7 @@
<template v-else-if="list.isArchived">
<dropdown-item
:to="{ name: 'list.settings.archive', params: { listId: list.id } }"
:to="{ name: 'list.settings.archive', params: { listId: list.id }, state: {backdropView: $route.fullPath} }"
icon="archive"
>
{{ $t('menu.unarchive') }}
@ -33,32 +33,32 @@
</template>
<template v-else>
<dropdown-item
:to="{ name: 'list.settings.edit', params: { listId: list.id } }"
:to="{ name: 'list.settings.edit', params: { listId: list.id }, state: {backdropView: $route.fullPath} }"
icon="pen"
>
{{ $t('menu.edit') }}
</dropdown-item>
<dropdown-item
v-if="backgroundsEnabled"
:to="{ name: 'list.settings.background', params: { listId: list.id } }"
:to="{ name: 'list.settings.background', params: { listId: list.id }, state: {backdropView: $route.fullPath} }"
icon="image"
>
{{ $t('menu.setBackground') }}
</dropdown-item>
<dropdown-item
:to="{ name: 'list.settings.share', params: { listId: list.id } }"
:to="{ name: 'list.settings.share', params: { listId: list.id }, state: {backdropView: $route.fullPath} }"
icon="share-alt"
>
{{ $t('menu.share') }}
</dropdown-item>
<dropdown-item
:to="{ name: 'list.settings.duplicate', params: { listId: list.id } }"
:to="{ name: 'list.settings.duplicate', params: { listId: list.id }, state: {backdropView: $route.fullPath} }"
icon="paste"
>
{{ $t('menu.duplicate') }}
</dropdown-item>
<dropdown-item
:to="{ name: 'list.settings.archive', params: { listId: list.id } }"
:to="{ name: 'list.settings.archive', params: { listId: list.id }, state: {backdropView: $route.fullPath} }"
icon="archive"
>
{{ $t('menu.archive') }}
@ -73,7 +73,7 @@
type="dropdown"
/>
<dropdown-item
:to="{ name: 'list.settings.delete', params: { listId: list.id } }"
:to="{ name: 'list.settings.delete', params: { listId: list.id }, state: {backdropView: $route.fullPath} }"
icon="trash-alt"
class="has-text-danger"
>

View File

@ -1,38 +1,38 @@
<template>
<modal @close="$router.back()" :overflow="true" :wide="wide">
<modal :overflow="true" :wide="wide" #default="{ onClose }">
<card
:title="title"
:shadow="false"
:padding="false"
class="has-text-left"
:has-close="true"
@close="$router.back()"
@close="onClose"
:loading="loading"
>
<div class="p-4">
<slot/>
<slot :onClose="onClose" />
</div>
<template #footer>
<slot name="footer">
<slot name="footer" :onClose="onClose">
<x-button
v-if="tertiary !== ''"
:shadow="false"
variant="tertiary"
@click.prevent.stop="$emit('tertiary')"
@click="$emit('tertiary')"
>
{{ tertiary }}
</x-button>
<x-button
variant="secondary"
@click.prevent.stop="$router.back()"
@click="onClose"
>
{{ $t('misc.cancel') }}
</x-button>
<x-button
v-if="hasPrimaryAction"
variant="primary"
@click.prevent.stop="primary()"
@click="primary(onClose)"
:icon="primaryIcon"
:disabled="primaryDisabled || loading"
class="ml-2"
@ -83,10 +83,11 @@ defineProps({
},
})
// 'close'
const emit = defineEmits(['create', 'primary', 'tertiary'])
function primary() {
emit('create')
emit('primary')
function primary(onClose: () => void) {
emit('create', onClose)
emit('primary', onClose)
}
</script>

View File

@ -1,7 +1,7 @@
<template>
<Teleport to="body">
<!-- FIXME: transition should not be included in the modal -->
<CustomTransition :name="transitionName" appear>
<CustomTransition :name="transitionName" appear :appear-class="transitionName">
<section
v-if="enabled"
class="modal-mask"
@ -10,11 +10,11 @@
variant,
]"
ref="modal"
v-bind="attrs"
v-bind="$attrs"
>
<div
class="modal-container"
@click.self.prevent.stop="$emit('close')"
@click.self.prevent.stop="onClose"
v-shortcut="'Escape'"
>
<div
@ -25,13 +25,13 @@
}"
>
<BaseButton
@click="$emit('close')"
@click="onClose"
class="close"
>
<icon icon="times"/>
</BaseButton>
<slot>
<slot name="default" :onClose="onClose">
<div class="header">
<slot name="header"></slot>
</div>
@ -40,14 +40,14 @@
</div>
<div class="actions">
<x-button
@click="$emit('close')"
@click="onClose"
variant="tertiary"
class="has-text-danger"
>
{{ $t('misc.cancel') }}
</x-button>
<x-button
@click="$emit('submit')"
@click="$emit('submit', onClose)"
variant="primary"
v-cy="'modalPrimary'"
:shadow="false"
@ -70,10 +70,11 @@ export default {
</script>
<script lang="ts" setup>
import {ref, watchEffect} from 'vue'
import {useScrollLock} from '@vueuse/core'
import CustomTransition from '@/components/misc/CustomTransition.vue'
import BaseButton from '@/components/base/BaseButton.vue'
import {ref, useAttrs, watchEffect} from 'vue'
import {useScrollLock} from '@vueuse/core'
const props = withDefaults(defineProps<{
enabled?: boolean,
@ -87,9 +88,7 @@ const props = withDefaults(defineProps<{
variant: 'default',
})
defineEmits(['close', 'submit'])
const attrs = useAttrs()
const emit = defineEmits(['close', 'submit'])
const modal = ref<HTMLElement | null>(null)
const scrollLock = useScrollLock(modal)
@ -97,6 +96,10 @@ const scrollLock = useScrollLock(modal)
watchEffect(() => {
scrollLock.value = props.enabled
})
function onClose() {
emit('close')
}
</script>
<style lang="scss" scoped>

View File

@ -10,7 +10,7 @@
<template v-if="namespace.isArchived">
<dropdown-item
:to="{ name: 'namespace.settings.archive', params: { id: namespace.id } }"
:to="{ name: 'namespace.settings.archive', params: { id: namespace.id }, state: { backdropView: $route.fullPath } }"
icon="archive"
>
{{ $t('menu.unarchive') }}
@ -18,25 +18,25 @@
</template>
<template v-else>
<dropdown-item
:to="{ name: 'namespace.settings.edit', params: { id: namespace.id } }"
:to="{ name: 'namespace.settings.edit', params: { id: namespace.id }, state: { backdropView: $route.fullPath } }"
icon="pen"
>
{{ $t('menu.edit') }}
</dropdown-item>
<dropdown-item
:to="{ name: 'namespace.settings.share', params: { namespaceId: namespace.id } }"
:to="{ name: 'namespace.settings.share', params: { namespaceId: namespace.id }, state: { backdropView: $route.fullPath } }"
icon="share-alt"
>
{{ $t('menu.share') }}
</dropdown-item>
<dropdown-item
:to="{ name: 'list.create', params: { namespaceId: namespace.id } }"
:to="{ name: 'list.create', params: { namespaceId: namespace.id }, state: { backdropView: $route.fullPath } }"
icon="plus"
>
{{ $t('menu.newList') }}
</dropdown-item>
<dropdown-item
:to="{ name: 'namespace.settings.archive', params: { id: namespace.id } }"
:to="{ name: 'namespace.settings.archive', params: { id: namespace.id }, state: { backdropView: $route.fullPath } }"
icon="archive"
>
{{ $t('menu.archive') }}
@ -51,7 +51,7 @@
type="dropdown"
/>
<dropdown-item
:to="{ name: 'namespace.settings.delete', params: { id: namespace.id } }"
:to="{ name: 'namespace.settings.delete', params: { id: namespace.id }, state: { backdropView: $route.fullPath } }"
icon="trash-alt"
class="has-text-danger"
>

View File

@ -44,10 +44,10 @@ export function useRouteWithModal() {
function closeModal() {
const historyState = computed(() => route.fullPath && window.history.state)
if (historyState.value) {
if (historyState.value === undefined) {
router.back()
} else {
const backdropRoute = historyState.value?.backdropView && router.resolve(historyState.value.backdropView)
const backdropRoute = historyState.value.backdropView && router.resolve(historyState.value.backdropView)
router.push(backdropRoute)
}
}

View File

@ -113,7 +113,7 @@ export function useSavedFilter(listId?: MaybeRef<IList['id']>) {
router.push({name: 'list.index', params: {listId: getListId(filter.value)}})
}
async function saveFilter() {
async function saveFilter(callback: () => void) {
const response = await filterService.update(filter.value)
await namespaceStore.loadNamespaces()
success({message: t('filters.edit.success')})
@ -123,7 +123,7 @@ export function useSavedFilter(listId?: MaybeRef<IList['id']>) {
id: getListId(filter.value),
title: filter.value.title,
}))
router.back()
callback()
}
async function deleteFilter() {

View File

@ -1,14 +1,14 @@
<template>
<modal
@close="$router.back()"
transition-name="fade"
variant="hint-modal"
>
>
<template #default="{onClose}">
<card
class="has-no-shadow"
:title="$t('about.title')"
:has-close="true"
@close="$router.back()"
@close="onClose"
:padding="false"
>
<div class="p-4">
@ -22,12 +22,13 @@
<template #footer>
<x-button
variant="secondary"
@click.prevent.stop="$router.back()"
@click="onClose"
>
{{ $t('misc.close') }}
</x-button>
</template>
</card>
</template>
</modal>
</template>

View File

@ -21,7 +21,7 @@
<template v-if="defaultNamespaceId > 0">
<p class="mt-4">{{ $t('home.list.newText') }}</p>
<x-button
:to="{ name: 'list.create', params: { namespaceId: defaultNamespaceId } }"
:to="{ name: 'list.create', params: { namespaceId: defaultNamespaceId }, state: { backdropView: $route.fullPath } }"
:shadow="false"
class="ml-2"
>

View File

@ -1,8 +1,5 @@
<template>
<modal
@close="$router.back()"
@submit="deleteFilter()"
>
<modal @submit="deleteFilter()">
<template #header>
<span>{{ $t('filters.delete.header') }}</span>
</template>

View File

@ -6,15 +6,16 @@
@primary="saveFilter"
:tertiary="$t('misc.delete')"
@tertiary="$router.push({ name: 'filter.settings.delete', params: { id: listId } })"
#default="{onClose}"
>
<form @submit.prevent="saveFilter()">
<form @submit.prevent="saveFilter(onClose)">
<div class="field">
<label class="label" for="title">{{ $t('filters.attributes.title') }}</label>
<div class="control">
<input
:class="{ 'disabled': filterService.loading}"
:disabled="filterService.loading || undefined"
@keyup.enter="saveFilter"
@keyup.enter="saveFilter(onClose)"
class="input"
id="title"
:placeholder="$t('filters.attributes.titlePlaceholder')"

View File

@ -1,8 +1,5 @@
<template>
<modal
@close="$router.back()"
variant="hint-modal"
>
<modal variant="hint-modal">
<card class="has-no-shadow" :title="$t('filters.create.title')">
<p>
{{ $t('filters.create.description') }}

View File

@ -1,7 +1,10 @@
<template>
<div :class="{ 'is-loading': loading}" class="loader-container">
<x-button
:to="{name:'labels.create'}"
:to="{
name:'labels.create',
state: { backdropView: $route.fullPath }
}"
class="is-pulled-right"
icon="plus"
>
@ -15,7 +18,10 @@
</p>
<p v-else class="has-text-centered has-text-grey is-italic">
{{ $t('label.newCTA') }}
<router-link :to="{name:'labels.create'}">{{ $t('label.create.title') }}.</router-link>
<router-link :to="{
name:'labels.create',
state: { backdropView: $route.fullPath }
}">{{ $t('label.create.title') }}.</router-link>
</p>
</div>

View File

@ -1,10 +1,6 @@
<template>
<modal
@close="$router.back()"
>
<card
:title="list.title"
>
<modal>
<card :title="list.title">
<div class="has-text-left" v-html="htmlDescription" v-if="htmlDescription !== ''"></div>
<p v-else class="is-italic">
{{ $t('list.noDescriptionAvailable') }}

View File

@ -1,5 +1,10 @@
<template>
<create-edit :title="$t('list.create.header')" @create="createNewList()" :primary-disabled="list.title === ''">
<create-edit
:title="$t('list.create.header')"
@create="createNewList()"
:primary-disabled="list.title === ''"
#default="{onClose}"
>
<div class="field">
<label class="label" for="listTitle">{{ $t('list.title') }}</label>
<div
@ -9,7 +14,7 @@
<input
:class="{ disabled: listService.loading }"
@keyup.enter="createNewList()"
@keyup.esc="$router.back()"
@keyup.esc="onClose"
class="input"
:placeholder="$t('list.create.titlePlaceholder')"
type="text"

View File

@ -1,8 +1,5 @@
<template>
<modal
@close="$router.back()"
@submit="archiveList()"
>
<modal @submit="archiveList">
<template #header><span>{{ list.isArchived ? $t('list.archive.unarchive') : $t('list.archive.archive') }}</span></template>
<template #text>
@ -17,7 +14,7 @@ export default {name: 'list-setting-archive'}
<script setup lang="ts">
import {computed} from 'vue'
import {useRouter, useRoute} from 'vue-router'
import {useRoute} from 'vue-router'
import {useI18n} from 'vue-i18n'
import {success} from '@/message'
@ -28,13 +25,12 @@ import {useListStore} from '@/stores/lists'
const {t} = useI18n({useScope: 'global'})
const listStore = useListStore()
const router = useRouter()
const route = useRoute()
const list = computed(() => listStore.getListById(route.params.listId))
useTitle(() => t('list.archive.title', {list: list.value.title}))
async function archiveList() {
async function archiveList(onClose: () => void) {
try {
const newList = await listStore.updateList({
...list.value,
@ -43,7 +39,7 @@ async function archiveList() {
useBaseStore().setCurrentList(newList)
success({message: t('list.archive.success')})
} finally {
router.back()
onClose()
}
}
</script>

View File

@ -73,19 +73,19 @@
</x-button>
</template>
<template #footer>
<template #footer="{onClose}">
<x-button
v-if="hasBackground"
:shadow="false"
variant="tertiary"
class="is-danger"
@click.prevent.stop="removeBackground"
@click="removeBackground(onClose)"
>
{{ $t('list.background.remove') }}
</x-button>
<x-button
variant="secondary"
@click.prevent.stop="$router.back()"
@click="onClose"
>
{{ $t('misc.close') }}
</x-button>
@ -100,7 +100,7 @@ export default { name: 'list-setting-background' }
<script setup lang="ts">
import {ref, computed, shallowReactive} from 'vue'
import {useI18n} from 'vue-i18n'
import {useRoute, useRouter} from 'vue-router'
import {useRoute} from 'vue-router'
import debounce from 'lodash.debounce'
import BaseButton from '@/components/base/BaseButton.vue'
@ -127,7 +127,6 @@ const SEARCH_DEBOUNCE = 300
const {t} = useI18n({useScope: 'global'})
const baseStore = useBaseStore()
const route = useRoute()
const router = useRouter()
useTitle(() => t('list.background.title'))
@ -216,13 +215,13 @@ async function uploadBackground() {
success({message: t('list.background.success')})
}
async function removeBackground() {
async function removeBackground(onClose: () => void) {
const list = await listService.value.removeBackground(currentList.value)
await baseStore.handleSetCurrentList({list, forceUpdate: true})
namespaceStore.setListInNamespaceById(list)
listStore.setList(list)
success({message: t('list.background.removeSuccess')})
router.back()
onClose()
}
</script>

View File

@ -1,8 +1,5 @@
<template>
<modal
@close="$router.back()"
@submit="deleteList()"
>
<modal @submit="deleteList()">
<template #header><span>{{ $t('list.delete.header') }}</span></template>
<template #text>

View File

@ -5,7 +5,8 @@
:primary-label="$t('misc.save')"
@primary="save"
:tertiary="$t('misc.delete')"
@tertiary="$router.push({ name: 'list.settings.delete', params: { id: listId } })"
@tertiary="$router.push({ name: 'list.settings.delete', params: { id: listId }, state: {backdropView: $route.fullPath} })"
#default="{onClose}"
>
<div class="field">
<label class="label" for="title">{{ $t('list.title') }}</label>
@ -13,7 +14,7 @@
<input
:class="{ 'disabled': isLoading}"
:disabled="isLoading || undefined"
@keyup.enter="save"
@keyup.enter="save(onClose)"
class="input"
id="title"
:placeholder="$t('list.edit.titlePlaceholder')"
@ -33,7 +34,7 @@
<input
:class="{ 'disabled': isLoading}"
:disabled="isLoading || undefined"
@keyup.enter="save"
@keyup.enter="save(onClose)"
class="input"
id="identifier"
:placeholder="$t('list.edit.identifierPlaceholder')"
@ -71,7 +72,6 @@ export default { name: 'list-setting-edit' }
<script setup lang="ts">
import type {PropType} from 'vue'
import {useRouter} from 'vue-router'
import {useI18n} from 'vue-i18n'
import Editor from '@/components/input/AsyncEditor'
@ -92,17 +92,15 @@ const props = defineProps({
},
})
const router = useRouter()
const {t} = useI18n({useScope: 'global'})
const {list, save: saveList, isLoading} = useList(props.listId)
useTitle(() => list?.title ? t('list.edit.title', {list: list.title}) : '')
async function save() {
async function save(onClose: () => void) {
await saveList()
await useBaseStore().handleSetCurrentList({list})
router.back()
onClose()
}
</script>

View File

@ -6,10 +6,10 @@
</fancycheckbox>
<div class="action-buttons">
<x-button :to="{name: 'filters.create'}" icon="filter">
<x-button :to="{name: 'filters.create', state: {backdropView: $route.fullPath}}" icon="filter">
{{ $t('filters.create.title') }}
</x-button>
<x-button :to="{name: 'namespace.create'}" icon="plus" v-cy="'new-namespace'">
<x-button :to="{name: 'namespace.create', state: {backdropView: $route.fullPath}}" icon="plus" v-cy="'new-namespace'">
{{ $t('namespace.create.title') }}
</x-button>
</div>
@ -17,7 +17,7 @@
<p v-if="namespaces.length === 0" class="has-text-centered has-text-grey mt-4 is-italic">
{{ $t('namespace.noneAvailable') }}
<BaseButton :to="{name: 'namespace.create'}">
<BaseButton :to="{name: 'namespace.create', state: {backdropView: $route.fullPath}}">
{{ $t('namespace.create.title') }}.
</BaseButton>
</p>
@ -25,7 +25,7 @@
<section :key="`n${n.id}`" class="namespace" v-for="n in namespaces">
<x-button
v-if="n.id > 0 && n.lists.length > 0"
:to="{name: 'list.create', params: {namespaceId: n.id}}"
:to="{name: 'list.create', params: {namespaceId: n.id}, state: { backdropView: $route.fullPath }}"
class="is-pulled-right"
variant="secondary"
icon="plus"
@ -34,7 +34,7 @@
</x-button>
<x-button
v-if="n.isArchived"
:to="{name: 'namespace.settings.archive', params: {id: n.id}}"
:to="{name: 'namespace.settings.archive', params: {id: n.id}, state: { backdropView: $route.fullPath } }"
class="is-pulled-right mr-4"
variant="secondary"
icon="archive"
@ -51,7 +51,7 @@
<p v-if="n.lists.length === 0" class="has-text-centered has-text-grey mt-4 is-italic">
{{ $t('namespace.noLists') }}
<BaseButton :to="{name: 'list.create', params: {namespaceId: n.id}}">
<BaseButton :to="{name: 'list.create', params: {namespaceId: n.id}, state: { backdropView: $route.fullPath }}">
{{ $t('namespace.createList') }}
</BaseButton>
</p>

View File

@ -1,8 +1,9 @@
<template>
<create-edit
:title="$t('namespace.create.title')"
@create="newNamespace()"
@create="newNamespace"
:primary-disabled="namespace.title === ''"
#default="{onClose}"
>
<div class="field">
<label class="label" for="namespaceTitle">{{ $t('namespace.attributes.title') }}</label>
@ -14,8 +15,8 @@
But with the input modal here since it autofocuses the input that input field catches the focus instead.
Hence we place the listener on the input field directly. -->
<input
@keyup.enter="newNamespace()"
@keyup.esc="$router.back()"
@keyup.enter="newNamespace(onClose)"
@keyup.esc="onClose"
class="input"
:placeholder="$t('namespace.attributes.titlePlaceholder')"
type="text"
@ -46,7 +47,6 @@
<script setup lang="ts">
import {ref, shallowReactive} from 'vue'
import {useI18n} from 'vue-i18n'
import {useRouter} from 'vue-router'
import Message from '@/components/misc/message.vue'
import CreateEdit from '@/components/misc/create-edit.vue'
@ -65,11 +65,10 @@ const namespace = ref<INamespace>(new NamespaceModel())
const namespaceService = shallowReactive(new NamespaceService())
const {t} = useI18n({useScope: 'global'})
const router = useRouter()
useTitle(() => t('namespace.create.title'))
async function newNamespace() {
async function newNamespace(onClose: () => void) {
if (namespace.value.title === '') {
showError.value = true
return
@ -79,6 +78,6 @@ async function newNamespace() {
const newNamespace = await namespaceService.create(namespace.value)
useNamespaceStore().addNamespace(newNamespace)
success({message: t('namespace.create.success')})
router.back()
onClose()
}
</script>

View File

@ -1,8 +1,5 @@
<template>
<modal
@close="$router.back()"
@submit="archiveNamespace()"
>
<modal @submit="archiveNamespace">
<template #header><span>{{ title }}</span></template>
<template #text>
@ -23,7 +20,6 @@ export default { name: 'namespace-setting-archive' }
<script setup lang="ts">
import {watch, ref, computed, shallowReactive, type PropType} from 'vue'
import {useRouter} from 'vue-router'
import {useI18n} from 'vue-i18n'
import {success} from '@/message'
@ -41,7 +37,6 @@ const props = defineProps({
},
})
const router = useRouter()
const {t} = useI18n({useScope: 'global'})
const namespaceStore = useNamespaceStore()
@ -69,7 +64,7 @@ const title = computed(() => {
})
useTitle(title)
async function archiveNamespace() {
async function archiveNamespace(onClose: () => void) {
try {
const isArchived = !namespace.value.isArchived
const archivedNamespace = await namespaceService.update({
@ -83,7 +78,7 @@ async function archiveNamespace() {
: t('namespace.archive.unarchiveSuccess'),
})
} finally {
router.back()
onClose()
}
}
</script>

View File

@ -1,8 +1,5 @@
<template>
<modal
@close="$router.back()"
@submit="deleteNamespace()"
>
<modal @submit="deleteNamespace()">
<template #header><span>{{ title }}</span></template>
<template #text>

View File

@ -5,9 +5,10 @@
:primary-label="$t('misc.save')"
@primary="save"
:tertiary="$t('misc.delete')"
@tertiary="$router.push({ name: 'namespace.settings.delete', params: { id: $route.params.id } })"
@tertiary="$router.push({ name: 'namespace.settings.delete', params: { id: $route.params.id }, state: { backdropView: $route.fullPath } })"
#default="{onClose}"
>
<form @submit.prevent="save()">
<form @submit.prevent="save(onClose)">
<div class="field">
<label class="label" for="namespacetext">{{ $t('namespace.attributes.title') }}</label>
<div class="control">
@ -58,7 +59,6 @@
<script lang="ts" setup>
import {nextTick, ref, watch} from 'vue'
import {success} from '@/message'
import router from '@/router'
import AsyncEditor from '@/components/input/AsyncEditor'
import Fancycheckbox from '@/components/input/fancycheckbox.vue'
@ -110,11 +110,11 @@ async function loadNamespace() {
title.value = t('namespace.edit.title', {namespace: namespace.value.title})
}
async function save() {
async function save(onClose: () => void) {
const updatedNamespace = await namespaceService.value.update(namespace.value)
// Update the namespace in the parent
namespaceStore.setNamespaceById(updatedNamespace)
success({message: t('namespace.edit.success')})
router.back()
onClose()
}
</script>

View File

@ -1,4 +1,5 @@
<template>
<OptionalWrapper :is="isModal && Modal" variant="scrolling">
<div
class="loader-container task-view-container"
:class="{
@ -442,10 +443,11 @@
</template>
</modal>
</div>
</OptionalWrapper>
</template>
<script lang="ts" setup>
import {ref, reactive, toRef, shallowReactive, computed, watch, nextTick, type PropType} from 'vue'
import {ref, reactive, toRef, shallowReactive, computed, watch, nextTick, type PropType, useAttrs} from 'vue'
import {useRouter, type RouteLocation} from 'vue-router'
import {useI18n} from 'vue-i18n'
import {unrefElement} from '@vueuse/core'
@ -480,7 +482,10 @@ import RelatedTasks from '@/components/tasks/partials/relatedTasks.vue'
import Reminders from '@/components/tasks/partials/reminders.vue'
import RepeatAfter from '@/components/tasks/partials/repeatAfter.vue'
import TaskSubscription from '@/components/misc/subscription.vue'
import OptionalWrapper from '@/components/base/OptionalWrapper.vue'
import CustomTransition from '@/components/misc/CustomTransition.vue'
import Modal from '@/components/misc/modal.vue'
import {uploadFile} from '@/helpers/attachments'
import {getNamespaceTitle} from '@/helpers/getNamespaceTitle'
@ -508,7 +513,8 @@ const props = defineProps({
})
defineEmits(['close'])
const attrs = useAttrs()
console.log(JSON.parse(JSON.stringify(attrs)))
const router = useRouter()
const {t} = useI18n({useScope: 'global'})

View File

@ -1,7 +1,10 @@
<template>
<div class="content loader-container is-max-width-desktop" :class="{ 'is-loading': teamService.loading}">
<x-button
:to="{name:'teams.create'}"
:to="{
name:'teams.create',
state: { backdropView: $route.fullPath },
}"
class="is-pulled-right"
icon="plus"
>
@ -18,7 +21,10 @@
</ul>
<p v-else-if="!teamService.loading" class="has-text-centered has-text-grey is-italic">
{{ $t('team.noTeams') }}
<router-link :to="{name: 'teams.create'}">
<router-link :to="{
name: 'teams.create',
state: { backdropView: $route.fullPath },
}">
{{ $t('team.create.title') }}.
</router-link>
</p>

View File

@ -1,5 +1,6 @@
<template>
<create-edit
class="new-team"
:title="title"
@create="newTeam()"
:primary-disabled="team.name === ''"