feat: remove all namespace leftovers
This commit is contained in:
parent
a5e710bfe5
commit
1bd17d6e50
@ -94,7 +94,6 @@ watch(() => route.name as string, (routeName) => {
|
|||||||
(
|
(
|
||||||
[
|
[
|
||||||
'home',
|
'home',
|
||||||
'namespace.edit',
|
|
||||||
'teams.index',
|
'teams.index',
|
||||||
'teams.edit',
|
'teams.edit',
|
||||||
'tasks.range',
|
'tasks.range',
|
||||||
|
@ -159,12 +159,10 @@ import type {SortableEvent} from 'sortablejs'
|
|||||||
|
|
||||||
import BaseButton from '@/components/base/BaseButton.vue'
|
import BaseButton from '@/components/base/BaseButton.vue'
|
||||||
import ProjectSettingsDropdown from '@/components/project/project-settings-dropdown.vue'
|
import ProjectSettingsDropdown from '@/components/project/project-settings-dropdown.vue'
|
||||||
import NamespaceSettingsDropdown from '@/components/namespace/namespace-settings-dropdown.vue'
|
|
||||||
import PoweredByLink from '@/components/home/PoweredByLink.vue'
|
import PoweredByLink from '@/components/home/PoweredByLink.vue'
|
||||||
import Logo from '@/components/home/Logo.vue'
|
import Logo from '@/components/home/Logo.vue'
|
||||||
|
|
||||||
import {calculateItemPosition} from '@/helpers/calculateItemPosition'
|
import {calculateItemPosition} from '@/helpers/calculateItemPosition'
|
||||||
import {getNamespaceTitle} from '@/helpers/getNamespaceTitle'
|
|
||||||
import {getProjectTitle} from '@/helpers/getProjectTitle'
|
import {getProjectTitle} from '@/helpers/getProjectTitle'
|
||||||
import type {IProject} from '@/modelTypes/IProject'
|
import type {IProject} from '@/modelTypes/IProject'
|
||||||
import type {INamespace} from '@/modelTypes/INamespace'
|
import type {INamespace} from '@/modelTypes/INamespace'
|
||||||
@ -172,7 +170,6 @@ import ColorBubble from '@/components/misc/colorBubble.vue'
|
|||||||
|
|
||||||
import {useBaseStore} from '@/stores/base'
|
import {useBaseStore} from '@/stores/base'
|
||||||
import {useProjectStore} from '@/stores/projects'
|
import {useProjectStore} from '@/stores/projects'
|
||||||
import {useNamespaceStore} from '@/stores/namespaces'
|
|
||||||
|
|
||||||
const drag = ref(false)
|
const drag = ref(false)
|
||||||
const dragOptions = {
|
const dragOptions = {
|
||||||
@ -199,7 +196,7 @@ const activeProjects = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const namespaceTitles = computed(() => {
|
const namespaceTitles = computed(() => {
|
||||||
return namespaces.value.map((namespace) => getNamespaceTitle(namespace))
|
return []
|
||||||
})
|
})
|
||||||
|
|
||||||
const namespaceProjectsCount = computed(() => {
|
const namespaceProjectsCount = computed(() => {
|
||||||
|
@ -1,63 +0,0 @@
|
|||||||
<template>
|
|
||||||
<multiselect
|
|
||||||
v-model="selectedNamespaces"
|
|
||||||
:search-results="foundNamespaces"
|
|
||||||
:loading="namespaceService.loading"
|
|
||||||
:multiple="true"
|
|
||||||
:placeholder="$t('namespace.search')"
|
|
||||||
label="namespace"
|
|
||||||
@search="findNamespaces"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import {computed, ref, shallowReactive, watchEffect, type PropType} from 'vue'
|
|
||||||
|
|
||||||
import Multiselect from '@/components/input/multiselect.vue'
|
|
||||||
|
|
||||||
import type {INamespace} from '@/modelTypes/INamespace'
|
|
||||||
|
|
||||||
import NamespaceService from '@/services/namespace'
|
|
||||||
import {includesById} from '@/helpers/utils'
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
modelValue: {
|
|
||||||
type: Array as PropType<INamespace[]>,
|
|
||||||
default: () => [],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
const emit = defineEmits<{
|
|
||||||
(e: 'update:modelValue', value: INamespace[]): void
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const namespaces = ref<INamespace[]>([])
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
namespaces.value = props.modelValue
|
|
||||||
})
|
|
||||||
|
|
||||||
const selectedNamespaces = computed({
|
|
||||||
get() {
|
|
||||||
return namespaces.value
|
|
||||||
},
|
|
||||||
set: (value) => {
|
|
||||||
namespaces.value = value
|
|
||||||
emit('update:modelValue', value)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const namespaceService = shallowReactive(new NamespaceService())
|
|
||||||
const foundNamespaces = ref<INamespace[]>([])
|
|
||||||
|
|
||||||
async function findNamespaces(query: string) {
|
|
||||||
if (query === '') {
|
|
||||||
foundNamespaces.value = []
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await namespaceService.getAll({}, {s: query}) as INamespace[]
|
|
||||||
|
|
||||||
// Filter selected items from the results
|
|
||||||
foundNamespaces.value = response.filter(({id}) => !includesById(namespaces.value, id))
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -44,8 +44,8 @@ export const KEYBOARD_SHORTCUTS : ShortcutGroup[] = [
|
|||||||
combination: 'then',
|
combination: 'then',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'keyboardShortcuts.navigation.namespaces',
|
title: 'keyboardShortcuts.navigation.projects',
|
||||||
keys: ['g', 'n'],
|
keys: ['g', 'p'],
|
||||||
combination: 'then',
|
combination: 'then',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -73,12 +73,6 @@ const {t} = useI18n({useScope: 'global'})
|
|||||||
|
|
||||||
const tooltipText = computed(() => {
|
const tooltipText = computed(() => {
|
||||||
if (disabled.value) {
|
if (disabled.value) {
|
||||||
if (props.entity === 'project' && subscriptionEntity.value === 'namespace') {
|
|
||||||
return t('task.subscription.subscribedProjectThroughParentNamespace')
|
|
||||||
}
|
|
||||||
if (props.entity === 'task' && subscriptionEntity.value === 'namespace') {
|
|
||||||
return t('task.subscription.subscribedTaskThroughParentNamespace')
|
|
||||||
}
|
|
||||||
if (props.entity === 'task' && subscriptionEntity.value === 'project') {
|
if (props.entity === 'task' && subscriptionEntity.value === 'project') {
|
||||||
return t('task.subscription.subscribedTaskThroughParentProject')
|
return t('task.subscription.subscribedTaskThroughParentProject')
|
||||||
}
|
}
|
||||||
@ -87,10 +81,6 @@ const tooltipText = computed(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (props.entity) {
|
switch (props.entity) {
|
||||||
case 'namespace':
|
|
||||||
return props.modelValue !== null ?
|
|
||||||
t('task.subscription.subscribedNamespace') :
|
|
||||||
t('task.subscription.notSubscribedNamespace')
|
|
||||||
case 'project':
|
case 'project':
|
||||||
return props.modelValue !== null ?
|
return props.modelValue !== null ?
|
||||||
t('task.subscription.subscribedProject') :
|
t('task.subscription.subscribedProject') :
|
||||||
@ -130,9 +120,6 @@ async function subscribe() {
|
|||||||
|
|
||||||
let message = ''
|
let message = ''
|
||||||
switch (props.entity) {
|
switch (props.entity) {
|
||||||
case 'namespace':
|
|
||||||
message = t('task.subscription.subscribeSuccessNamespace')
|
|
||||||
break
|
|
||||||
case 'project':
|
case 'project':
|
||||||
message = t('task.subscription.subscribeSuccessProject')
|
message = t('task.subscription.subscribeSuccessProject')
|
||||||
break
|
break
|
||||||
@ -153,9 +140,6 @@ async function unsubscribe() {
|
|||||||
|
|
||||||
let message = ''
|
let message = ''
|
||||||
switch (props.entity) {
|
switch (props.entity) {
|
||||||
case 'namespace':
|
|
||||||
message = t('task.subscription.unsubscribeSuccessNamespace')
|
|
||||||
break
|
|
||||||
case 'project':
|
case 'project':
|
||||||
message = t('task.subscription.unsubscribeSuccessProject')
|
message = t('task.subscription.unsubscribeSuccessProject')
|
||||||
break
|
break
|
||||||
|
@ -1,103 +0,0 @@
|
|||||||
<template>
|
|
||||||
<dropdown>
|
|
||||||
<template #trigger="triggerProps">
|
|
||||||
<slot name="trigger" v-bind="triggerProps">
|
|
||||||
<BaseButton class="dropdown-trigger" @click="triggerProps.toggleOpen">
|
|
||||||
<icon icon="ellipsis-h" class="icon"/>
|
|
||||||
</BaseButton>
|
|
||||||
</slot>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-if="namespace.isArchived">
|
|
||||||
<dropdown-item
|
|
||||||
:to="{ name: 'namespace.settings.archive', params: { id: namespace.id } }"
|
|
||||||
icon="archive"
|
|
||||||
>
|
|
||||||
{{ $t('menu.unarchive') }}
|
|
||||||
</dropdown-item>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<dropdown-item
|
|
||||||
:to="{ name: 'namespace.settings.edit', params: { id: namespace.id } }"
|
|
||||||
icon="pen"
|
|
||||||
>
|
|
||||||
{{ $t('menu.edit') }}
|
|
||||||
</dropdown-item>
|
|
||||||
<dropdown-item
|
|
||||||
:to="{ name: 'namespace.settings.share', params: { namespaceId: namespace.id } }"
|
|
||||||
icon="share-alt"
|
|
||||||
>
|
|
||||||
{{ $t('menu.share') }}
|
|
||||||
</dropdown-item>
|
|
||||||
<dropdown-item
|
|
||||||
:to="{ name: 'project.create', params: { namespaceId: namespace.id } }"
|
|
||||||
icon="plus"
|
|
||||||
>
|
|
||||||
{{ $t('menu.newProject') }}
|
|
||||||
</dropdown-item>
|
|
||||||
<dropdown-item
|
|
||||||
:to="{ name: 'namespace.settings.archive', params: { id: namespace.id } }"
|
|
||||||
icon="archive"
|
|
||||||
>
|
|
||||||
{{ $t('menu.archive') }}
|
|
||||||
</dropdown-item>
|
|
||||||
<Subscription
|
|
||||||
class="has-no-shadow"
|
|
||||||
:is-button="false"
|
|
||||||
entity="namespace"
|
|
||||||
:entity-id="namespace.id"
|
|
||||||
:model-value="subscription"
|
|
||||||
@update:model-value="setSubscriptionInStore"
|
|
||||||
type="dropdown"
|
|
||||||
/>
|
|
||||||
<dropdown-item
|
|
||||||
:to="{ name: 'namespace.settings.delete', params: { id: namespace.id } }"
|
|
||||||
icon="trash-alt"
|
|
||||||
class="has-text-danger"
|
|
||||||
>
|
|
||||||
{{ $t('menu.delete') }}
|
|
||||||
</dropdown-item>
|
|
||||||
</template>
|
|
||||||
</dropdown>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import {ref, onMounted, type PropType} from 'vue'
|
|
||||||
|
|
||||||
import BaseButton from '@/components/base/BaseButton.vue'
|
|
||||||
import Dropdown from '@/components/misc/dropdown.vue'
|
|
||||||
import DropdownItem from '@/components/misc/dropdown-item.vue'
|
|
||||||
import Subscription from '@/components/misc/subscription.vue'
|
|
||||||
import type {INamespace} from '@/modelTypes/INamespace'
|
|
||||||
import type {ISubscription} from '@/modelTypes/ISubscription'
|
|
||||||
import {useNamespaceStore} from '@/stores/namespaces'
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
namespace: {
|
|
||||||
type: Object as PropType<INamespace>,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const namespaceStore = useNamespaceStore()
|
|
||||||
|
|
||||||
const subscription = ref<ISubscription | null>(null)
|
|
||||||
onMounted(() => {
|
|
||||||
subscription.value = props.namespace.subscription
|
|
||||||
})
|
|
||||||
|
|
||||||
function setSubscriptionInStore(sub: ISubscription) {
|
|
||||||
subscription.value = sub
|
|
||||||
namespaceStore.setNamespaceById({
|
|
||||||
...props.namespace,
|
|
||||||
subscription: sub,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.dropdown-trigger {
|
|
||||||
padding: 0.5rem;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -46,7 +46,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<CustomTransition name="fade">
|
<CustomTransition name="fade">
|
||||||
<Message variant="warning" v-if="currentProject.isArchived" class="mb-4">
|
<Message variant="warning" v-if="currentProject.isArchived" class="mb-4">
|
||||||
{{ $t('project.archived') }}
|
{{ $t('project.archivedText') }}
|
||||||
</Message>
|
</Message>
|
||||||
</CustomTransition>
|
</CustomTransition>
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
:class="{'is-visible': background}"
|
:class="{'is-visible': background}"
|
||||||
:style="{'background-image': background !== null ? `url(${background})` : undefined}"
|
:style="{'background-image': background !== null ? `url(${background})` : undefined}"
|
||||||
/>
|
/>
|
||||||
<span v-if="project.isArchived" class="is-archived" >{{ $t('namespace.archived') }}</span>
|
<span v-if="project.isArchived" class="is-archived" >{{ $t('project.archived') }}</span>
|
||||||
|
|
||||||
<div class="project-title" aria-hidden="true">{{ project.title }}</div>
|
<div class="project-title" aria-hidden="true">{{ project.title }}</div>
|
||||||
<BaseButton
|
<BaseButton
|
||||||
|
@ -165,16 +165,6 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
|
||||||
<label class="label">{{ $t('namespace.namespaces') }}</label>
|
|
||||||
<div class="control">
|
|
||||||
<SelectNamespace
|
|
||||||
v-model="entities.namespace"
|
|
||||||
@select="changeMultiselectFilter('namespace', 'namespace')"
|
|
||||||
@remove="changeMultiselectFilter('namespace', 'namespace')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
</card>
|
</card>
|
||||||
</template>
|
</template>
|
||||||
@ -189,7 +179,6 @@ import {camelCase} from 'camel-case'
|
|||||||
|
|
||||||
import type {ILabel} from '@/modelTypes/ILabel'
|
import type {ILabel} from '@/modelTypes/ILabel'
|
||||||
import type {IUser} from '@/modelTypes/IUser'
|
import type {IUser} from '@/modelTypes/IUser'
|
||||||
import type {INamespace} from '@/modelTypes/INamespace'
|
|
||||||
import type {IProject} from '@/modelTypes/IProject'
|
import type {IProject} from '@/modelTypes/IProject'
|
||||||
|
|
||||||
import {useLabelStore} from '@/stores/labels'
|
import {useLabelStore} from '@/stores/labels'
|
||||||
@ -201,7 +190,6 @@ import EditLabels from '@/components/tasks/partials/editLabels.vue'
|
|||||||
import Fancycheckbox from '@/components/input/fancycheckbox.vue'
|
import Fancycheckbox from '@/components/input/fancycheckbox.vue'
|
||||||
import SelectUser from '@/components/input/SelectUser.vue'
|
import SelectUser from '@/components/input/SelectUser.vue'
|
||||||
import SelectProject from '@/components/input/SelectProject.vue'
|
import SelectProject from '@/components/input/SelectProject.vue'
|
||||||
import SelectNamespace from '@/components/input/SelectNamespace.vue'
|
|
||||||
|
|
||||||
import {parseDateOrString} from '@/helpers/time/parseDateOrString'
|
import {parseDateOrString} from '@/helpers/time/parseDateOrString'
|
||||||
import {dateIsValid, formatISO} from '@/helpers/time/formatDate'
|
import {dateIsValid, formatISO} from '@/helpers/time/formatDate'
|
||||||
@ -209,7 +197,6 @@ import {objectToSnakeCase} from '@/helpers/case'
|
|||||||
|
|
||||||
import UserService from '@/services/user'
|
import UserService from '@/services/user'
|
||||||
import ProjectService from '@/services/project'
|
import ProjectService from '@/services/project'
|
||||||
import NamespaceService from '@/services/namespace'
|
|
||||||
|
|
||||||
// FIXME: do not use this here for now. instead create new version from DEFAULT_PARAMS
|
// FIXME: do not use this here for now. instead create new version from DEFAULT_PARAMS
|
||||||
import {getDefaultParams} from '@/composables/useTaskList'
|
import {getDefaultParams} from '@/composables/useTaskList'
|
||||||
@ -240,7 +227,6 @@ const DEFAULT_FILTERS = {
|
|||||||
assignees: '',
|
assignees: '',
|
||||||
labels: '',
|
labels: '',
|
||||||
project_id: '',
|
project_id: '',
|
||||||
namespace: '',
|
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -265,23 +251,20 @@ const filters = ref({...DEFAULT_FILTERS})
|
|||||||
const services = {
|
const services = {
|
||||||
users: shallowReactive(new UserService()),
|
users: shallowReactive(new UserService()),
|
||||||
projects: shallowReactive(new ProjectService()),
|
projects: shallowReactive(new ProjectService()),
|
||||||
namespace: shallowReactive(new NamespaceService()),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Entities {
|
interface Entities {
|
||||||
users: IUser[]
|
users: IUser[]
|
||||||
labels: ILabel[]
|
labels: ILabel[]
|
||||||
projects: IProject[]
|
projects: IProject[]
|
||||||
namespace: INamespace[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type EntityType = 'users' | 'labels' | 'projects' | 'namespace'
|
type EntityType = 'users' | 'labels' | 'projects'
|
||||||
|
|
||||||
const entities: Entities = reactive({
|
const entities: Entities = reactive({
|
||||||
users: [],
|
users: [],
|
||||||
labels: [],
|
labels: [],
|
||||||
projects: [],
|
projects: [],
|
||||||
namespace: [],
|
|
||||||
})
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@ -328,7 +311,6 @@ function prepareFilters() {
|
|||||||
prepareDate('reminders')
|
prepareDate('reminders')
|
||||||
prepareRelatedObjectFilter('users', 'assignees')
|
prepareRelatedObjectFilter('users', 'assignees')
|
||||||
prepareRelatedObjectFilter('projects', 'project_id')
|
prepareRelatedObjectFilter('projects', 'project_id')
|
||||||
prepareRelatedObjectFilter('namespace')
|
|
||||||
|
|
||||||
prepareSingleValue('labels')
|
prepareSingleValue('labels')
|
||||||
|
|
||||||
|
@ -96,7 +96,6 @@ import type {ISubscription} from '@/modelTypes/ISubscription'
|
|||||||
import {isSavedFilter} from '@/services/savedFilter'
|
import {isSavedFilter} from '@/services/savedFilter'
|
||||||
import {useConfigStore} from '@/stores/config'
|
import {useConfigStore} from '@/stores/config'
|
||||||
import {useProjectStore} from '@/stores/projects'
|
import {useProjectStore} from '@/stores/projects'
|
||||||
import {useNamespaceStore} from '@/stores/namespaces'
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
project: {
|
project: {
|
||||||
@ -106,7 +105,6 @@ const props = defineProps({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const projectStore = useProjectStore()
|
const projectStore = useProjectStore()
|
||||||
const namespaceStore = useNamespaceStore()
|
|
||||||
const subscription = ref<ISubscription | null>(null)
|
const subscription = ref<ISubscription | null>(null)
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
subscription.value = props.project.subscription ?? null
|
subscription.value = props.project.subscription ?? null
|
||||||
@ -122,6 +120,5 @@ function setSubscriptionInStore(sub: ISubscription) {
|
|||||||
subscription: sub,
|
subscription: sub,
|
||||||
}
|
}
|
||||||
projectStore.setProject(updatedProject)
|
projectStore.setProject(updatedProject)
|
||||||
namespaceStore.setProjectInNamespaceById(updatedProject)
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
@ -61,7 +61,6 @@ import {useRouter} from 'vue-router'
|
|||||||
import TaskService from '@/services/task'
|
import TaskService from '@/services/task'
|
||||||
import TeamService from '@/services/team'
|
import TeamService from '@/services/team'
|
||||||
|
|
||||||
import NamespaceModel from '@/models/namespace'
|
|
||||||
import TeamModel from '@/models/team'
|
import TeamModel from '@/models/team'
|
||||||
import ProjectModel from '@/models/project'
|
import ProjectModel from '@/models/project'
|
||||||
|
|
||||||
@ -70,7 +69,6 @@ import QuickAddMagic from '@/components/tasks/partials/quick-add-magic.vue'
|
|||||||
|
|
||||||
import {useBaseStore} from '@/stores/base'
|
import {useBaseStore} from '@/stores/base'
|
||||||
import {useProjectStore} from '@/stores/projects'
|
import {useProjectStore} from '@/stores/projects'
|
||||||
import {useNamespaceStore} from '@/stores/namespaces'
|
|
||||||
import {useLabelStore} from '@/stores/labels'
|
import {useLabelStore} from '@/stores/labels'
|
||||||
import {useTaskStore} from '@/stores/tasks'
|
import {useTaskStore} from '@/stores/tasks'
|
||||||
|
|
||||||
@ -81,7 +79,6 @@ import {success} from '@/message'
|
|||||||
|
|
||||||
import type {ITeam} from '@/modelTypes/ITeam'
|
import type {ITeam} from '@/modelTypes/ITeam'
|
||||||
import type {ITask} from '@/modelTypes/ITask'
|
import type {ITask} from '@/modelTypes/ITask'
|
||||||
import type {INamespace} from '@/modelTypes/INamespace'
|
|
||||||
import type {IProject} from '@/modelTypes/IProject'
|
import type {IProject} from '@/modelTypes/IProject'
|
||||||
|
|
||||||
const {t} = useI18n({useScope: 'global'})
|
const {t} = useI18n({useScope: 'global'})
|
||||||
@ -89,7 +86,6 @@ const router = useRouter()
|
|||||||
|
|
||||||
const baseStore = useBaseStore()
|
const baseStore = useBaseStore()
|
||||||
const projectStore = useProjectStore()
|
const projectStore = useProjectStore()
|
||||||
const namespaceStore = useNamespaceStore()
|
|
||||||
const labelStore = useLabelStore()
|
const labelStore = useLabelStore()
|
||||||
const taskStore = useTaskStore()
|
const taskStore = useTaskStore()
|
||||||
|
|
||||||
@ -105,7 +101,6 @@ enum ACTION_TYPE {
|
|||||||
enum COMMAND_TYPE {
|
enum COMMAND_TYPE {
|
||||||
NEW_TASK = 'newTask',
|
NEW_TASK = 'newTask',
|
||||||
NEW_PROJECT = 'newProject',
|
NEW_PROJECT = 'newProject',
|
||||||
NEW_NAMESPACE = 'newNamespace',
|
|
||||||
NEW_TEAM = 'newTeam',
|
NEW_TEAM = 'newTeam',
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,7 +142,6 @@ const foundProjects = computed(() => {
|
|||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
const ncache: { [id: ProjectModel['id']]: INamespace } = {}
|
|
||||||
const history = getHistory()
|
const history = getHistory()
|
||||||
const allProjects = [
|
const allProjects = [
|
||||||
...new Set([
|
...new Set([
|
||||||
@ -156,15 +150,7 @@ const foundProjects = computed(() => {
|
|||||||
]),
|
]),
|
||||||
]
|
]
|
||||||
|
|
||||||
return allProjects.filter((l) => {
|
return allProjects.filter((l) => typeof l !== 'undefined' && l !== null)
|
||||||
if (typeof l === 'undefined' || l === null) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (typeof ncache[l.namespaceId] === 'undefined') {
|
|
||||||
ncache[l.namespaceId] = namespaceStore.getNamespaceById(l.namespaceId)
|
|
||||||
}
|
|
||||||
return !ncache[l.namespaceId].isArchived
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// FIXME: use fuzzysearch
|
// FIXME: use fuzzysearch
|
||||||
@ -205,7 +191,6 @@ const results = computed<Result[]>(() => {
|
|||||||
|
|
||||||
const loading = computed(() =>
|
const loading = computed(() =>
|
||||||
taskService.loading ||
|
taskService.loading ||
|
||||||
namespaceStore.isLoading ||
|
|
||||||
projectStore.isLoading ||
|
projectStore.isLoading ||
|
||||||
teamService.loading,
|
teamService.loading,
|
||||||
)
|
)
|
||||||
@ -230,12 +215,6 @@ const commands = computed<{ [key in COMMAND_TYPE]: Command }>(() => ({
|
|||||||
placeholder: t('quickActions.newProject'),
|
placeholder: t('quickActions.newProject'),
|
||||||
action: newProject,
|
action: newProject,
|
||||||
},
|
},
|
||||||
newNamespace: {
|
|
||||||
type: COMMAND_TYPE.NEW_NAMESPACE,
|
|
||||||
title: t('quickActions.cmds.newNamespace'),
|
|
||||||
placeholder: t('quickActions.newNamespace'),
|
|
||||||
action: newNamespace,
|
|
||||||
},
|
|
||||||
newTeam: {
|
newTeam: {
|
||||||
type: COMMAND_TYPE.NEW_TEAM,
|
type: COMMAND_TYPE.NEW_TEAM,
|
||||||
title: t('quickActions.cmds.newTeam'),
|
title: t('quickActions.cmds.newTeam'),
|
||||||
@ -252,7 +231,6 @@ const currentProject = computed(() => Object.keys(baseStore.currentProject).leng
|
|||||||
)
|
)
|
||||||
|
|
||||||
const hintText = computed(() => {
|
const hintText = computed(() => {
|
||||||
let namespace
|
|
||||||
if (selectedCmd.value !== null && currentProject.value !== null) {
|
if (selectedCmd.value !== null && currentProject.value !== null) {
|
||||||
switch (selectedCmd.value.type) {
|
switch (selectedCmd.value.type) {
|
||||||
case COMMAND_TYPE.NEW_TASK:
|
case COMMAND_TYPE.NEW_TASK:
|
||||||
@ -260,12 +238,7 @@ const hintText = computed(() => {
|
|||||||
title: currentProject.value.title,
|
title: currentProject.value.title,
|
||||||
})
|
})
|
||||||
case COMMAND_TYPE.NEW_PROJECT:
|
case COMMAND_TYPE.NEW_PROJECT:
|
||||||
namespace = namespaceStore.getNamespaceById(
|
return t('quickActions.createProject')
|
||||||
currentProject.value.namespaceId,
|
|
||||||
)
|
|
||||||
return t('quickActions.createProject', {
|
|
||||||
title: namespace?.title,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const prefixes =
|
const prefixes =
|
||||||
@ -278,7 +251,7 @@ const availableCmds = computed(() => {
|
|||||||
if (currentProject.value !== null) {
|
if (currentProject.value !== null) {
|
||||||
cmds.push(commands.value.newTask, commands.value.newProject)
|
cmds.push(commands.value.newTask, commands.value.newProject)
|
||||||
}
|
}
|
||||||
cmds.push(commands.value.newNamespace, commands.value.newTeam)
|
cmds.push(commands.value.newTeam)
|
||||||
return cmds
|
return cmds
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -506,7 +479,6 @@ async function newProject() {
|
|||||||
}
|
}
|
||||||
const newProject = await projectStore.createProject(new ProjectModel({
|
const newProject = await projectStore.createProject(new ProjectModel({
|
||||||
title: query.value,
|
title: query.value,
|
||||||
namespaceId: currentProject.value.namespaceId,
|
|
||||||
}))
|
}))
|
||||||
success({ message: t('project.create.createdSuccess')})
|
success({ message: t('project.create.createdSuccess')})
|
||||||
await router.push({
|
await router.push({
|
||||||
@ -515,12 +487,6 @@ async function newProject() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function newNamespace() {
|
|
||||||
const newNamespace = new NamespaceModel({ title: query.value })
|
|
||||||
await namespaceStore.createNamespace(newNamespace)
|
|
||||||
success({ message: t('namespace.create.success') })
|
|
||||||
}
|
|
||||||
|
|
||||||
async function newTeam() {
|
async function newTeam() {
|
||||||
const newTeam = new TeamModel({ name: query.value })
|
const newTeam = new TeamModel({ name: query.value })
|
||||||
const team = await teamService.create(newTeam)
|
const team = await teamService.create(newTeam)
|
||||||
|
@ -139,10 +139,6 @@ import {ref, reactive, computed, shallowReactive, type Ref} from 'vue'
|
|||||||
import type {PropType} from 'vue'
|
import type {PropType} from 'vue'
|
||||||
import {useI18n} from 'vue-i18n'
|
import {useI18n} from 'vue-i18n'
|
||||||
|
|
||||||
import UserNamespaceService from '@/services/userNamespace'
|
|
||||||
import UserNamespaceModel from '@/models/userNamespace'
|
|
||||||
import type {IUserNamespace} from '@/modelTypes/IUserNamespace'
|
|
||||||
|
|
||||||
import UserProjectService from '@/services/userProject'
|
import UserProjectService from '@/services/userProject'
|
||||||
import UserProjectModel from '@/models/userProject'
|
import UserProjectModel from '@/models/userProject'
|
||||||
import type {IUserProject} from '@/modelTypes/IUserProject'
|
import type {IUserProject} from '@/modelTypes/IUserProject'
|
||||||
@ -151,10 +147,6 @@ import UserService from '@/services/user'
|
|||||||
import UserModel, { getDisplayName } from '@/models/user'
|
import UserModel, { getDisplayName } from '@/models/user'
|
||||||
import type {IUser} from '@/modelTypes/IUser'
|
import type {IUser} from '@/modelTypes/IUser'
|
||||||
|
|
||||||
import TeamNamespaceService from '@/services/teamNamespace'
|
|
||||||
import TeamNamespaceModel from '@/models/teamNamespace'
|
|
||||||
import type { ITeamNamespace } from '@/modelTypes/ITeamNamespace'
|
|
||||||
|
|
||||||
import TeamProjectService from '@/services/teamProject'
|
import TeamProjectService from '@/services/teamProject'
|
||||||
import TeamProjectModel from '@/models/teamProject'
|
import TeamProjectModel from '@/models/teamProject'
|
||||||
import type { ITeamProject } from '@/modelTypes/ITeamProject'
|
import type { ITeamProject } from '@/modelTypes/ITeamProject'
|
||||||
@ -170,13 +162,15 @@ import Nothing from '@/components/misc/nothing.vue'
|
|||||||
import {success} from '@/message'
|
import {success} from '@/message'
|
||||||
import {useAuthStore} from '@/stores/auth'
|
import {useAuthStore} from '@/stores/auth'
|
||||||
|
|
||||||
|
// FIXME: I think this whole thing can now only manage user/team sharing for projects? Maybe remove a little generalization?
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
type: {
|
type: {
|
||||||
type: String as PropType<'project' | 'namespace'>,
|
type: String as PropType<'project'>,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
shareType: {
|
shareType: {
|
||||||
type: String as PropType<'user' | 'team' | 'namespace'>,
|
type: String as PropType<'user' | 'team'>,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
id: {
|
id: {
|
||||||
@ -191,9 +185,9 @@ const props = defineProps({
|
|||||||
|
|
||||||
const {t} = useI18n({useScope: 'global'})
|
const {t} = useI18n({useScope: 'global'})
|
||||||
|
|
||||||
// This user service is either a userNamespaceService or a userProjectService, depending on the type we are using
|
// This user service is a userProjectService, depending on the type we are using
|
||||||
let stuffService: UserNamespaceService | UserProjectService | TeamProjectService | TeamNamespaceService
|
let stuffService: UserProjectService | TeamProjectService
|
||||||
let stuffModel: IUserNamespace | IUserProject | ITeamProject | ITeamNamespace
|
let stuffModel: IUserProject | ITeamProject
|
||||||
let searchService: UserService | TeamService
|
let searchService: UserService | TeamService
|
||||||
let sharable: Ref<IUser | ITeam>
|
let sharable: Ref<IUser | ITeam>
|
||||||
|
|
||||||
@ -231,10 +225,6 @@ const sharableName = computed(() => {
|
|||||||
return t('project.list.title')
|
return t('project.list.title')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.shareType === 'namespace') {
|
|
||||||
return t('namespace.namespace')
|
|
||||||
}
|
|
||||||
|
|
||||||
return ''
|
return ''
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -247,11 +237,6 @@ if (props.shareType === 'user') {
|
|||||||
if (props.type === 'project') {
|
if (props.type === 'project') {
|
||||||
stuffService = shallowReactive(new UserProjectService())
|
stuffService = shallowReactive(new UserProjectService())
|
||||||
stuffModel = reactive(new UserProjectModel({projectId: props.id}))
|
stuffModel = reactive(new UserProjectModel({projectId: props.id}))
|
||||||
} else if (props.type === 'namespace') {
|
|
||||||
stuffService = shallowReactive(new UserNamespaceService())
|
|
||||||
stuffModel = reactive(new UserNamespaceModel({
|
|
||||||
namespaceId: props.id,
|
|
||||||
}))
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Unknown type: ' + props.type)
|
throw new Error('Unknown type: ' + props.type)
|
||||||
}
|
}
|
||||||
@ -264,11 +249,6 @@ if (props.shareType === 'user') {
|
|||||||
if (props.type === 'project') {
|
if (props.type === 'project') {
|
||||||
stuffService = shallowReactive(new TeamProjectService())
|
stuffService = shallowReactive(new TeamProjectService())
|
||||||
stuffModel = reactive(new TeamProjectModel({projectId: props.id}))
|
stuffModel = reactive(new TeamProjectModel({projectId: props.id}))
|
||||||
} else if (props.type === 'namespace') {
|
|
||||||
stuffService = shallowReactive(new TeamNamespaceService())
|
|
||||||
stuffModel = reactive(new TeamNamespaceModel({
|
|
||||||
namespaceId: props.id,
|
|
||||||
}))
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Unknown type: ' + props.type)
|
throw new Error('Unknown type: ' + props.type)
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
@search="findProjects"
|
@search="findProjects"
|
||||||
>
|
>
|
||||||
<template #searchResult="{option}">
|
<template #searchResult="{option}">
|
||||||
<span class="project-namespace-title search-result">{{ namespace((option as IProject).namespaceId) }} ></span>
|
|
||||||
{{ (option as IProject).title }}
|
{{ (option as IProject).title }}
|
||||||
</template>
|
</template>
|
||||||
</Multiselect>
|
</Multiselect>
|
||||||
@ -23,10 +22,8 @@ import type {PropType} from 'vue'
|
|||||||
import {useI18n} from 'vue-i18n'
|
import {useI18n} from 'vue-i18n'
|
||||||
|
|
||||||
import type {IProject} from '@/modelTypes/IProject'
|
import type {IProject} from '@/modelTypes/IProject'
|
||||||
import type {INamespace} from '@/modelTypes/INamespace'
|
|
||||||
|
|
||||||
import {useProjectStore} from '@/stores/projects'
|
import {useProjectStore} from '@/stores/projects'
|
||||||
import {useNamespaceStore} from '@/stores/namespaces'
|
|
||||||
|
|
||||||
import ProjectModel from '@/models/project'
|
import ProjectModel from '@/models/project'
|
||||||
|
|
||||||
@ -54,7 +51,6 @@ watch(
|
|||||||
)
|
)
|
||||||
|
|
||||||
const projectStore = useProjectStore()
|
const projectStore = useProjectStore()
|
||||||
const namespaceStore = useNamespaceStore()
|
|
||||||
const foundProjects = ref<IProject[]>([])
|
const foundProjects = ref<IProject[]>([])
|
||||||
function findProjects(query: string) {
|
function findProjects(query: string) {
|
||||||
if (query === '') {
|
if (query === '') {
|
||||||
@ -70,17 +66,4 @@ function select(l: IProject | null) {
|
|||||||
Object.assign(project, l)
|
Object.assign(project, l)
|
||||||
emit('update:modelValue', project)
|
emit('update:modelValue', project)
|
||||||
}
|
}
|
||||||
|
|
||||||
function namespace(namespaceId: INamespace['id']) {
|
|
||||||
const namespace = namespaceStore.getNamespaceById(namespaceId)
|
|
||||||
return namespace !== null
|
|
||||||
? namespace.title
|
|
||||||
: t('project.shared')
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.project-namespace-title {
|
|
||||||
color: var(--grey-500);
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -46,11 +46,6 @@
|
|||||||
class="different-project"
|
class="different-project"
|
||||||
v-if="task.projectId !== projectId"
|
v-if="task.projectId !== projectId"
|
||||||
>
|
>
|
||||||
<span
|
|
||||||
v-if="task.differentNamespace !== null"
|
|
||||||
v-tooltip="$t('task.relation.differentNamespace')">
|
|
||||||
{{ task.differentNamespace }} >
|
|
||||||
</span>
|
|
||||||
<span
|
<span
|
||||||
v-if="task.differentProject !== null"
|
v-if="task.differentProject !== null"
|
||||||
v-tooltip="$t('task.relation.differentProject')">
|
v-tooltip="$t('task.relation.differentProject')">
|
||||||
@ -101,11 +96,6 @@
|
|||||||
class="different-project"
|
class="different-project"
|
||||||
v-if="t.projectId !== projectId"
|
v-if="t.projectId !== projectId"
|
||||||
>
|
>
|
||||||
<span
|
|
||||||
v-if="t.differentNamespace !== null"
|
|
||||||
v-tooltip="$t('task.relation.differentNamespace')">
|
|
||||||
{{ t.differentNamespace }} >
|
|
||||||
</span>
|
|
||||||
<span
|
<span
|
||||||
v-if="t.differentProject !== null"
|
v-if="t.differentProject !== null"
|
||||||
v-tooltip="$t('task.relation.differentProject')">
|
v-tooltip="$t('task.relation.differentProject')">
|
||||||
@ -168,10 +158,9 @@ import BaseButton from '@/components/base/BaseButton.vue'
|
|||||||
import Multiselect from '@/components/input/multiselect.vue'
|
import Multiselect from '@/components/input/multiselect.vue'
|
||||||
import Fancycheckbox from '@/components/input/fancycheckbox.vue'
|
import Fancycheckbox from '@/components/input/fancycheckbox.vue'
|
||||||
|
|
||||||
import {useNamespaceStore} from '@/stores/namespaces'
|
|
||||||
|
|
||||||
import {error, success} from '@/message'
|
import {error, success} from '@/message'
|
||||||
import {useTaskStore} from '@/stores/tasks'
|
import {useTaskStore} from '@/stores/tasks'
|
||||||
|
import {useProjectStore} from '@/stores/projects'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
taskId: {
|
taskId: {
|
||||||
@ -196,7 +185,7 @@ const props = defineProps({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const taskStore = useTaskStore()
|
const taskStore = useTaskStore()
|
||||||
const namespaceStore = useNamespaceStore()
|
const projectStore = useProjectStore()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const {t} = useI18n({useScope: 'global'})
|
const {t} = useI18n({useScope: 'global'})
|
||||||
|
|
||||||
@ -230,24 +219,13 @@ async function findTasks(newQuery: string) {
|
|||||||
foundTasks.value = await taskService.getAll({}, {s: newQuery})
|
foundTasks.value = await taskService.getAll({}, {s: newQuery})
|
||||||
}
|
}
|
||||||
|
|
||||||
const getProjectAndNamespaceById = (projectId: number) => namespaceStore.getProjectAndNamespaceById(projectId, true)
|
|
||||||
|
|
||||||
const namespace = computed(() => getProjectAndNamespaceById(props.projectId)?.namespace)
|
|
||||||
|
|
||||||
function mapRelatedTasks(tasks: ITask[]) {
|
function mapRelatedTasks(tasks: ITask[]) {
|
||||||
return tasks.map(task => {
|
return tasks.map(task => {
|
||||||
// by doing this here once we can save a lot of duplicate calls in the template
|
// by doing this here once we can save a lot of duplicate calls in the template
|
||||||
const {
|
const project = projectStore.getProjectById(task.ProjectId)
|
||||||
project,
|
|
||||||
namespace: taskNamespace,
|
|
||||||
} = getProjectAndNamespaceById(task.projectId) || {project: null, namespace: null}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...task,
|
...task,
|
||||||
differentNamespace:
|
|
||||||
(taskNamespace !== null &&
|
|
||||||
taskNamespace.id !== namespace.value.id &&
|
|
||||||
taskNamespace?.title) || null,
|
|
||||||
differentProject:
|
differentProject:
|
||||||
(project !== null &&
|
(project !== null &&
|
||||||
task.projectId !== props.projectId &&
|
task.projectId !== props.projectId &&
|
||||||
|
@ -149,7 +149,6 @@ import {formatDateSince, formatISO, formatDateLong} from '@/helpers/time/formatD
|
|||||||
import {success} from '@/message'
|
import {success} from '@/message'
|
||||||
|
|
||||||
import {useProjectStore} from '@/stores/projects'
|
import {useProjectStore} from '@/stores/projects'
|
||||||
import {useNamespaceStore} from '@/stores/namespaces'
|
|
||||||
import {useBaseStore} from '@/stores/base'
|
import {useBaseStore} from '@/stores/base'
|
||||||
import {useTaskStore} from '@/stores/tasks'
|
import {useTaskStore} from '@/stores/tasks'
|
||||||
|
|
||||||
@ -209,7 +208,6 @@ onBeforeUnmount(() => {
|
|||||||
const baseStore = useBaseStore()
|
const baseStore = useBaseStore()
|
||||||
const projectStore = useProjectStore()
|
const projectStore = useProjectStore()
|
||||||
const taskStore = useTaskStore()
|
const taskStore = useTaskStore()
|
||||||
const namespaceStore = useNamespaceStore()
|
|
||||||
|
|
||||||
const project = computed(() => projectStore.getProjectById(task.value.projectId))
|
const project = computed(() => projectStore.getProjectById(task.value.projectId))
|
||||||
const projectColor = computed(() => project.value !== null ? project.value.hexColor : '')
|
const projectColor = computed(() => project.value !== null ? project.value.hexColor : '')
|
||||||
@ -260,7 +258,6 @@ async function toggleFavorite() {
|
|||||||
task.value.isFavorite = !task.value.isFavorite
|
task.value.isFavorite = !task.value.isFavorite
|
||||||
task.value = await taskService.update(task.value)
|
task.value = await taskService.update(task.value)
|
||||||
emit('task-updated', task.value)
|
emit('task-updated', task.value)
|
||||||
namespaceStore.loadNamespacesIfFavoritesDontExist()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const deferDueDate = ref<typeof DeferTask | null>(null)
|
const deferDueDate = ref<typeof DeferTask | null>(null)
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
import {ref, computed} from 'vue'
|
|
||||||
import {useNamespaceStore} from '@/stores/namespaces'
|
|
||||||
|
|
||||||
export function useNamespaceSearch() {
|
|
||||||
const query = ref('')
|
|
||||||
|
|
||||||
const namespaceStore = useNamespaceStore()
|
|
||||||
const namespaces = computed(() => namespaceStore.searchNamespace(query.value))
|
|
||||||
|
|
||||||
function findNamespaces(newQuery: string) {
|
|
||||||
query.value = newQuery
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
namespaces,
|
|
||||||
findNamespaces,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
|||||||
import {i18n} from '@/i18n'
|
|
||||||
import type {INamespace} from '@/modelTypes/INamespace'
|
|
||||||
|
|
||||||
export const getNamespaceTitle = (n: INamespace) => {
|
|
||||||
if (n.id === -1) {
|
|
||||||
return i18n.global.t('namespace.pseudo.sharedProjects.title')
|
|
||||||
}
|
|
||||||
if (n.id === -2) {
|
|
||||||
return i18n.global.t('namespace.pseudo.favorites.title')
|
|
||||||
}
|
|
||||||
if (n.id === -3) {
|
|
||||||
return i18n.global.t('namespace.pseudo.savedFilters.title')
|
|
||||||
}
|
|
||||||
return n.title
|
|
||||||
}
|
|
@ -143,7 +143,7 @@
|
|||||||
},
|
},
|
||||||
"deletion": {
|
"deletion": {
|
||||||
"title": "Delete your Vikunja Account",
|
"title": "Delete your Vikunja Account",
|
||||||
"text1": "The deletion of your account is permanent and cannot be undone. We will delete all your namespaces, projects, tasks and everything associated with it.",
|
"text1": "The deletion of your account is permanent and cannot be undone. We will delete all your projects, tasks and everything associated with it.",
|
||||||
"text2": "To proceed, please enter your password. You will receive an email with further instructions.",
|
"text2": "To proceed, please enter your password. You will receive an email with further instructions.",
|
||||||
"confirm": "Delete my account",
|
"confirm": "Delete my account",
|
||||||
"requestSuccess": "The request was successful. You'll receive an email with further instructions.",
|
"requestSuccess": "The request was successful. You'll receive an email with further instructions.",
|
||||||
@ -165,7 +165,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"project": {
|
"project": {
|
||||||
"archived": "This project is archived. It is not possible to create new or edit tasks for it.",
|
"archivedText": "This project is archived. It is not possible to create new or edit tasks for it.",
|
||||||
|
"archived": "Archived",
|
||||||
"showArchived": "Show Archived",
|
"showArchived": "Show Archived",
|
||||||
"title": "Project Title",
|
"title": "Project Title",
|
||||||
"color": "Color",
|
"color": "Color",
|
||||||
@ -211,7 +212,6 @@
|
|||||||
"duplicate": {
|
"duplicate": {
|
||||||
"title": "Duplicate this project",
|
"title": "Duplicate this project",
|
||||||
"label": "Duplicate",
|
"label": "Duplicate",
|
||||||
"text": "Select a namespace which should hold the duplicated project:",
|
|
||||||
"success": "The project was successfully duplicated."
|
"success": "The project was successfully duplicated."
|
||||||
},
|
},
|
||||||
"edit": {
|
"edit": {
|
||||||
@ -322,66 +322,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"namespace": {
|
|
||||||
"title": "Namespaces & Projects",
|
|
||||||
"namespace": "Namespace",
|
|
||||||
"noneAvailable": "You don't have any namespaces right now.",
|
|
||||||
"unarchive": "Un-Archive",
|
|
||||||
"archived": "Archived",
|
|
||||||
"noProjects": "This namespace does not contain any projects.",
|
|
||||||
"createProject": "Create a new project in this namespace.",
|
|
||||||
"namespaces": "Namespaces",
|
|
||||||
"search": "Type to search for a namespace…",
|
|
||||||
"create": {
|
|
||||||
"title": "New namespace",
|
|
||||||
"titleRequired": "Please specify a title.",
|
|
||||||
"explanation": "A namespace is a collection of projects you can share and use to organize your projects with. In fact, every project belongs to a namespace.",
|
|
||||||
"tooltip": "What's a namespace?",
|
|
||||||
"success": "The namespace was successfully created."
|
|
||||||
},
|
|
||||||
"archive": {
|
|
||||||
"titleArchive": "Archive \"{namespace}\"",
|
|
||||||
"titleUnarchive": "Un-Archive \"{namespace}\"",
|
|
||||||
"archiveText": "You won't be able to edit this namespace or create new projects until you un-archive it. This will also archive all projects in this namespace.",
|
|
||||||
"unarchiveText": "You will be able to create new projects or edit it.",
|
|
||||||
"success": "The namespace was successfully archived.",
|
|
||||||
"unarchiveSuccess": "The namespace was successfully un-archived.",
|
|
||||||
"description": "If a namespace is archived, you cannot create new projects or edit it."
|
|
||||||
},
|
|
||||||
"delete": {
|
|
||||||
"title": "Delete \"{namespace}\"",
|
|
||||||
"text1": "Are you sure you want to delete this namespace and all of its contents?",
|
|
||||||
"text2": "This includes all projects and tasks and CANNOT BE UNDONE!",
|
|
||||||
"success": "The namespace was successfully deleted."
|
|
||||||
},
|
|
||||||
"edit": {
|
|
||||||
"title": "Edit \"{namespace}\"",
|
|
||||||
"success": "The namespace was successfully updated."
|
|
||||||
},
|
|
||||||
"share": {
|
|
||||||
"title": "Share \"{namespace}\""
|
|
||||||
},
|
|
||||||
"attributes": {
|
|
||||||
"title": "Namespace Title",
|
|
||||||
"titlePlaceholder": "The namespace title goes here…",
|
|
||||||
"description": "Description",
|
|
||||||
"descriptionPlaceholder": "The namespaces description goes here…",
|
|
||||||
"color": "Color",
|
|
||||||
"archived": "Is Archived",
|
|
||||||
"isArchived": "This namespace is archived"
|
|
||||||
},
|
|
||||||
"pseudo": {
|
|
||||||
"sharedProjects": {
|
|
||||||
"title": "Shared Projects"
|
|
||||||
},
|
|
||||||
"favorites": {
|
|
||||||
"title": "Favorites"
|
|
||||||
},
|
|
||||||
"savedFilters": {
|
|
||||||
"title": "Filters"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"filters": {
|
"filters": {
|
||||||
"title": "Filters",
|
"title": "Filters",
|
||||||
"clear": "Clear Filters",
|
"clear": "Clear Filters",
|
||||||
@ -403,7 +343,7 @@
|
|||||||
},
|
},
|
||||||
"create": {
|
"create": {
|
||||||
"title": "New Saved Filter",
|
"title": "New Saved Filter",
|
||||||
"description": "A saved filter is a virtual project which is computed from a set of filters each time it is accessed. Once created, it will appear in a special namespace.",
|
"description": "A saved filter is a virtual project which is computed from a set of filters each time it is accessed.",
|
||||||
"action": "Create new saved filter",
|
"action": "Create new saved filter",
|
||||||
"titleRequired": "Please provide a title for the filter."
|
"titleRequired": "Please provide a title for the filter."
|
||||||
},
|
},
|
||||||
@ -677,19 +617,13 @@
|
|||||||
"updated": "Updated"
|
"updated": "Updated"
|
||||||
},
|
},
|
||||||
"subscription": {
|
"subscription": {
|
||||||
"subscribedProjectThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this project through its namespace.",
|
|
||||||
"subscribedTaskThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this task through its namespace.",
|
|
||||||
"subscribedTaskThroughParentProject": "You can't unsubscribe here because you are subscribed to this task through its project.",
|
"subscribedTaskThroughParentProject": "You can't unsubscribe here because you are subscribed to this task through its project.",
|
||||||
"subscribedNamespace": "You are currently subscribed to this namespace and will receive notifications for changes.",
|
|
||||||
"notSubscribedNamespace": "You are not subscribed to this namespace and won't receive notifications for changes.",
|
|
||||||
"subscribedProject": "You are currently subscribed to this project and will receive notifications for changes.",
|
"subscribedProject": "You are currently subscribed to this project and will receive notifications for changes.",
|
||||||
"notSubscribedProject": "You are not subscribed to this project and won't receive notifications for changes.",
|
"notSubscribedProject": "You are not subscribed to this project and won't receive notifications for changes.",
|
||||||
"subscribedTask": "You are currently subscribed to this task and will receive notifications for changes.",
|
"subscribedTask": "You are currently subscribed to this task and will receive notifications for changes.",
|
||||||
"notSubscribedTask": "You are not subscribed to this task and won't receive notifications for changes.",
|
"notSubscribedTask": "You are not subscribed to this task and won't receive notifications for changes.",
|
||||||
"subscribe": "Subscribe",
|
"subscribe": "Subscribe",
|
||||||
"unsubscribe": "Unsubscribe",
|
"unsubscribe": "Unsubscribe",
|
||||||
"subscribeSuccessNamespace": "You are now subscribed to this namespace",
|
|
||||||
"unsubscribeSuccessNamespace": "You are now unsubscribed to this namespace",
|
|
||||||
"subscribeSuccessProject": "You are now subscribed to this project",
|
"subscribeSuccessProject": "You are now subscribed to this project",
|
||||||
"unsubscribeSuccessProject": "You are now unsubscribed to this project",
|
"unsubscribeSuccessProject": "You are now unsubscribed to this project",
|
||||||
"subscribeSuccessTask": "You are now subscribed to this task",
|
"subscribeSuccessTask": "You are now subscribed to this task",
|
||||||
@ -766,7 +700,6 @@
|
|||||||
"searchPlaceholder": "Type search for a new task to add as related…",
|
"searchPlaceholder": "Type search for a new task to add as related…",
|
||||||
"createPlaceholder": "Add this as new related task",
|
"createPlaceholder": "Add this as new related task",
|
||||||
"differentProject": "This task belongs to a different project.",
|
"differentProject": "This task belongs to a different project.",
|
||||||
"differentNamespace": "This task belongs to a different namespace.",
|
|
||||||
"noneYet": "No task relations yet.",
|
"noneYet": "No task relations yet.",
|
||||||
"delete": "Delete Task Relation",
|
"delete": "Delete Task Relation",
|
||||||
"deleteText1": "Are you sure you want to delete this task relation?",
|
"deleteText1": "Are you sure you want to delete this task relation?",
|
||||||
@ -851,19 +784,19 @@
|
|||||||
"delete": {
|
"delete": {
|
||||||
"header": "Delete the team",
|
"header": "Delete the team",
|
||||||
"text1": "Are you sure you want to delete this team and all of its members?",
|
"text1": "Are you sure you want to delete this team and all of its members?",
|
||||||
"text2": "All team members will lose access to projects and namespaces shared with this team. This CANNOT BE UNDONE!",
|
"text2": "All team members will lose access to projects shared with this team. This CANNOT BE UNDONE!",
|
||||||
"success": "The team was successfully deleted."
|
"success": "The team was successfully deleted."
|
||||||
},
|
},
|
||||||
"deleteUser": {
|
"deleteUser": {
|
||||||
"header": "Remove a user from the team",
|
"header": "Remove a user from the team",
|
||||||
"text1": "Are you sure you want to remove this user from the team?",
|
"text1": "Are you sure you want to remove this user from the team?",
|
||||||
"text2": "They will lose access to all projects and namespaces this team has access to. This CANNOT BE UNDONE!",
|
"text2": "They will lose access to all projects this team has access to. This CANNOT BE UNDONE!",
|
||||||
"success": "The user was successfully deleted from the team."
|
"success": "The user was successfully deleted from the team."
|
||||||
},
|
},
|
||||||
"leave": {
|
"leave": {
|
||||||
"title": "Leave team",
|
"title": "Leave team",
|
||||||
"text1": "Are you sure you want to leave this team?",
|
"text1": "Are you sure you want to leave this team?",
|
||||||
"text2": "You will lose access to all projects and namespaces this team has access to. If you change your mind you'll need a team admin to add you again.",
|
"text2": "You will lose access to all projects this team has access to. If you change your mind you'll need a team admin to add you again.",
|
||||||
"success": "You have successfully left the team."
|
"success": "You have successfully left the team."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -913,9 +846,9 @@
|
|||||||
"title": "Navigation",
|
"title": "Navigation",
|
||||||
"overview": "Navigate to overview",
|
"overview": "Navigate to overview",
|
||||||
"upcoming": "Navigate to upcoming tasks",
|
"upcoming": "Navigate to upcoming tasks",
|
||||||
"namespaces": "Navigate to namespaces & projects",
|
|
||||||
"labels": "Navigate to labels",
|
"labels": "Navigate to labels",
|
||||||
"teams": "Navigate to teams"
|
"teams": "Navigate to teams",
|
||||||
|
"projects": "Navigate to projects"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"update": {
|
"update": {
|
||||||
@ -949,7 +882,7 @@
|
|||||||
"notification": {
|
"notification": {
|
||||||
"title": "Notifications",
|
"title": "Notifications",
|
||||||
"none": "You don't have any notifications. Have a nice day!",
|
"none": "You don't have any notifications. Have a nice day!",
|
||||||
"explainer": "Notifications will appear here when actions on namespaces, projects or tasks you subscribed to happen."
|
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen."
|
||||||
},
|
},
|
||||||
"quickActions": {
|
"quickActions": {
|
||||||
"commands": "Commands",
|
"commands": "Commands",
|
||||||
@ -960,14 +893,12 @@
|
|||||||
"teams": "Teams",
|
"teams": "Teams",
|
||||||
"newProject": "Enter the title of the new project…",
|
"newProject": "Enter the title of the new project…",
|
||||||
"newTask": "Enter the title of the new task…",
|
"newTask": "Enter the title of the new task…",
|
||||||
"newNamespace": "Enter the title of the new namespace…",
|
|
||||||
"newTeam": "Enter the name of the new team…",
|
"newTeam": "Enter the name of the new team…",
|
||||||
"createTask": "Create a task in the current project ({title})",
|
"createTask": "Create a task in the current project ({title})",
|
||||||
"createProject": "Create a project in the current namespace ({title})",
|
"createProject": "Create a project",
|
||||||
"cmds": {
|
"cmds": {
|
||||||
"newTask": "New task",
|
"newTask": "New task",
|
||||||
"newProject": "New project",
|
"newProject": "New project",
|
||||||
"newNamespace": "New namespace",
|
|
||||||
"newTeam": "New team"
|
"newTeam": "New team"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1023,16 +954,9 @@
|
|||||||
"4017": "Invalid task filter comparator.",
|
"4017": "Invalid task filter comparator.",
|
||||||
"4018": "Invalid task filter concatenator.",
|
"4018": "Invalid task filter concatenator.",
|
||||||
"4019": "Invalid task filter value.",
|
"4019": "Invalid task filter value.",
|
||||||
"5001": "The namespace does not exist.",
|
|
||||||
"5003": "You do not have access to the specified namespace.",
|
|
||||||
"5006": "The namespace name cannot be empty.",
|
|
||||||
"5009": "You need to have namespace read access to perform that action.",
|
|
||||||
"5010": "This team does not have access to that namespace.",
|
|
||||||
"5011": "This user has already access to that namespace.",
|
|
||||||
"5012": "The namespace is archived and can therefore only be accessed read only.",
|
|
||||||
"6001": "The team name cannot be empty.",
|
"6001": "The team name cannot be empty.",
|
||||||
"6002": "The team does not exist.",
|
"6002": "The team does not exist.",
|
||||||
"6004": "The team already has access to that namespace or project.",
|
"6004": "The team already has access to that project.",
|
||||||
"6005": "The user is already a member of that team.",
|
"6005": "The user is already a member of that team.",
|
||||||
"6006": "Cannot delete the last team member.",
|
"6006": "Cannot delete the last team member.",
|
||||||
"6007": "The team does not have access to the project to perform that action.",
|
"6007": "The team does not have access to the project to perform that action.",
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
import type {IAbstract} from './IAbstract'
|
|
||||||
import type {IProject} from './IProject'
|
|
||||||
import type {IUser} from './IUser'
|
|
||||||
import type {ISubscription} from './ISubscription'
|
|
||||||
|
|
||||||
export interface INamespace extends IAbstract {
|
|
||||||
id: number
|
|
||||||
title: string
|
|
||||||
description: string
|
|
||||||
owner: IUser
|
|
||||||
projects: IProject[]
|
|
||||||
isArchived: boolean
|
|
||||||
hexColor: string
|
|
||||||
subscription: ISubscription
|
|
||||||
|
|
||||||
created: Date
|
|
||||||
updated: Date
|
|
||||||
}
|
|
@ -2,7 +2,6 @@ import type {IAbstract} from './IAbstract'
|
|||||||
import type {ITask} from './ITask'
|
import type {ITask} from './ITask'
|
||||||
import type {IUser} from './IUser'
|
import type {IUser} from './IUser'
|
||||||
import type {ISubscription} from './ISubscription'
|
import type {ISubscription} from './ISubscription'
|
||||||
import type {INamespace} from './INamespace'
|
|
||||||
|
|
||||||
|
|
||||||
export interface IProject extends IAbstract {
|
export interface IProject extends IAbstract {
|
||||||
@ -11,7 +10,6 @@ export interface IProject extends IAbstract {
|
|||||||
description: string
|
description: string
|
||||||
owner: IUser
|
owner: IUser
|
||||||
tasks: ITask[]
|
tasks: ITask[]
|
||||||
namespaceId: INamespace['id']
|
|
||||||
isArchived: boolean
|
isArchived: boolean
|
||||||
hexColor: string
|
hexColor: string
|
||||||
identifier: string
|
identifier: string
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
import type {IAbstract} from './IAbstract'
|
import type {IAbstract} from './IAbstract'
|
||||||
import type {IProject} from './IProject'
|
import type {IProject} from './IProject'
|
||||||
import type {INamespace} from './INamespace'
|
|
||||||
|
|
||||||
export interface IProjectDuplicate extends IAbstract {
|
export interface IProjectDuplicate extends IAbstract {
|
||||||
projectId: number
|
projectId: number
|
||||||
namespaceId: INamespace['id']
|
|
||||||
project: IProject
|
project: IProject
|
||||||
}
|
}
|
@ -1,6 +0,0 @@
|
|||||||
import type {ITeamShareBase} from './ITeamShareBase'
|
|
||||||
import type {INamespace} from './INamespace'
|
|
||||||
|
|
||||||
export interface ITeamNamespace extends ITeamShareBase {
|
|
||||||
namespaceId: INamespace['id']
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
import type {IUserShareBase} from './IUserShareBase'
|
|
||||||
import type {INamespace} from './INamespace'
|
|
||||||
|
|
||||||
export interface IUserNamespace extends IUserShareBase {
|
|
||||||
namespaceId: INamespace['id']
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
import AbstractModel from './abstractModel'
|
|
||||||
import ProjectModel from './project'
|
|
||||||
import UserModel from './user'
|
|
||||||
import SubscriptionModel from '@/models/subscription'
|
|
||||||
|
|
||||||
import type {INamespace} from '@/modelTypes/INamespace'
|
|
||||||
import type {IUser} from '@/modelTypes/IUser'
|
|
||||||
import type {IProject} from '@/modelTypes/IProject'
|
|
||||||
import type {ISubscription} from '@/modelTypes/ISubscription'
|
|
||||||
|
|
||||||
export default class NamespaceModel extends AbstractModel<INamespace> implements INamespace {
|
|
||||||
id = 0
|
|
||||||
title = ''
|
|
||||||
description = ''
|
|
||||||
owner: IUser = UserModel
|
|
||||||
projects: IProject[] = []
|
|
||||||
isArchived = false
|
|
||||||
hexColor = ''
|
|
||||||
subscription: ISubscription = null
|
|
||||||
|
|
||||||
created: Date = null
|
|
||||||
updated: Date = null
|
|
||||||
|
|
||||||
constructor(data: Partial<INamespace> = {}) {
|
|
||||||
super()
|
|
||||||
this.assignData(data)
|
|
||||||
|
|
||||||
if (this.hexColor !== '' && this.hexColor.substring(0, 1) !== '#') {
|
|
||||||
this.hexColor = '#' + this.hexColor
|
|
||||||
}
|
|
||||||
|
|
||||||
this.projects = this.projects.map(l => {
|
|
||||||
return new ProjectModel(l)
|
|
||||||
})
|
|
||||||
|
|
||||||
this.owner = new UserModel(this.owner)
|
|
||||||
|
|
||||||
if(typeof this.subscription !== 'undefined' && this.subscription !== null) {
|
|
||||||
this.subscription = new SubscriptionModel(this.subscription)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.created = new Date(this.created)
|
|
||||||
this.updated = new Date(this.updated)
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,7 +6,6 @@ import SubscriptionModel from '@/models/subscription'
|
|||||||
import type {IProject} from '@/modelTypes/IProject'
|
import type {IProject} from '@/modelTypes/IProject'
|
||||||
import type {IUser} from '@/modelTypes/IUser'
|
import type {IUser} from '@/modelTypes/IUser'
|
||||||
import type {ITask} from '@/modelTypes/ITask'
|
import type {ITask} from '@/modelTypes/ITask'
|
||||||
import type {INamespace} from '@/modelTypes/INamespace'
|
|
||||||
import type {ISubscription} from '@/modelTypes/ISubscription'
|
import type {ISubscription} from '@/modelTypes/ISubscription'
|
||||||
|
|
||||||
export default class ProjectModel extends AbstractModel<IProject> implements IProject {
|
export default class ProjectModel extends AbstractModel<IProject> implements IProject {
|
||||||
@ -15,7 +14,6 @@ export default class ProjectModel extends AbstractModel<IProject> implements IPr
|
|||||||
description = ''
|
description = ''
|
||||||
owner: IUser = UserModel
|
owner: IUser = UserModel
|
||||||
tasks: ITask[] = []
|
tasks: ITask[] = []
|
||||||
namespaceId: INamespace['id'] = 0
|
|
||||||
isArchived = false
|
isArchived = false
|
||||||
hexColor = ''
|
hexColor = ''
|
||||||
identifier = ''
|
identifier = ''
|
||||||
|
@ -2,12 +2,10 @@ import AbstractModel from './abstractModel'
|
|||||||
import ProjectModel from './project'
|
import ProjectModel from './project'
|
||||||
|
|
||||||
import type {IProjectDuplicate} from '@/modelTypes/IProjectDuplicate'
|
import type {IProjectDuplicate} from '@/modelTypes/IProjectDuplicate'
|
||||||
import type {INamespace} from '@/modelTypes/INamespace'
|
|
||||||
import type {IProject} from '@/modelTypes/IProject'
|
import type {IProject} from '@/modelTypes/IProject'
|
||||||
|
|
||||||
export default class ProjectDuplicateModel extends AbstractModel<IProjectDuplicate> implements IProjectDuplicate {
|
export default class ProjectDuplicateModel extends AbstractModel<IProjectDuplicate> implements IProjectDuplicate {
|
||||||
projectId = 0
|
projectId = 0
|
||||||
namespaceId: INamespace['id'] = 0
|
|
||||||
project: IProject = ProjectModel
|
project: IProject = ProjectModel
|
||||||
|
|
||||||
constructor(data : Partial<IProjectDuplicate>) {
|
constructor(data : Partial<IProjectDuplicate>) {
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
import TeamShareBaseModel from './teamShareBase'
|
|
||||||
|
|
||||||
import type {ITeamNamespace} from '@/modelTypes/ITeamNamespace'
|
|
||||||
import type {INamespace} from '@/modelTypes/INamespace'
|
|
||||||
|
|
||||||
export default class TeamNamespaceModel extends TeamShareBaseModel implements ITeamNamespace {
|
|
||||||
namespaceId: INamespace['id'] = 0
|
|
||||||
|
|
||||||
constructor(data: Partial<ITeamNamespace>) {
|
|
||||||
super(data)
|
|
||||||
this.assignData(data)
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,7 +6,7 @@ import type {ITeam} from '@/modelTypes/ITeam'
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is a base class for common team sharing model.
|
* This class is a base class for common team sharing model.
|
||||||
* It is extended in a way so it can be used for namespaces as well for projects.
|
* It is extended in a way, so it can be used for projects.
|
||||||
*/
|
*/
|
||||||
export default class TeamShareBaseModel extends AbstractModel<ITeamShareBase> implements ITeamShareBase {
|
export default class TeamShareBaseModel extends AbstractModel<ITeamShareBase> implements ITeamShareBase {
|
||||||
teamId: ITeam['id'] = 0
|
teamId: ITeam['id'] = 0
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
import UserShareBaseModel from './userShareBase'
|
|
||||||
|
|
||||||
import type {INamespace} from '@/modelTypes/INamespace'
|
|
||||||
import type {IUserNamespace} from '@/modelTypes/IUserNamespace'
|
|
||||||
|
|
||||||
// This class extends the user share model with a 'rights' parameter which is used in sharing
|
|
||||||
export default class UserNamespaceModel extends UserShareBaseModel implements IUserNamespace {
|
|
||||||
namespaceId: INamespace['id'] = 0
|
|
||||||
|
|
||||||
constructor(data: Partial<IUserNamespace>) {
|
|
||||||
super(data)
|
|
||||||
this.assignData(data)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
import AbstractService from './abstractService'
|
|
||||||
import NamespaceModel from '../models/namespace'
|
|
||||||
import type {INamespace} from '@/modelTypes/INamespace'
|
|
||||||
import {colorFromHex} from '@/helpers/color/colorFromHex'
|
|
||||||
|
|
||||||
export default class NamespaceService extends AbstractService<INamespace> {
|
|
||||||
constructor() {
|
|
||||||
super({
|
|
||||||
create: '/namespaces',
|
|
||||||
get: '/namespaces/{id}',
|
|
||||||
getAll: '/namespaces',
|
|
||||||
update: '/namespaces/{id}',
|
|
||||||
delete: '/namespaces/{id}',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
modelFactory(data) {
|
|
||||||
return new NamespaceModel(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeUpdate(namespace) {
|
|
||||||
namespace.hexColor = colorFromHex(namespace.hexColor)
|
|
||||||
return namespace
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeCreate(namespace) {
|
|
||||||
namespace.hexColor = colorFromHex(namespace.hexColor)
|
|
||||||
return namespace
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,7 +7,7 @@ import {colorFromHex} from '@/helpers/color/colorFromHex'
|
|||||||
export default class ProjectService extends AbstractService<IProject> {
|
export default class ProjectService extends AbstractService<IProject> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
create: '/namespaces/{namespaceId}/projects',
|
create: '/projects',
|
||||||
get: '/projects/{id}',
|
get: '/projects/{id}',
|
||||||
getAll: '/projects',
|
getAll: '/projects',
|
||||||
update: '/projects/{id}',
|
update: '/projects/{id}',
|
||||||
|
@ -12,7 +12,6 @@ import AbstractService from '@/services/abstractService'
|
|||||||
import SavedFilterModel from '@/models/savedFilter'
|
import SavedFilterModel from '@/models/savedFilter'
|
||||||
|
|
||||||
import {useBaseStore} from '@/stores/base'
|
import {useBaseStore} from '@/stores/base'
|
||||||
import {useNamespaceStore} from '@/stores/namespaces'
|
|
||||||
|
|
||||||
import {objectToSnakeCase, objectToCamelCase} from '@/helpers/case'
|
import {objectToSnakeCase, objectToCamelCase} from '@/helpers/case'
|
||||||
import {success} from '@/message'
|
import {success} from '@/message'
|
||||||
@ -81,7 +80,6 @@ export default class SavedFilterService extends AbstractService<ISavedFilter> {
|
|||||||
export function useSavedFilter(projectId?: MaybeRef<IProject['id']>) {
|
export function useSavedFilter(projectId?: MaybeRef<IProject['id']>) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const {t} = useI18n({useScope:'global'})
|
const {t} = useI18n({useScope:'global'})
|
||||||
const namespaceStore = useNamespaceStore()
|
|
||||||
|
|
||||||
const filterService = shallowReactive(new SavedFilterService())
|
const filterService = shallowReactive(new SavedFilterService())
|
||||||
|
|
||||||
@ -110,13 +108,11 @@ export function useSavedFilter(projectId?: MaybeRef<IProject['id']>) {
|
|||||||
|
|
||||||
async function createFilter() {
|
async function createFilter() {
|
||||||
filter.value = await filterService.create(filter.value)
|
filter.value = await filterService.create(filter.value)
|
||||||
await namespaceStore.loadNamespaces()
|
|
||||||
router.push({name: 'project.index', params: {projectId: getProjectId(filter.value)}})
|
router.push({name: 'project.index', params: {projectId: getProjectId(filter.value)}})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveFilter() {
|
async function saveFilter() {
|
||||||
const response = await filterService.update(filter.value)
|
const response = await filterService.update(filter.value)
|
||||||
await namespaceStore.loadNamespaces()
|
|
||||||
success({message: t('filters.edit.success')})
|
success({message: t('filters.edit.success')})
|
||||||
response.filters = objectToSnakeCase(response.filters)
|
response.filters = objectToSnakeCase(response.filters)
|
||||||
filter.value = response
|
filter.value = response
|
||||||
@ -129,7 +125,6 @@ export function useSavedFilter(projectId?: MaybeRef<IProject['id']>) {
|
|||||||
|
|
||||||
async function deleteFilter() {
|
async function deleteFilter() {
|
||||||
await filterService.delete(filter.value)
|
await filterService.delete(filter.value)
|
||||||
await namespaceStore.loadNamespaces()
|
|
||||||
success({message: t('filters.delete.success')})
|
success({message: t('filters.delete.success')})
|
||||||
router.push({name: 'projects.index'})
|
router.push({name: 'projects.index'})
|
||||||
}
|
}
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
import AbstractService from './abstractService'
|
|
||||||
import TeamNamespaceModel from '@/models/teamNamespace'
|
|
||||||
import type {ITeamNamespace} from '@/modelTypes/ITeamNamespace'
|
|
||||||
import TeamModel from '@/models/team'
|
|
||||||
|
|
||||||
export default class TeamNamespaceService extends AbstractService<ITeamNamespace> {
|
|
||||||
constructor() {
|
|
||||||
super({
|
|
||||||
create: '/namespaces/{namespaceId}/teams',
|
|
||||||
getAll: '/namespaces/{namespaceId}/teams',
|
|
||||||
update: '/namespaces/{namespaceId}/teams/{teamId}',
|
|
||||||
delete: '/namespaces/{namespaceId}/teams/{teamId}',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
modelFactory(data) {
|
|
||||||
return new TeamNamespaceModel(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
modelGetAllFactory(data) {
|
|
||||||
return new TeamModel(data)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
import AbstractService from './abstractService'
|
|
||||||
import UserNamespaceModel from '@/models/userNamespace'
|
|
||||||
import type {IUserNamespace} from '@/modelTypes/IUserNamespace'
|
|
||||||
import UserModel from '@/models/user'
|
|
||||||
|
|
||||||
export default class UserNamespaceService extends AbstractService<IUserNamespace> {
|
|
||||||
constructor() {
|
|
||||||
super({
|
|
||||||
create: '/namespaces/{namespaceId}/users',
|
|
||||||
getAll: '/namespaces/{namespaceId}/users',
|
|
||||||
update: '/namespaces/{namespaceId}/users/{userId}',
|
|
||||||
delete: '/namespaces/{namespaceId}/users/{userId}',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
modelFactory(data) {
|
|
||||||
return new UserNamespaceModel(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
modelGetAllFactory(data) {
|
|
||||||
return new UserModel(data)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,236 +0,0 @@
|
|||||||
import {computed, readonly, ref} from 'vue'
|
|
||||||
import {defineStore, acceptHMRUpdate} from 'pinia'
|
|
||||||
|
|
||||||
import NamespaceService from '../services/namespace'
|
|
||||||
import {setModuleLoading} from '@/stores/helper'
|
|
||||||
import {createNewIndexer} from '@/indexes'
|
|
||||||
import type {INamespace} from '@/modelTypes/INamespace'
|
|
||||||
import type {IProject} from '@/modelTypes/IProject'
|
|
||||||
import {useProjectStore} from '@/stores/projects'
|
|
||||||
|
|
||||||
const {add, remove, search, update} = createNewIndexer('namespaces', ['title', 'description'])
|
|
||||||
|
|
||||||
export const useNamespaceStore = defineStore('namespace', () => {
|
|
||||||
const projectStore = useProjectStore()
|
|
||||||
|
|
||||||
const isLoading = ref(false)
|
|
||||||
// FIXME: should be object with id as key
|
|
||||||
const namespaces = ref<INamespace[]>([])
|
|
||||||
|
|
||||||
const getProjectAndNamespaceById = computed(() => (projectId: IProject['id'], ignorePseudoNamespaces = false) => {
|
|
||||||
for (const n in namespaces.value) {
|
|
||||||
|
|
||||||
if (ignorePseudoNamespaces && namespaces.value[n].id < 0) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const l in namespaces.value[n].projects) {
|
|
||||||
if (namespaces.value[n].projects[l].id === projectId) {
|
|
||||||
return {
|
|
||||||
project: namespaces.value[n].projects[l],
|
|
||||||
namespace: namespaces.value[n],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
})
|
|
||||||
|
|
||||||
const getNamespaceById = computed(() => (namespaceId: INamespace['id']) => {
|
|
||||||
return namespaces.value.find(({id}) => id == namespaceId) || null
|
|
||||||
})
|
|
||||||
|
|
||||||
const searchNamespace = computed(() => {
|
|
||||||
return (query: string) => (
|
|
||||||
search(query)
|
|
||||||
?.filter(value => value > 0)
|
|
||||||
.map(getNamespaceById.value)
|
|
||||||
.filter(n => n !== null)
|
|
||||||
|| []
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
function setIsLoading(newIsLoading: boolean) {
|
|
||||||
isLoading.value = newIsLoading
|
|
||||||
}
|
|
||||||
|
|
||||||
function setNamespaces(newNamespaces: INamespace[]) {
|
|
||||||
namespaces.value = newNamespaces
|
|
||||||
newNamespaces.forEach(n => {
|
|
||||||
add(n)
|
|
||||||
|
|
||||||
// Check for each project in that namespace if it has a subscription and set it if not
|
|
||||||
n.projects.forEach(l => {
|
|
||||||
if (l.subscription === null || l.subscription.entity !== 'project') {
|
|
||||||
l.subscription = n.subscription
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function setNamespaceById(namespace: INamespace) {
|
|
||||||
const namespaceIndex = namespaces.value.findIndex(n => n.id === namespace.id)
|
|
||||||
|
|
||||||
if (namespaceIndex === -1) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!namespace.projects || namespace.projects.length === 0) {
|
|
||||||
namespace.projects = namespaces.value[namespaceIndex].projects
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for each project in that namespace if it has a subscription and set it if not
|
|
||||||
namespace.projects.forEach(l => {
|
|
||||||
if (l.subscription === null || l.subscription.entity !== 'project') {
|
|
||||||
l.subscription = namespace.subscription
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
namespaces.value[namespaceIndex] = namespace
|
|
||||||
update(namespace)
|
|
||||||
}
|
|
||||||
|
|
||||||
function setProjectInNamespaceById(project: IProject) {
|
|
||||||
for (const n in namespaces.value) {
|
|
||||||
// We don't have the namespace id on the project which means we need to loop over all projects until we find it.
|
|
||||||
// FIXME: Not ideal at all - we should fix that at the api level.
|
|
||||||
if (namespaces.value[n].id === project.namespaceId) {
|
|
||||||
for (const l in namespaces.value[n].projects) {
|
|
||||||
if (namespaces.value[n].projects[l].id === project.id) {
|
|
||||||
const namespace = namespaces.value[n]
|
|
||||||
namespace.projects[l] = project
|
|
||||||
namespaces.value[n] = namespace
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addNamespace(namespace: INamespace) {
|
|
||||||
namespaces.value.push(namespace)
|
|
||||||
add(namespace)
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeNamespaceById(namespaceId: INamespace['id']) {
|
|
||||||
for (const n in namespaces.value) {
|
|
||||||
if (namespaces.value[n].id === namespaceId) {
|
|
||||||
remove(namespaces.value[n])
|
|
||||||
namespaces.value.splice(n, 1)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addProjectToNamespace(project: IProject) {
|
|
||||||
for (const n in namespaces.value) {
|
|
||||||
if (namespaces.value[n].id === project.namespaceId) {
|
|
||||||
namespaces.value[n].projects.push(project)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeProjectFromNamespaceById(project: IProject) {
|
|
||||||
for (const n in namespaces.value) {
|
|
||||||
// We don't have the namespace id on the project which means we need to loop over all projects until we find it.
|
|
||||||
// FIXME: Not ideal at all - we should fix that at the api level.
|
|
||||||
if (namespaces.value[n].id === project.namespaceId) {
|
|
||||||
for (const l in namespaces.value[n].projects) {
|
|
||||||
if (namespaces.value[n].projects[l].id === project.id) {
|
|
||||||
namespaces.value[n].projects.splice(l, 1)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadNamespaces() {
|
|
||||||
const cancel = setModuleLoading(setIsLoading)
|
|
||||||
|
|
||||||
const namespaceService = new NamespaceService()
|
|
||||||
try {
|
|
||||||
// We always load all namespaces and filter them on the frontend
|
|
||||||
const namespaces = await namespaceService.getAll({}, {is_archived: true}) as INamespace[]
|
|
||||||
setNamespaces(namespaces)
|
|
||||||
|
|
||||||
// Put all projects in the project state
|
|
||||||
const projects = namespaces.flatMap(({projects}) => projects)
|
|
||||||
|
|
||||||
projectStore.setProjects(projects)
|
|
||||||
|
|
||||||
return namespaces
|
|
||||||
} finally {
|
|
||||||
cancel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadNamespacesIfFavoritesDontExist() {
|
|
||||||
// The first or second namespace should be the one holding all favorites
|
|
||||||
if (namespaces.value[0].id === -2 || namespaces.value[1]?.id === -2) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return loadNamespaces()
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeFavoritesNamespaceIfEmpty() {
|
|
||||||
if (namespaces.value[0].id === -2 && namespaces.value[0].projects.length === 0) {
|
|
||||||
namespaces.value.splice(0, 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function deleteNamespace(namespace: INamespace) {
|
|
||||||
const cancel = setModuleLoading(setIsLoading)
|
|
||||||
const namespaceService = new NamespaceService()
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await namespaceService.delete(namespace)
|
|
||||||
removeNamespaceById(namespace.id)
|
|
||||||
return response
|
|
||||||
} finally {
|
|
||||||
cancel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createNamespace(namespace: INamespace) {
|
|
||||||
const cancel = setModuleLoading(setIsLoading)
|
|
||||||
const namespaceService = new NamespaceService()
|
|
||||||
|
|
||||||
try {
|
|
||||||
const createdNamespace = await namespaceService.create(namespace)
|
|
||||||
addNamespace(createdNamespace)
|
|
||||||
return createdNamespace
|
|
||||||
} finally {
|
|
||||||
cancel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
isLoading: readonly(isLoading),
|
|
||||||
namespaces: readonly(namespaces),
|
|
||||||
|
|
||||||
getProjectAndNamespaceById,
|
|
||||||
getNamespaceById,
|
|
||||||
searchNamespace,
|
|
||||||
|
|
||||||
setNamespaces,
|
|
||||||
setNamespaceById,
|
|
||||||
setProjectInNamespaceById,
|
|
||||||
addNamespace,
|
|
||||||
removeNamespaceById,
|
|
||||||
addProjectToNamespace,
|
|
||||||
removeProjectFromNamespaceById,
|
|
||||||
loadNamespaces,
|
|
||||||
loadNamespacesIfFavoritesDontExist,
|
|
||||||
removeFavoritesNamespaceIfEmpty,
|
|
||||||
deleteNamespace,
|
|
||||||
createNamespace,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// support hot reloading
|
|
||||||
if (import.meta.hot) {
|
|
||||||
import.meta.hot.accept(acceptHMRUpdate(useNamespaceStore, import.meta.hot))
|
|
||||||
}
|
|
@ -6,7 +6,6 @@ import ProjectService from '@/services/project'
|
|||||||
import {setModuleLoading} from '@/stores/helper'
|
import {setModuleLoading} from '@/stores/helper'
|
||||||
import {removeProjectFromHistory} from '@/modules/projectHistory'
|
import {removeProjectFromHistory} from '@/modules/projectHistory'
|
||||||
import {createNewIndexer} from '@/indexes'
|
import {createNewIndexer} from '@/indexes'
|
||||||
import {useNamespaceStore} from './namespaces'
|
|
||||||
|
|
||||||
import type {IProject} from '@/modelTypes/IProject'
|
import type {IProject} from '@/modelTypes/IProject'
|
||||||
|
|
||||||
@ -26,7 +25,6 @@ export interface ProjectState {
|
|||||||
|
|
||||||
export const useProjectStore = defineStore('project', () => {
|
export const useProjectStore = defineStore('project', () => {
|
||||||
const baseStore = useBaseStore()
|
const baseStore = useBaseStore()
|
||||||
const namespaceStore = useNamespaceStore()
|
|
||||||
|
|
||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
|
|
||||||
@ -100,8 +98,6 @@ export const useProjectStore = defineStore('project', () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const createdProject = await projectService.create(project)
|
const createdProject = await projectService.create(project)
|
||||||
createdProject.namespaceId = project.namespaceId
|
|
||||||
namespaceStore.addProjectToNamespace(createdProject)
|
|
||||||
setProject(createdProject)
|
setProject(createdProject)
|
||||||
return createdProject
|
return createdProject
|
||||||
} finally {
|
} finally {
|
||||||
@ -116,21 +112,13 @@ export const useProjectStore = defineStore('project', () => {
|
|||||||
try {
|
try {
|
||||||
await projectService.update(project)
|
await projectService.update(project)
|
||||||
setProject(project)
|
setProject(project)
|
||||||
namespaceStore.setProjectInNamespaceById(project)
|
|
||||||
|
|
||||||
// the returned project from projectService.update is the same!
|
// the returned project from projectService.update is the same!
|
||||||
// in order to not create a manipulation in pinia store we have to create a new copy
|
// in order to not create a manipulation in pinia store we have to create a new copy
|
||||||
const newProject = {
|
const newProject = {
|
||||||
...project,
|
...project,
|
||||||
namespaceId: FavoriteProjectsNamespace,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespaceStore.removeProjectFromNamespaceById(newProject)
|
|
||||||
if (project.isFavorite) {
|
|
||||||
namespaceStore.addProjectToNamespace(newProject)
|
|
||||||
}
|
|
||||||
namespaceStore.loadNamespacesIfFavoritesDontExist()
|
|
||||||
namespaceStore.removeFavoritesNamespaceIfEmpty()
|
|
||||||
return newProject
|
return newProject
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Reset the project state to the initial one to avoid confusion for the user
|
// Reset the project state to the initial one to avoid confusion for the user
|
||||||
@ -151,7 +139,6 @@ export const useProjectStore = defineStore('project', () => {
|
|||||||
try {
|
try {
|
||||||
const response = await projectService.delete(project)
|
const response = await projectService.delete(project)
|
||||||
removeProjectById(project)
|
removeProjectById(project)
|
||||||
namespaceStore.removeProjectFromNamespaceById(project)
|
|
||||||
removeProjectFromHistory({id: project.id})
|
removeProjectFromHistory({id: project.id})
|
||||||
return response
|
return response
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -17,22 +17,11 @@
|
|||||||
@taskAdded="updateTaskKey"
|
@taskAdded="updateTaskKey"
|
||||||
class="is-max-width-desktop"
|
class="is-max-width-desktop"
|
||||||
/>
|
/>
|
||||||
<template v-if="!hasTasks && !loading">
|
<template v-if="!hasTasks && !loading && migratorsEnabled">
|
||||||
<template v-if="defaultNamespaceId > 0">
|
<p class="mt-4">
|
||||||
<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">
|
|
||||||
{{ $t('home.project.importText') }}
|
{{ $t('home.project.importText') }}
|
||||||
</p>
|
</p>
|
||||||
<x-button
|
<x-button
|
||||||
v-if="migratorsEnabled"
|
|
||||||
:to="{ name: 'migrate.start' }"
|
:to="{ name: 'migrate.start' }"
|
||||||
:shadow="false">
|
:shadow="false">
|
||||||
{{ $t('home.project.import') }}
|
{{ $t('home.project.import') }}
|
||||||
@ -66,7 +55,6 @@ import {useDaytimeSalutation} from '@/composables/useDaytimeSalutation'
|
|||||||
import {useBaseStore} from '@/stores/base'
|
import {useBaseStore} from '@/stores/base'
|
||||||
import {useProjectStore} from '@/stores/projects'
|
import {useProjectStore} from '@/stores/projects'
|
||||||
import {useConfigStore} from '@/stores/config'
|
import {useConfigStore} from '@/stores/config'
|
||||||
import {useNamespaceStore} from '@/stores/namespaces'
|
|
||||||
import {useAuthStore} from '@/stores/auth'
|
import {useAuthStore} from '@/stores/auth'
|
||||||
import {useTaskStore} from '@/stores/tasks'
|
import {useTaskStore} from '@/stores/tasks'
|
||||||
import type {IProject} from '@/modelTypes/IProject'
|
import type {IProject} from '@/modelTypes/IProject'
|
||||||
@ -76,7 +64,6 @@ const salutation = useDaytimeSalutation()
|
|||||||
const baseStore = useBaseStore()
|
const baseStore = useBaseStore()
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
const configStore = useConfigStore()
|
const configStore = useConfigStore()
|
||||||
const namespaceStore = useNamespaceStore()
|
|
||||||
const projectStore = useProjectStore()
|
const projectStore = useProjectStore()
|
||||||
const taskStore = useTaskStore()
|
const taskStore = useTaskStore()
|
||||||
|
|
||||||
@ -93,8 +80,7 @@ const projectHistory = computed(() => {
|
|||||||
|
|
||||||
const migratorsEnabled = computed(() => configStore.availableMigrators?.length > 0)
|
const migratorsEnabled = computed(() => configStore.availableMigrators?.length > 0)
|
||||||
const hasTasks = computed(() => baseStore.hasTasks)
|
const hasTasks = computed(() => baseStore.hasTasks)
|
||||||
const defaultNamespaceId = computed(() => namespaceStore.namespaces?.[0]?.id || 0)
|
const hasProjects = computed(() => projectStore.projects.length > 0)
|
||||||
const hasProjects = computed(() => namespaceStore.namespaces?.[0]?.projects.length > 0)
|
|
||||||
const loading = computed(() => taskStore.isLoading)
|
const loading = computed(() => taskStore.isLoading)
|
||||||
const deletionScheduledAt = computed(() => parseDateOrNull(authStore.info?.deletionScheduledAt))
|
const deletionScheduledAt = computed(() => parseDateOrNull(authStore.info?.deletionScheduledAt))
|
||||||
|
|
||||||
|
@ -89,8 +89,8 @@ import {formatDateLong} from '@/helpers/time/formatDate'
|
|||||||
import {parseDateOrNull} from '@/helpers/parseDateOrNull'
|
import {parseDateOrNull} from '@/helpers/parseDateOrNull'
|
||||||
|
|
||||||
import {MIGRATORS} from './migrators'
|
import {MIGRATORS} from './migrators'
|
||||||
import {useNamespaceStore} from '@/stores/namespaces'
|
|
||||||
import {useTitle} from '@/composables/useTitle'
|
import {useTitle} from '@/composables/useTitle'
|
||||||
|
import {useProjectStore} from '@/stores/projects'
|
||||||
|
|
||||||
const PROGRESS_DOTS_COUNT = 8
|
const PROGRESS_DOTS_COUNT = 8
|
||||||
|
|
||||||
@ -163,8 +163,8 @@ async function migrate() {
|
|||||||
? await migrationFileService.migrate(migrationConfig as File)
|
? await migrationFileService.migrate(migrationConfig as File)
|
||||||
: await migrationService.migrate(migrationConfig as MigrationConfig)
|
: await migrationService.migrate(migrationConfig as MigrationConfig)
|
||||||
message.value = result.message
|
message.value = result.message
|
||||||
const namespaceStore = useNamespaceStore()
|
const projectStore = useProjectStore()
|
||||||
return namespaceStore.loadNamespaces()
|
return projectStore.loadProjects()
|
||||||
} finally {
|
} finally {
|
||||||
isMigrating.value = false
|
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
|
showError.value = false
|
||||||
|
|
||||||
project.namespaceId = Number(route.params.namespaceId as string)
|
|
||||||
const newProject = await projectStore.createProject(project)
|
const newProject = await projectStore.createProject(project)
|
||||||
await router.push({
|
await router.push({
|
||||||
name: 'project.index',
|
name: 'project.index',
|
||||||
|
@ -108,7 +108,6 @@ import CustomTransition from '@/components/misc/CustomTransition.vue'
|
|||||||
|
|
||||||
import {useBaseStore} from '@/stores/base'
|
import {useBaseStore} from '@/stores/base'
|
||||||
import {useProjectStore} from '@/stores/projects'
|
import {useProjectStore} from '@/stores/projects'
|
||||||
import {useNamespaceStore} from '@/stores/namespaces'
|
|
||||||
import {useConfigStore} from '@/stores/config'
|
import {useConfigStore} from '@/stores/config'
|
||||||
|
|
||||||
import BackgroundUnsplashService from '@/services/backgroundUnsplash'
|
import BackgroundUnsplashService from '@/services/backgroundUnsplash'
|
||||||
@ -146,7 +145,6 @@ const debounceNewBackgroundSearch = debounce(newBackgroundSearch, SEARCH_DEBOUNC
|
|||||||
const backgroundUploadService = ref(new BackgroundUploadService())
|
const backgroundUploadService = ref(new BackgroundUploadService())
|
||||||
const projectService = ref(new ProjectService())
|
const projectService = ref(new ProjectService())
|
||||||
const projectStore = useProjectStore()
|
const projectStore = useProjectStore()
|
||||||
const namespaceStore = useNamespaceStore()
|
|
||||||
const configStore = useConfigStore()
|
const configStore = useConfigStore()
|
||||||
|
|
||||||
const unsplashBackgroundEnabled = computed(() => configStore.enabledBackgroundProviders.includes('unsplash'))
|
const unsplashBackgroundEnabled = computed(() => configStore.enabledBackgroundProviders.includes('unsplash'))
|
||||||
@ -195,7 +193,6 @@ async function setBackground(backgroundId: string) {
|
|||||||
projectId: route.params.projectId,
|
projectId: route.params.projectId,
|
||||||
})
|
})
|
||||||
await baseStore.handleSetCurrentProject({project, forceUpdate: true})
|
await baseStore.handleSetCurrentProject({project, forceUpdate: true})
|
||||||
namespaceStore.setProjectInNamespaceById(project)
|
|
||||||
projectStore.setProject(project)
|
projectStore.setProject(project)
|
||||||
success({message: t('project.background.success')})
|
success({message: t('project.background.success')})
|
||||||
}
|
}
|
||||||
@ -211,7 +208,6 @@ async function uploadBackground() {
|
|||||||
backgroundUploadInput.value?.files[0],
|
backgroundUploadInput.value?.files[0],
|
||||||
)
|
)
|
||||||
await baseStore.handleSetCurrentProject({project, forceUpdate: true})
|
await baseStore.handleSetCurrentProject({project, forceUpdate: true})
|
||||||
namespaceStore.setProjectInNamespaceById(project)
|
|
||||||
projectStore.setProject(project)
|
projectStore.setProject(project)
|
||||||
success({message: t('project.background.success')})
|
success({message: t('project.background.success')})
|
||||||
}
|
}
|
||||||
@ -219,7 +215,6 @@ async function uploadBackground() {
|
|||||||
async function removeBackground() {
|
async function removeBackground() {
|
||||||
const project = await projectService.value.removeBackground(currentProject.value)
|
const project = await projectService.value.removeBackground(currentProject.value)
|
||||||
await baseStore.handleSetCurrentProject({project, forceUpdate: true})
|
await baseStore.handleSetCurrentProject({project, forceUpdate: true})
|
||||||
namespaceStore.setProjectInNamespaceById(project)
|
|
||||||
projectStore.setProject(project)
|
projectStore.setProject(project)
|
||||||
success({message: t('project.background.removeSuccess')})
|
success({message: t('project.background.removeSuccess')})
|
||||||
router.back()
|
router.back()
|
||||||
|
@ -7,15 +7,6 @@
|
|||||||
:loading="projectDuplicateService.loading"
|
:loading="projectDuplicateService.loading"
|
||||||
>
|
>
|
||||||
<p>{{ $t('project.duplicate.text') }}</p>
|
<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>
|
</create-edit>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -29,32 +20,17 @@ import CreateEdit from '@/components/misc/create-edit.vue'
|
|||||||
import Multiselect from '@/components/input/multiselect.vue'
|
import Multiselect from '@/components/input/multiselect.vue'
|
||||||
|
|
||||||
import ProjectDuplicateModel from '@/models/projectDuplicateModel'
|
import ProjectDuplicateModel from '@/models/projectDuplicateModel'
|
||||||
import type {INamespace} from '@/modelTypes/INamespace'
|
|
||||||
|
|
||||||
import {success} from '@/message'
|
import {success} from '@/message'
|
||||||
import {useTitle} from '@/composables/useTitle'
|
import {useTitle} from '@/composables/useTitle'
|
||||||
import {useNamespaceSearch} from '@/composables/useNamespaceSearch'
|
|
||||||
import {useProjectStore} from '@/stores/projects'
|
import {useProjectStore} from '@/stores/projects'
|
||||||
import {useNamespaceStore} from '@/stores/namespaces'
|
|
||||||
|
|
||||||
const {t} = useI18n({useScope: 'global'})
|
const {t} = useI18n({useScope: 'global'})
|
||||||
useTitle(() => t('project.duplicate.title'))
|
useTitle(() => t('project.duplicate.title'))
|
||||||
|
|
||||||
const {
|
|
||||||
namespaces,
|
|
||||||
findNamespaces,
|
|
||||||
} = useNamespaceSearch()
|
|
||||||
|
|
||||||
const selectedNamespace = ref<INamespace>()
|
|
||||||
|
|
||||||
function selectNamespace(namespace: INamespace) {
|
|
||||||
selectedNamespace.value = namespace
|
|
||||||
}
|
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const projectStore = useProjectStore()
|
const projectStore = useProjectStore()
|
||||||
const namespaceStore = useNamespaceStore()
|
|
||||||
|
|
||||||
const projectDuplicateService = shallowReactive(new ProjectDuplicateService())
|
const projectDuplicateService = shallowReactive(new ProjectDuplicateService())
|
||||||
|
|
||||||
@ -62,12 +38,10 @@ async function duplicateProject() {
|
|||||||
const projectDuplicate = new ProjectDuplicateModel({
|
const projectDuplicate = new ProjectDuplicateModel({
|
||||||
// FIXME: should be parameter
|
// FIXME: should be parameter
|
||||||
projectId: route.params.projectId,
|
projectId: route.params.projectId,
|
||||||
namespaceId: selectedNamespace.value?.id,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const duplicate = await projectDuplicateService.create(projectDuplicate)
|
const duplicate = await projectDuplicateService.create(projectDuplicate)
|
||||||
|
|
||||||
namespaceStore.addProjectToNamespace(duplicate.project)
|
|
||||||
projectStore.setProject(duplicate.project)
|
projectStore.setProject(duplicate.project)
|
||||||
success({message: t('project.duplicate.success')})
|
success({message: t('project.duplicate.success')})
|
||||||
router.push({name: 'project.index', params: {projectId: duplicate.project.id}})
|
router.push({name: 'project.index', params: {projectId: duplicate.project.id}})
|
||||||
|
@ -13,8 +13,7 @@
|
|||||||
:can-write="canWrite"
|
:can-write="canWrite"
|
||||||
ref="heading"
|
ref="heading"
|
||||||
/>
|
/>
|
||||||
<h6 class="subtitle" v-if="parent && parent.namespace && parent.project">
|
<h6 class="subtitle" v-if="parent && parent.project">
|
||||||
{{ getNamespaceTitle(parent.namespace) }} ›
|
|
||||||
<router-link :to="{ name: 'project.index', params: { projectId: parent.project.id } }">
|
<router-link :to="{ name: 'project.index', params: { projectId: parent.project.id } }">
|
||||||
{{ getProjectTitle(parent.project) }}
|
{{ getProjectTitle(parent.project) }}
|
||||||
</router-link>
|
</router-link>
|
||||||
@ -486,12 +485,10 @@ import TaskSubscription from '@/components/misc/subscription.vue'
|
|||||||
import CustomTransition from '@/components/misc/CustomTransition.vue'
|
import CustomTransition from '@/components/misc/CustomTransition.vue'
|
||||||
|
|
||||||
import {uploadFile} from '@/helpers/attachments'
|
import {uploadFile} from '@/helpers/attachments'
|
||||||
import {getNamespaceTitle} from '@/helpers/getNamespaceTitle'
|
|
||||||
import {getProjectTitle} from '@/helpers/getProjectTitle'
|
import {getProjectTitle} from '@/helpers/getProjectTitle'
|
||||||
import {scrollIntoView} from '@/helpers/scrollIntoView'
|
import {scrollIntoView} from '@/helpers/scrollIntoView'
|
||||||
|
|
||||||
import {useBaseStore} from '@/stores/base'
|
import {useBaseStore} from '@/stores/base'
|
||||||
import {useNamespaceStore} from '@/stores/namespaces'
|
|
||||||
import {useAttachmentStore} from '@/stores/attachments'
|
import {useAttachmentStore} from '@/stores/attachments'
|
||||||
import {useTaskStore} from '@/stores/tasks'
|
import {useTaskStore} from '@/stores/tasks'
|
||||||
import {useKanbanStore} from '@/stores/kanban'
|
import {useKanbanStore} from '@/stores/kanban'
|
||||||
@ -500,6 +497,7 @@ import {useTitle} from '@/composables/useTitle'
|
|||||||
|
|
||||||
import {success} from '@/message'
|
import {success} from '@/message'
|
||||||
import type {Action as MessageAction} from '@/message'
|
import type {Action as MessageAction} from '@/message'
|
||||||
|
import {useProjectStore} from '@/stores/projects'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
taskId: {
|
taskId: {
|
||||||
@ -517,7 +515,7 @@ const router = useRouter()
|
|||||||
const {t} = useI18n({useScope: 'global'})
|
const {t} = useI18n({useScope: 'global'})
|
||||||
|
|
||||||
const baseStore = useBaseStore()
|
const baseStore = useBaseStore()
|
||||||
const namespaceStore = useNamespaceStore()
|
const projectStore = useProjectStore()
|
||||||
const attachmentStore = useAttachmentStore()
|
const attachmentStore = useAttachmentStore()
|
||||||
const taskStore = useTaskStore()
|
const taskStore = useTaskStore()
|
||||||
const kanbanStore = useKanbanStore()
|
const kanbanStore = useKanbanStore()
|
||||||
@ -541,16 +539,11 @@ const taskId = toRef(props, 'taskId')
|
|||||||
const parent = computed(() => {
|
const parent = computed(() => {
|
||||||
if (!task.projectId) {
|
if (!task.projectId) {
|
||||||
return {
|
return {
|
||||||
namespace: null,
|
|
||||||
project: null,
|
project: null,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!namespaceStore.getProjectAndNamespaceById) {
|
return projectStore.getProjectById(task.projectId)
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return namespaceStore.getProjectAndNamespaceById(task.projectId)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
@ -775,7 +768,6 @@ async function toggleFavorite() {
|
|||||||
task.isFavorite = !task.isFavorite
|
task.isFavorite = !task.isFavorite
|
||||||
const newTask = await taskService.update(task)
|
const newTask = await taskService.update(task)
|
||||||
Object.assign(task, newTask)
|
Object.assign(task, newTask)
|
||||||
await namespaceStore.loadNamespacesIfFavoritesDontExist()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setPriority(priority: Priority) {
|
async function setPriority(priority: Priority) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user