feat: remove all namespace leftovers
This commit is contained in:
@ -17,22 +17,11 @@
|
||||
@taskAdded="updateTaskKey"
|
||||
class="is-max-width-desktop"
|
||||
/>
|
||||
<template v-if="!hasTasks && !loading">
|
||||
<template v-if="defaultNamespaceId > 0">
|
||||
<p class="mt-4">{{ $t('home.project.newText') }}</p>
|
||||
<x-button
|
||||
:to="{ name: 'project.create', params: { namespaceId: defaultNamespaceId } }"
|
||||
:shadow="false"
|
||||
class="ml-2"
|
||||
>
|
||||
{{ $t('home.project.new') }}
|
||||
</x-button>
|
||||
</template>
|
||||
<p class="mt-4" v-if="migratorsEnabled">
|
||||
<template v-if="!hasTasks && !loading && migratorsEnabled">
|
||||
<p class="mt-4">
|
||||
{{ $t('home.project.importText') }}
|
||||
</p>
|
||||
<x-button
|
||||
v-if="migratorsEnabled"
|
||||
:to="{ name: 'migrate.start' }"
|
||||
:shadow="false">
|
||||
{{ $t('home.project.import') }}
|
||||
@ -66,7 +55,6 @@ import {useDaytimeSalutation} from '@/composables/useDaytimeSalutation'
|
||||
import {useBaseStore} from '@/stores/base'
|
||||
import {useProjectStore} from '@/stores/projects'
|
||||
import {useConfigStore} from '@/stores/config'
|
||||
import {useNamespaceStore} from '@/stores/namespaces'
|
||||
import {useAuthStore} from '@/stores/auth'
|
||||
import {useTaskStore} from '@/stores/tasks'
|
||||
import type {IProject} from '@/modelTypes/IProject'
|
||||
@ -76,7 +64,6 @@ const salutation = useDaytimeSalutation()
|
||||
const baseStore = useBaseStore()
|
||||
const authStore = useAuthStore()
|
||||
const configStore = useConfigStore()
|
||||
const namespaceStore = useNamespaceStore()
|
||||
const projectStore = useProjectStore()
|
||||
const taskStore = useTaskStore()
|
||||
|
||||
@ -93,8 +80,7 @@ const projectHistory = computed(() => {
|
||||
|
||||
const migratorsEnabled = computed(() => configStore.availableMigrators?.length > 0)
|
||||
const hasTasks = computed(() => baseStore.hasTasks)
|
||||
const defaultNamespaceId = computed(() => namespaceStore.namespaces?.[0]?.id || 0)
|
||||
const hasProjects = computed(() => namespaceStore.namespaces?.[0]?.projects.length > 0)
|
||||
const hasProjects = computed(() => projectStore.projects.length > 0)
|
||||
const loading = computed(() => taskStore.isLoading)
|
||||
const deletionScheduledAt = computed(() => parseDateOrNull(authStore.info?.deletionScheduledAt))
|
||||
|
||||
|
@ -89,8 +89,8 @@ import {formatDateLong} from '@/helpers/time/formatDate'
|
||||
import {parseDateOrNull} from '@/helpers/parseDateOrNull'
|
||||
|
||||
import {MIGRATORS} from './migrators'
|
||||
import {useNamespaceStore} from '@/stores/namespaces'
|
||||
import {useTitle} from '@/composables/useTitle'
|
||||
import {useProjectStore} from '@/stores/projects'
|
||||
|
||||
const PROGRESS_DOTS_COUNT = 8
|
||||
|
||||
@ -163,8 +163,8 @@ async function migrate() {
|
||||
? await migrationFileService.migrate(migrationConfig as File)
|
||||
: await migrationService.migrate(migrationConfig as MigrationConfig)
|
||||
message.value = result.message
|
||||
const namespaceStore = useNamespaceStore()
|
||||
return namespaceStore.loadNamespaces()
|
||||
const projectStore = useProjectStore()
|
||||
return projectStore.loadProjects()
|
||||
} finally {
|
||||
isMigrating.value = false
|
||||
}
|
||||
|
@ -1,84 +0,0 @@
|
||||
<template>
|
||||
<create-edit
|
||||
:title="$t('namespace.create.title')"
|
||||
@create="newNamespace()"
|
||||
:primary-disabled="namespace.title === ''"
|
||||
>
|
||||
<div class="field">
|
||||
<label class="label" for="namespaceTitle">{{ $t('namespace.attributes.title') }}</label>
|
||||
<div
|
||||
class="control is-expanded"
|
||||
:class="{ 'is-loading': namespaceService.loading }"
|
||||
>
|
||||
<!-- The user should be able to close the modal by pressing escape - that already works with the default modal.
|
||||
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()"
|
||||
class="input"
|
||||
:placeholder="$t('namespace.attributes.titlePlaceholder')"
|
||||
type="text"
|
||||
:class="{ disabled: namespaceService.loading }"
|
||||
v-focus
|
||||
v-model="namespace.title"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p class="help is-danger" v-if="showError && namespace.title === ''">
|
||||
{{ $t('namespace.create.titleRequired') }}
|
||||
</p>
|
||||
<div class="field">
|
||||
<label class="label">{{ $t('namespace.attributes.color') }}</label>
|
||||
<div class="control">
|
||||
<color-picker v-model="namespace.hexColor"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<message class="mt-4">
|
||||
<h4 class="title">{{ $t('namespace.create.tooltip') }}</h4>
|
||||
|
||||
{{ $t('namespace.create.explanation') }}
|
||||
</message>
|
||||
</create-edit>
|
||||
</template>
|
||||
|
||||
<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'
|
||||
import ColorPicker from '@/components/input/ColorPicker.vue'
|
||||
|
||||
import NamespaceModel from '@/models/namespace'
|
||||
import NamespaceService from '@/services/namespace'
|
||||
import {useNamespaceStore} from '@/stores/namespaces'
|
||||
import type {INamespace} from '@/modelTypes/INamespace'
|
||||
|
||||
import {useTitle} from '@/composables/useTitle'
|
||||
import {success} from '@/message'
|
||||
|
||||
const showError = ref(false)
|
||||
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() {
|
||||
if (namespace.value.title === '') {
|
||||
showError.value = true
|
||||
return
|
||||
}
|
||||
showError.value = false
|
||||
|
||||
const newNamespace = await namespaceService.create(namespace.value)
|
||||
useNamespaceStore().addNamespace(newNamespace)
|
||||
success({message: t('namespace.create.success')})
|
||||
router.back()
|
||||
}
|
||||
</script>
|
@ -1,89 +0,0 @@
|
||||
<template>
|
||||
<modal
|
||||
@close="$router.back()"
|
||||
@submit="archiveNamespace()"
|
||||
>
|
||||
<template #header><span>{{ title }}</span></template>
|
||||
|
||||
<template #text>
|
||||
<p>
|
||||
{{
|
||||
namespace.isArchived
|
||||
? $t('namespace.archive.unarchiveText')
|
||||
: $t('namespace.archive.archiveText')
|
||||
}}
|
||||
</p>
|
||||
</template>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default { name: 'namespace-setting-archive' }
|
||||
</script>
|
||||
|
||||
<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'
|
||||
import {useTitle} from '@/composables/useTitle'
|
||||
|
||||
import {useNamespaceStore} from '@/stores/namespaces'
|
||||
import NamespaceService from '@/services/namespace'
|
||||
import NamespaceModel from '@/models/namespace'
|
||||
import type {INamespace} from '@/modelTypes/INamespace'
|
||||
|
||||
const props = defineProps({
|
||||
namespaceId: {
|
||||
type: Number as PropType<INamespace['id']>,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const router = useRouter()
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
|
||||
const namespaceStore = useNamespaceStore()
|
||||
const namespaceService = shallowReactive(new NamespaceService())
|
||||
const namespace = ref<INamespace>(new NamespaceModel())
|
||||
|
||||
watch(
|
||||
() => props.namespaceId,
|
||||
async () => {
|
||||
namespace.value = namespaceStore.getNamespaceById(props.namespaceId) || new NamespaceModel()
|
||||
|
||||
// FIXME: ressouce should be loaded in store
|
||||
namespace.value = await namespaceService.get({id: props.namespaceId})
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
const title = computed(() => {
|
||||
if (!namespace.value) {
|
||||
return
|
||||
}
|
||||
return namespace.value.isArchived
|
||||
? t('namespace.archive.titleUnarchive', {namespace: namespace.value.title})
|
||||
: t('namespace.archive.titleArchive', {namespace: namespace.value.title})
|
||||
})
|
||||
useTitle(title)
|
||||
|
||||
async function archiveNamespace() {
|
||||
try {
|
||||
const isArchived = !namespace.value.isArchived
|
||||
const archivedNamespace = await namespaceService.update({
|
||||
...namespace.value,
|
||||
isArchived,
|
||||
})
|
||||
namespaceStore.setNamespaceById(archivedNamespace)
|
||||
success({
|
||||
message: isArchived
|
||||
? t('namespace.archive.success')
|
||||
: t('namespace.archive.unarchiveSuccess'),
|
||||
})
|
||||
} finally {
|
||||
router.back()
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,69 +0,0 @@
|
||||
<template>
|
||||
<modal
|
||||
@close="$router.back()"
|
||||
@submit="deleteNamespace()"
|
||||
>
|
||||
<template #header><span>{{ title }}</span></template>
|
||||
|
||||
<template #text>
|
||||
<p>{{ $t('namespace.delete.text1') }}<br/>
|
||||
{{ $t('namespace.delete.text2') }}</p>
|
||||
</template>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default { name: 'namespace-setting-delete' }
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ref, computed, watch, shallowReactive} from 'vue'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
import {useRouter} from 'vue-router'
|
||||
|
||||
import {useTitle} from '@/composables/useTitle'
|
||||
import {success} from '@/message'
|
||||
import {useNamespaceStore} from '@/stores/namespaces'
|
||||
import NamespaceModel from '@/models/namespace'
|
||||
import NamespaceService from '@/services/namespace'
|
||||
import type { INamespace } from '@/modelTypes/INamespace'
|
||||
|
||||
const props = defineProps({
|
||||
namespaceId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
const router = useRouter()
|
||||
const namespaceStore = useNamespaceStore()
|
||||
|
||||
const namespaceService = shallowReactive(new NamespaceService())
|
||||
const namespace = ref<INamespace>(new NamespaceModel())
|
||||
|
||||
watch(
|
||||
() => props.namespaceId,
|
||||
async () => {
|
||||
namespace.value = namespaceStore.getNamespaceById(props.namespaceId) || new NamespaceModel()
|
||||
|
||||
// FIXME: ressouce should be loaded in store
|
||||
namespace.value = await namespaceService.get({id: props.namespaceId})
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
const title = computed(() => {
|
||||
if (!namespace.value) {
|
||||
return
|
||||
}
|
||||
return t('namespace.delete.title', {namespace: namespace.value.title})
|
||||
})
|
||||
useTitle(title)
|
||||
|
||||
async function deleteNamespace() {
|
||||
await namespaceStore.deleteNamespace(namespace.value)
|
||||
success({message: t('namespace.delete.success')})
|
||||
router.push({name: 'home'})
|
||||
}
|
||||
</script>
|
@ -1,120 +0,0 @@
|
||||
<template>
|
||||
<create-edit
|
||||
:title="title"
|
||||
primary-icon=""
|
||||
:primary-label="$t('misc.save')"
|
||||
@primary="save"
|
||||
:tertiary="$t('misc.delete')"
|
||||
@tertiary="$router.push({ name: 'namespace.settings.delete', params: { id: $route.params.id } })"
|
||||
>
|
||||
<form @submit.prevent="save()">
|
||||
<div class="field">
|
||||
<label class="label" for="namespacetext">{{ $t('namespace.attributes.title') }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
:class="{ 'disabled': namespaceService.loading}"
|
||||
:disabled="namespaceService.loading || undefined"
|
||||
class="input"
|
||||
id="namespacetext"
|
||||
:placeholder="$t('namespace.attributes.titlePlaceholder')"
|
||||
type="text"
|
||||
v-focus
|
||||
v-model="namespace.title"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="namespacedescription">{{ $t('namespace.attributes.description') }}</label>
|
||||
<div class="control">
|
||||
<AsyncEditor
|
||||
:class="{ 'disabled': namespaceService.loading}"
|
||||
:preview-is-default="false"
|
||||
id="namespacedescription"
|
||||
:placeholder="$t('namespace.attributes.descriptionPlaceholder')"
|
||||
v-if="editorActive"
|
||||
v-model="namespace.description"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="isArchivedCheck">{{ $t('namespace.attributes.archived') }}</label>
|
||||
<div class="control">
|
||||
<fancycheckbox
|
||||
v-model="namespace.isArchived"
|
||||
v-tooltip="$t('namespace.archive.description')">
|
||||
{{ $t('namespace.attributes.isArchived') }}
|
||||
</fancycheckbox>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">{{ $t('namespace.attributes.color') }}</label>
|
||||
<div class="control">
|
||||
<color-picker v-model="namespace.hexColor"/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</create-edit>
|
||||
</template>
|
||||
|
||||
<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'
|
||||
import ColorPicker from '@/components/input/ColorPicker.vue'
|
||||
import CreateEdit from '@/components/misc/create-edit.vue'
|
||||
|
||||
import NamespaceService from '@/services/namespace'
|
||||
import NamespaceModel from '@/models/namespace'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
import {useTitle} from '@/composables/useTitle'
|
||||
import {useNamespaceStore} from '@/stores/namespaces'
|
||||
|
||||
import type {INamespace} from '@/modelTypes/INamespace'
|
||||
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
const namespaceStore = useNamespaceStore()
|
||||
|
||||
const namespaceService = ref(new NamespaceService())
|
||||
const namespace = ref<INamespace>(new NamespaceModel())
|
||||
const editorActive = ref(false)
|
||||
const title = ref('')
|
||||
useTitle(() => title.value)
|
||||
|
||||
const props = defineProps({
|
||||
namespaceId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.namespaceId,
|
||||
loadNamespace,
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
)
|
||||
|
||||
async function loadNamespace() {
|
||||
// HACK: This makes the editor trigger its mounted function again which makes it forget every input
|
||||
// it currently has in its textarea. This is a counter-hack to a hack inside of vue-easymde
|
||||
// which made it impossible to detect change from the outside. Therefore the component would
|
||||
// not update if new content from the outside was made available.
|
||||
// See https://github.com/NikulinIlya/vue-easymde/issues/3
|
||||
editorActive.value = false
|
||||
nextTick(() => editorActive.value = true)
|
||||
|
||||
namespace.value = await namespaceService.value.get({id: props.namespaceId})
|
||||
title.value = t('namespace.edit.title', {namespace: namespace.value.title})
|
||||
}
|
||||
|
||||
async function save() {
|
||||
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()
|
||||
}
|
||||
</script>
|
@ -1,67 +0,0 @@
|
||||
<template>
|
||||
<create-edit
|
||||
:title="title"
|
||||
:has-primary-action="false"
|
||||
>
|
||||
<template v-if="namespace">
|
||||
<manageSharing
|
||||
:id="namespace.id"
|
||||
:userIsAdmin="userIsAdmin"
|
||||
shareType="user"
|
||||
type="namespace"
|
||||
/>
|
||||
<manageSharing
|
||||
:id="namespace.id"
|
||||
:userIsAdmin="userIsAdmin"
|
||||
shareType="team"
|
||||
type="namespace"
|
||||
/>
|
||||
</template>
|
||||
</create-edit>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default { name: 'namespace-setting-share' }
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {ref, computed, watchEffect} from 'vue'
|
||||
import {useRoute} from 'vue-router'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
|
||||
import NamespaceService from '@/services/namespace'
|
||||
import NamespaceModel from '@/models/namespace'
|
||||
import type {INamespace} from '@/modelTypes/INamespace'
|
||||
import {RIGHTS} from '@/constants/rights'
|
||||
|
||||
import CreateEdit from '@/components/misc/create-edit.vue'
|
||||
import manageSharing from '@/components/sharing/userTeam.vue'
|
||||
import {useTitle} from '@/composables/useTitle'
|
||||
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
|
||||
const namespace = ref<INamespace>()
|
||||
|
||||
const title = computed(() => namespace.value?.title
|
||||
? t('namespace.share.title', { namespace: namespace.value.title })
|
||||
: '',
|
||||
)
|
||||
useTitle(title)
|
||||
|
||||
const userIsAdmin = computed(() => namespace?.value?.maxRight === RIGHTS.ADMIN)
|
||||
|
||||
async function loadNamespace(namespaceId: number) {
|
||||
if (!namespaceId) return
|
||||
const namespaceService = new NamespaceService()
|
||||
namespace.value = await namespaceService.get(new NamespaceModel({id: namespaceId}))
|
||||
|
||||
// TODO: set namespace in store
|
||||
}
|
||||
|
||||
const route = useRoute()
|
||||
const namespaceId = computed(() => route.params.namespaceId !== undefined
|
||||
? parseInt(route.params.namespaceId as string)
|
||||
: undefined,
|
||||
)
|
||||
watchEffect(() => namespaceId.value !== undefined && loadNamespace(namespaceId.value))
|
||||
</script>
|
@ -63,7 +63,6 @@ async function createNewProject() {
|
||||
}
|
||||
showError.value = false
|
||||
|
||||
project.namespaceId = Number(route.params.namespaceId as string)
|
||||
const newProject = await projectStore.createProject(project)
|
||||
await router.push({
|
||||
name: 'project.index',
|
||||
|
@ -108,7 +108,6 @@ import CustomTransition from '@/components/misc/CustomTransition.vue'
|
||||
|
||||
import {useBaseStore} from '@/stores/base'
|
||||
import {useProjectStore} from '@/stores/projects'
|
||||
import {useNamespaceStore} from '@/stores/namespaces'
|
||||
import {useConfigStore} from '@/stores/config'
|
||||
|
||||
import BackgroundUnsplashService from '@/services/backgroundUnsplash'
|
||||
@ -146,7 +145,6 @@ const debounceNewBackgroundSearch = debounce(newBackgroundSearch, SEARCH_DEBOUNC
|
||||
const backgroundUploadService = ref(new BackgroundUploadService())
|
||||
const projectService = ref(new ProjectService())
|
||||
const projectStore = useProjectStore()
|
||||
const namespaceStore = useNamespaceStore()
|
||||
const configStore = useConfigStore()
|
||||
|
||||
const unsplashBackgroundEnabled = computed(() => configStore.enabledBackgroundProviders.includes('unsplash'))
|
||||
@ -195,7 +193,6 @@ async function setBackground(backgroundId: string) {
|
||||
projectId: route.params.projectId,
|
||||
})
|
||||
await baseStore.handleSetCurrentProject({project, forceUpdate: true})
|
||||
namespaceStore.setProjectInNamespaceById(project)
|
||||
projectStore.setProject(project)
|
||||
success({message: t('project.background.success')})
|
||||
}
|
||||
@ -211,7 +208,6 @@ async function uploadBackground() {
|
||||
backgroundUploadInput.value?.files[0],
|
||||
)
|
||||
await baseStore.handleSetCurrentProject({project, forceUpdate: true})
|
||||
namespaceStore.setProjectInNamespaceById(project)
|
||||
projectStore.setProject(project)
|
||||
success({message: t('project.background.success')})
|
||||
}
|
||||
@ -219,7 +215,6 @@ async function uploadBackground() {
|
||||
async function removeBackground() {
|
||||
const project = await projectService.value.removeBackground(currentProject.value)
|
||||
await baseStore.handleSetCurrentProject({project, forceUpdate: true})
|
||||
namespaceStore.setProjectInNamespaceById(project)
|
||||
projectStore.setProject(project)
|
||||
success({message: t('project.background.removeSuccess')})
|
||||
router.back()
|
||||
|
@ -7,15 +7,6 @@
|
||||
:loading="projectDuplicateService.loading"
|
||||
>
|
||||
<p>{{ $t('project.duplicate.text') }}</p>
|
||||
|
||||
<Multiselect
|
||||
:placeholder="$t('namespace.search')"
|
||||
@search="findNamespaces"
|
||||
:search-results="namespaces"
|
||||
@select="selectNamespace"
|
||||
label="title"
|
||||
:search-delay="10"
|
||||
/>
|
||||
</create-edit>
|
||||
</template>
|
||||
|
||||
@ -29,32 +20,17 @@ import CreateEdit from '@/components/misc/create-edit.vue'
|
||||
import Multiselect from '@/components/input/multiselect.vue'
|
||||
|
||||
import ProjectDuplicateModel from '@/models/projectDuplicateModel'
|
||||
import type {INamespace} from '@/modelTypes/INamespace'
|
||||
|
||||
import {success} from '@/message'
|
||||
import {useTitle} from '@/composables/useTitle'
|
||||
import {useNamespaceSearch} from '@/composables/useNamespaceSearch'
|
||||
import {useProjectStore} from '@/stores/projects'
|
||||
import {useNamespaceStore} from '@/stores/namespaces'
|
||||
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
useTitle(() => t('project.duplicate.title'))
|
||||
|
||||
const {
|
||||
namespaces,
|
||||
findNamespaces,
|
||||
} = useNamespaceSearch()
|
||||
|
||||
const selectedNamespace = ref<INamespace>()
|
||||
|
||||
function selectNamespace(namespace: INamespace) {
|
||||
selectedNamespace.value = namespace
|
||||
}
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const projectStore = useProjectStore()
|
||||
const namespaceStore = useNamespaceStore()
|
||||
|
||||
const projectDuplicateService = shallowReactive(new ProjectDuplicateService())
|
||||
|
||||
@ -62,12 +38,10 @@ async function duplicateProject() {
|
||||
const projectDuplicate = new ProjectDuplicateModel({
|
||||
// FIXME: should be parameter
|
||||
projectId: route.params.projectId,
|
||||
namespaceId: selectedNamespace.value?.id,
|
||||
})
|
||||
|
||||
const duplicate = await projectDuplicateService.create(projectDuplicate)
|
||||
|
||||
namespaceStore.addProjectToNamespace(duplicate.project)
|
||||
projectStore.setProject(duplicate.project)
|
||||
success({message: t('project.duplicate.success')})
|
||||
router.push({name: 'project.index', params: {projectId: duplicate.project.id}})
|
||||
|
@ -13,8 +13,7 @@
|
||||
:can-write="canWrite"
|
||||
ref="heading"
|
||||
/>
|
||||
<h6 class="subtitle" v-if="parent && parent.namespace && parent.project">
|
||||
{{ getNamespaceTitle(parent.namespace) }} ›
|
||||
<h6 class="subtitle" v-if="parent && parent.project">
|
||||
<router-link :to="{ name: 'project.index', params: { projectId: parent.project.id } }">
|
||||
{{ getProjectTitle(parent.project) }}
|
||||
</router-link>
|
||||
@ -486,12 +485,10 @@ import TaskSubscription from '@/components/misc/subscription.vue'
|
||||
import CustomTransition from '@/components/misc/CustomTransition.vue'
|
||||
|
||||
import {uploadFile} from '@/helpers/attachments'
|
||||
import {getNamespaceTitle} from '@/helpers/getNamespaceTitle'
|
||||
import {getProjectTitle} from '@/helpers/getProjectTitle'
|
||||
import {scrollIntoView} from '@/helpers/scrollIntoView'
|
||||
|
||||
import {useBaseStore} from '@/stores/base'
|
||||
import {useNamespaceStore} from '@/stores/namespaces'
|
||||
import {useAttachmentStore} from '@/stores/attachments'
|
||||
import {useTaskStore} from '@/stores/tasks'
|
||||
import {useKanbanStore} from '@/stores/kanban'
|
||||
@ -500,6 +497,7 @@ import {useTitle} from '@/composables/useTitle'
|
||||
|
||||
import {success} from '@/message'
|
||||
import type {Action as MessageAction} from '@/message'
|
||||
import {useProjectStore} from '@/stores/projects'
|
||||
|
||||
const props = defineProps({
|
||||
taskId: {
|
||||
@ -517,7 +515,7 @@ const router = useRouter()
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
|
||||
const baseStore = useBaseStore()
|
||||
const namespaceStore = useNamespaceStore()
|
||||
const projectStore = useProjectStore()
|
||||
const attachmentStore = useAttachmentStore()
|
||||
const taskStore = useTaskStore()
|
||||
const kanbanStore = useKanbanStore()
|
||||
@ -541,16 +539,11 @@ const taskId = toRef(props, 'taskId')
|
||||
const parent = computed(() => {
|
||||
if (!task.projectId) {
|
||||
return {
|
||||
namespace: null,
|
||||
project: null,
|
||||
}
|
||||
}
|
||||
|
||||
if (!namespaceStore.getProjectAndNamespaceById) {
|
||||
return null
|
||||
}
|
||||
|
||||
return namespaceStore.getProjectAndNamespaceById(task.projectId)
|
||||
|
||||
return projectStore.getProjectById(task.projectId)
|
||||
})
|
||||
|
||||
watch(
|
||||
@ -775,7 +768,6 @@ async function toggleFavorite() {
|
||||
task.isFavorite = !task.isFavorite
|
||||
const newTask = await taskService.update(task)
|
||||
Object.assign(task, newTask)
|
||||
await namespaceStore.loadNamespacesIfFavoritesDontExist()
|
||||
}
|
||||
|
||||
async function setPriority(priority: Priority) {
|
||||
|
Reference in New Issue
Block a user