diff --git a/frontend/src/i18n/lang/en.json b/frontend/src/i18n/lang/en.json index 2fe5e9361..ada85a81f 100644 --- a/frontend/src/i18n/lang/en.json +++ b/frontend/src/i18n/lang/en.json @@ -95,6 +95,7 @@ "weekStartMonday": "Monday", "language": "Language", "defaultProject": "Default Project", + "defaultView": "Default View", "timezone": "Time Zone", "overdueTasksRemindersTime": "Overdue tasks reminder email time", "filterUsedOnOverview": "Saved filter used on the overview page" @@ -314,6 +315,9 @@ "delete": "Delete" } }, + "first": { + "title": "First View" + }, "list": { "title": "List", "add": "Add", diff --git a/frontend/src/modelTypes/IProjectView.ts b/frontend/src/modelTypes/IProjectView.ts index 076f072af..4a0565729 100644 --- a/frontend/src/modelTypes/IProjectView.ts +++ b/frontend/src/modelTypes/IProjectView.ts @@ -9,6 +9,12 @@ export const PROJECT_VIEW_KINDS = { } as const export type ProjectViewKind = typeof PROJECT_VIEW_KINDS[keyof typeof PROJECT_VIEW_KINDS] +export const DEFAULT_PROJECT_VIEW_SETTINGS = { + FIRST: 'first', + ...PROJECT_VIEW_KINDS, +} as const +export type DefaultProjectViewKind = typeof DEFAULT_PROJECT_VIEW_SETTINGS[keyof typeof DEFAULT_PROJECT_VIEW_SETTINGS] + export const PROJECT_VIEW_BUCKET_CONFIGURATION_MODES = ['none', 'manual', 'filter'] export type ProjectViewBucketConfigurationMode = typeof PROJECT_VIEW_BUCKET_CONFIGURATION_MODES[number] diff --git a/frontend/src/modelTypes/IUserSettings.ts b/frontend/src/modelTypes/IUserSettings.ts index 5f20e3852..0ca266288 100644 --- a/frontend/src/modelTypes/IUserSettings.ts +++ b/frontend/src/modelTypes/IUserSettings.ts @@ -4,12 +4,14 @@ import type {IProject} from './IProject' import type {PrefixMode} from '@/modules/parseTaskText' import type {BasicColorSchema} from '@vueuse/core' import type {SupportedLocale} from '@/i18n' +import type {DefaultProjectViewKind} from '@/modelTypes/IProjectView' export interface IFrontendSettings { playSoundWhenDone: boolean quickAddMagicMode: PrefixMode colorSchema: BasicColorSchema filterIdUsedOnOverview: IProject['id'] | null + defaultView?: DefaultProjectViewKind } export interface IUserSettings extends IAbstract { diff --git a/frontend/src/models/userSettings.ts b/frontend/src/models/userSettings.ts index 397963dff..31ec7b745 100644 --- a/frontend/src/models/userSettings.ts +++ b/frontend/src/models/userSettings.ts @@ -3,6 +3,7 @@ import AbstractModel from './abstractModel' import type {IFrontendSettings, IUserSettings} from '@/modelTypes/IUserSettings' import {getBrowserLanguage} from '@/i18n' import {PrefixMode} from '@/modules/parseTaskText' +import {DEFAULT_PROJECT_VIEW_SETTINGS} from '@/modelTypes/IProjectView' export default class UserSettingsModel extends AbstractModel implements IUserSettings { name = '' @@ -19,6 +20,7 @@ export default class UserSettingsModel extends AbstractModel impl playSoundWhenDone: true, quickAddMagicMode: PrefixMode.Default, colorSchema: 'auto', + defaultView: DEFAULT_PROJECT_VIEW_SETTINGS.FIRST, } constructor(data: Partial = {}) { diff --git a/frontend/src/views/project/ProjectView.vue b/frontend/src/views/project/ProjectView.vue index 86bdfa449..33a356245 100644 --- a/frontend/src/views/project/ProjectView.vue +++ b/frontend/src/views/project/ProjectView.vue @@ -8,6 +8,8 @@ import ProjectList from '@/components/project/views/ProjectList.vue' import ProjectGantt from '@/components/project/views/ProjectGantt.vue' import ProjectTable from '@/components/project/views/ProjectTable.vue' import ProjectKanban from '@/components/project/views/ProjectKanban.vue' +import {useAuthStore} from '@/stores/auth' +import {DEFAULT_PROJECT_VIEW_SETTINGS} from '@/modelTypes/IProjectView' const { projectId, @@ -19,6 +21,7 @@ const { const router = useRouter() const projectStore = useProjectStore() +const authStore = useAuthStore() const currentView = computed(() => { const project = projectStore.projects[projectId] @@ -26,17 +29,27 @@ const currentView = computed(() => { return project?.views.find(v => v.id === viewId) }) -function redirectToFirstViewIfNecessary() { +function redirectToDefaultViewIfNecessary() { if (viewId === 0 || !projectStore.projects[projectId]?.views.find(v => v.id === viewId)) { // Ideally, we would do that in the router redirect, but the projects (and therefore, the views) // are not always loaded then. - const firstViewId = projectStore.projects[projectId]?.views[0].id - if (firstViewId) { + + let view + if (authStore.settings.frontendSettings.defaultView !== DEFAULT_PROJECT_VIEW_SETTINGS.FIRST) { + view = projectStore.projects[projectId]?.views.find(v => v.viewKind === authStore.settings.frontendSettings.defaultView) + } + + // Use the first view as fallback if the default view is not available + if (view === undefined && projectStore.projects[projectId]?.views?.length > 0) { + view = projectStore.projects[projectId]?.views[0] + } + + if (view) { router.replace({ name: 'project.view', params: { projectId, - viewId: firstViewId, + viewId: view.id, }, }) } @@ -45,13 +58,13 @@ function redirectToFirstViewIfNecessary() { watch( () => viewId, - redirectToFirstViewIfNecessary, + redirectToDefaultViewIfNecessary, {immediate: true}, ) watch( () => projectStore.projects[projectId], - redirectToFirstViewIfNecessary, + redirectToDefaultViewIfNecessary, ) // using a watcher instead of beforeEnter because beforeEnter is not called when only the viewId changes diff --git a/frontend/src/views/user/settings/General.vue b/frontend/src/views/user/settings/General.vue index 64622d11b..97ec38023 100644 --- a/frontend/src/views/user/settings/General.vue +++ b/frontend/src/views/user/settings/General.vue @@ -26,6 +26,22 @@ +
+ +
+ +
+
`${t('user.settings.general.title')} - ${t('user.settings.title')}`) @@ -253,12 +270,15 @@ function useAvailableTimezones() { const availableTimezones = useAvailableTimezones() const authStore = useAuthStore() + const settings = ref({ ...authStore.settings, frontendSettings: { // Sub objects get exported as read only as well, so we need to // explicitly spread the object here to allow modification ...authStore.settings.frontendSettings, + // Add fallback for old settings that don't have the default view set + defaultView: authStore.settings.frontendSettings.defaultView ?? DEFAULT_PROJECT_VIEW_SETTINGS.FIRST, }, }) const id = ref(createRandomID())