This commit is contained in:
Nikolai Giman
2026-08-22 09:01:33 +02:00
parent a6329e998d
commit 8011da1259
11 changed files with 58 additions and 27 deletions
@@ -1,16 +1,9 @@
import { ALL_TASKS_LIST_ID, type DefaultView } from 'taskview-api'
import type { RouteLocationRaw, Router } from 'vue-router'
import { $tvApi } from '@/plugins/axios'
import { useUserStore } from '@/stores/user.store'
import { useOrganizationStore } from '@/stores/organization.store'
import { useUiPreferencesStore } from '@/stores/uiPreferences.store'
const VIEW_ROUTES: Record<DefaultView, string> = {
tasks: 'user',
kanban: 'kanban',
graph: 'graph',
sprints: 'sprints',
}
import { useProjectRoute } from '@/composables/useProjectRoute'
export const redirectToUser = async (router: Router) => {
const userStore = useUserStore()
@@ -36,15 +29,12 @@ export const resolveDefaultRoute = async (): Promise<RouteLocationRaw | null> =>
// The default project may live in any of the user's organizations; try the current one first
const orgStore = useOrganizationStore()
const orgs = [...orgStore.organizations].sort((a) => (a.slug === orgStore.currentOrgSlug ? -1 : 1))
const { projectRoute } = useProjectRoute()
for (const org of orgs) {
const goals = await $tvApi.goals.fetchGoals(org.id)
if (!goals?.some((goal) => goal.id === projectId)) continue
const view = uiPrefs.settings.defaultView ?? 'tasks'
const params: Record<string, string | number> = { orgSlug: org.slug, projectId }
if (view === 'tasks') params.listId = ALL_TASKS_LIST_ID
return { name: VIEW_ROUTES[view], params }
const goal = goals?.find((g) => g.id === projectId)
if (goal) return projectRoute(goal, org.slug)
}
return null
@@ -25,9 +25,10 @@ import { useOrganizationStore } from '@/stores/organization.store'
import ProjectsList from '@/components/features/projects/ProjectsList.vue'
import ArchiveList from '@/components/features/projects/ArchiveList.vue'
import type { Project, ProjectSaveData } from '@/components/features/projects/types'
import { ALL_TASKS_LIST_ID } from 'taskview-api'
import { useProjectRoute } from '@/composables/useProjectRoute'
const router = useRouter()
const { projectRoute } = useProjectRoute()
const goalsStore = useGoalsStore()
const orgStore = useOrganizationStore()
@@ -75,7 +76,7 @@ async function handleAdd(name: string) {
...(orgStore.currentOrg && { organizationId: orgStore.currentOrg.id }),
})
if (newProject) {
router.push({ name: 'user', params: { projectId: newProject.id, listId: ALL_TASKS_LIST_ID } })
router.push(projectRoute(newProject))
}
}
</script>
@@ -26,7 +26,7 @@
v-for="project in projects"
:key="project.id"
variant="taskview"
:to="{ name: 'user', params: { projectId: project.id, listId: '-1401' } }"
:to="projectRoute(project)"
:active="currentProjectId === project.id"
>
<div
@@ -209,6 +209,7 @@ import ProjectDeleteDialog from '@/components/features/projects/parts/ProjectDel
import ProjectAddInput from '@/components/features/projects/parts/ProjectAddInput.vue'
import type { Project, ProjectSaveData } from '@/components/features/projects/types'
import { useGoalPermissionsFor } from '@/composables/useGoalPermissions'
import { useProjectRoute } from '@/composables/useProjectRoute'
import { useI18n } from 'vue-i18n'
const route = useRoute()
@@ -234,6 +235,7 @@ const emit = defineEmits<{
}>()
const { t } = useI18n()
const { projectRoute } = useProjectRoute()
const isOpen = defineModel<boolean>('open', { required: false, default: true })
@@ -38,7 +38,7 @@
v-model="defaultView"
:items="viewItems"
value-key="value"
:disabled="defaultProject === NONE"
data-testid="default-view-select"
variant="soft"
class="w-full lg:w-72"
size="xl"
@@ -70,7 +70,6 @@ const defaultProject = computed<number>({
get: () => store.settings.defaultProjectId ?? NONE,
set: (value) => {
store.setSetting('defaultProjectId', value === NONE ? undefined : value)
if (value === NONE) store.setSetting('defaultView', undefined)
},
})
@@ -50,8 +50,8 @@
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { ALL_TASKS_LIST_ID } from 'taskview-api'
import { useDashboard } from '@/composables/useDashboard'
import { useProjectRoute } from '@/composables/useProjectRoute'
import { useGoalsStore } from '@/stores/goals.store'
import { useOrganizationStore } from '@/stores/organization.store'
import SearchActivator from '@/components/features/main/screen-main/parts/SearchActivator.vue'
@@ -69,6 +69,7 @@ import SidebarOrgSelect from './dashboard-second/SidebarOrgSelect.vue'
const { t } = useI18n()
const router = useRouter()
const { projectRoute } = useProjectRoute()
const { isSidebarOpen, isSidebarCollapsed } = useDashboard()
const goalsStore = useGoalsStore()
const orgStore = useOrganizationStore()
@@ -79,7 +80,7 @@ async function handleAddProject(name: string) {
...(orgStore.currentOrg && { organizationId: orgStore.currentOrg.id }),
})
if (newProject) {
router.push({ name: 'user', params: { projectId: newProject.id, listId: ALL_TASKS_LIST_ID } })
router.push(projectRoute(newProject))
}
}
</script>
@@ -150,9 +150,9 @@ import { computed, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRoute, useRouter } from 'vue-router'
import { storeToRefs } from 'pinia'
import { ALL_TASKS_LIST_ID } from 'taskview-api'
import { useGoalsStore } from '@/stores/goals.store'
import { useGoalPermissionsFor } from '@/composables/useGoalPermissions'
import { useProjectRoute } from '@/composables/useProjectRoute'
import type { Project, ProjectSaveData } from '@/components/features/projects/types'
import ProjectEditModal from '@/components/features/projects/parts/ProjectEditModal.vue'
import ProjectDeleteDialog from '@/components/features/projects/parts/ProjectDeleteDialog.vue'
@@ -160,6 +160,7 @@ import ProjectDeleteDialog from '@/components/features/projects/parts/ProjectDel
const { t } = useI18n()
const route = useRoute()
const router = useRouter()
const { projectRoute } = useProjectRoute()
const goalsStore = useGoalsStore()
const { goals } = storeToRefs(goalsStore)
@@ -186,7 +187,7 @@ const { canEditGoal, canDeleteGoal } = useGoalPermissionsFor(currentProject)
function selectProject(project: Project) {
open.value = false
router.push({ name: 'user', params: { projectId: project.id, listId: ALL_TASKS_LIST_ID } })
router.push(projectRoute(project))
}
async function archive() {
+37
View File
@@ -0,0 +1,37 @@
import type { RouteLocationRaw } from 'vue-router'
import { ALL_TASKS_LIST_ID, type DefaultView, type GoalItem, type GoalPermissions } from 'taskview-api'
import { useUiPreferencesStore } from '@/stores/uiPreferences.store'
import { AllGoalPermissions } from '@/types/goals.types'
export const VIEW_ROUTES: Record<DefaultView, string> = {
tasks: 'user',
kanban: 'kanban',
graph: 'graph',
sprints: 'sprints',
}
const VIEW_PERMISSIONS: Record<Exclude<DefaultView, 'tasks'>, (keyof GoalPermissions)[]> = {
kanban: [AllGoalPermissions.KANBAN_CAN_VIEW, AllGoalPermissions.KANBAN_CAN_MANAGE],
graph: [AllGoalPermissions.GRAPH_CAN_VIEW, AllGoalPermissions.GRAPH_CAN_MANAGE],
sprints: [AllGoalPermissions.SPRINT_CAN_VIEW],
}
function canOpenView(goal: GoalItem, view: DefaultView): boolean {
if (view === 'tasks') return true
return VIEW_PERMISSIONS[view].some((perm) => !!goal.permissions[perm])
}
export function useProjectRoute() {
const uiPrefs = useUiPreferencesStore()
function projectRoute(goal: GoalItem, orgSlug?: string): RouteLocationRaw {
const preferred = uiPrefs.settings.defaultView ?? 'tasks'
const view: DefaultView = canOpenView(goal, preferred) ? preferred : 'tasks'
const params: Record<string, string | number> = { projectId: goal.id }
if (orgSlug) params.orgSlug = orgSlug
if (view === 'tasks') params.listId = ALL_TASKS_LIST_ID
return { name: VIEW_ROUTES[view], params }
}
return { projectRoute }
}
+1 -1
View File
@@ -810,7 +810,7 @@ export default {
defaultProjectHint: 'Dieses Projekt direkt nach der Anmeldung öffnen.',
defaultProjectNone: 'Startbildschirm (Standard)',
defaultView: 'Standardansicht',
defaultViewHint: 'Welche Ansicht des Projekts geöffnet wird.',
defaultViewHint: 'Welche Ansicht beim Öffnen eines Projekts angezeigt wird.',
viewTasks: 'Aufgaben',
viewKanban: 'Kanban',
viewGraph: 'Graph',
+1 -1
View File
@@ -824,7 +824,7 @@ export default {
defaultProjectHint: 'Open this project right after signing in.',
defaultProjectNone: 'Home screen (default)',
defaultView: 'Default view',
defaultViewHint: 'Which view of the default project to open.',
defaultViewHint: 'Which view to open when you enter a project.',
viewTasks: 'Tasks',
viewKanban: 'Kanban',
viewGraph: 'Graph',
+1 -1
View File
@@ -810,7 +810,7 @@ export default {
defaultProjectHint: 'Abrir este proyecto justo después de iniciar sesión.',
defaultProjectNone: 'Pantalla de inicio (predeterminado)',
defaultView: 'Vista predeterminada',
defaultViewHint: 'Qué vista del proyecto abrir.',
defaultViewHint: 'Qué vista abrir al entrar en un proyecto.',
viewTasks: 'Tareas',
viewKanban: 'Kanban',
viewGraph: 'Grafo',
+1 -1
View File
@@ -797,7 +797,7 @@ export default {
defaultProjectHint: 'Открывать этот проект сразу после входа.',
defaultProjectNone: 'Главный экран (по умолчанию)',
defaultView: 'Вид по умолчанию',
defaultViewHint: 'Какой вид проекта открывать.',
defaultViewHint: 'Какой вид открывать при переходе в проект.',
viewTasks: 'Задачи',
viewKanban: 'Канбан',
viewGraph: 'Граф',