mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-13 16:14:07 -08:00
3db53a1934
* refactor(editor): N8N-4540 Main navigation layout rework (#4060) * ✨ Implemented new editor layout using css grid * ✨ Reworking main navigation layout, migrating some styling to css modules * ✨ Reworking main sidebar layout and responsiveness * 💄 Minor type update * ✨ Updated editor grid layout so empty cells are collapsed (`fit-content`), fixed updates menu items styling * ✨ Implemented new user area look & feel in main sidebar * 💄 Adjusting sidebar bottom padding when user area is not shown * 💄 CSS cleanup/refactor + minor vue refactoring * ✨ Fixing overscoll issue in chrome and scrolling behaviour of the content view * 👌 Addressing review feedback * ✨ Added collapsed and expanded versions of n8n logo * ✨ Updating infinite scrolling in templates view to work with the new layout * 💄 Updating main sidebar expanded width and templates view left margin * 💄 Updating main content height * 💄 Adding global styles for scrollable views with centered content, minor updates to user area * ✨ Updating zoomToFit logic, lasso select box position and new nodes positioning * ✨ Fixing new node drop position now that mouse detection has been adjusted * 👌 Updating templates view scroll to top logic and responsive padding, aligning menu items titles * 💄 Moving template layout style from global css class to component level * ✨ Moved 'Workflows' menu to node view header. Added new dropdown component for user area and the new WF menu * 💄 Updating disabled states in new WF menu * 💄 Initial stab at new sidebar styling * ✨ Finished main navigation restyling * ✨ Updating `zoomToFit` and centering logic * ✨ Adding updates menu item to settings sidebar * 💄 Adding updates item to the settings sidebar and final touches on main sidebar style * 💄 Removing old code & refactoring * 💄 Minor CSS tweaks * 💄 Opening credentials modal on sidebar menu item click. Minor CSS updates * 💄 Updating sidebar expand/collapse animation * 💄 Few more refinements of sidebar animation * 👌 Addressing code review comments * ✨ Moved ActionDropdown component to design system * 👌 Fixing bugs reported during code review and testing * 👌 Addressing design review comments for the new sidebar * ✔️ Updating `N8nActionDropdown` component tests * ✨ Remembering scroll position when going back to templates list * ✨ Updating zoomToFit logic to account for footer content * 👌 Addressing latest sidebar review comments * 👌 Addressing main sidebar product review comments * 💄 Updating css variable names after vite merge * ✔️ Fixing linting errors in the design system * ✔️ Fixing `element-ui` type import * 👌 Addressing the code review comments. * ✨ Adding link to new credentials view, removed old modal * 💄 Updating credentials view responsiveness and route highlight handling * 💄 Adding highlight to workflows submenu when on new workflow page * 💄 Updated active submenu text color
502 lines
10 KiB
TypeScript
502 lines
10 KiB
TypeScript
import Vue from 'vue';
|
|
|
|
import ChangePasswordView from './views/ChangePasswordView.vue';
|
|
import ErrorView from './views/ErrorView.vue';
|
|
import ForgotMyPasswordView from './views/ForgotMyPasswordView.vue';
|
|
import MainHeader from '@/components/MainHeader/MainHeader.vue';
|
|
import MainSidebar from '@/components/MainSidebar.vue';
|
|
import NodeView from '@/views/NodeView.vue';
|
|
import SettingsPersonalView from './views/SettingsPersonalView.vue';
|
|
import SettingsUsersView from './views/SettingsUsersView.vue';
|
|
import SettingsCommunityNodesView from './views/SettingsCommunityNodesView.vue';
|
|
import SettingsApiView from './views/SettingsApiView.vue';
|
|
import SettingsFakeDoorView from './views/SettingsFakeDoorView.vue';
|
|
import SetupView from './views/SetupView.vue';
|
|
import SigninView from './views/SigninView.vue';
|
|
import SignupView from './views/SignupView.vue';
|
|
import Router, { Route } from 'vue-router';
|
|
|
|
import TemplatesCollectionView from '@/views/TemplatesCollectionView.vue';
|
|
import TemplatesWorkflowView from '@/views/TemplatesWorkflowView.vue';
|
|
import TemplatesSearchView from '@/views/TemplatesSearchView.vue';
|
|
import CredentialsView from '@/views/CredentialsView.vue';
|
|
import { Store } from 'vuex';
|
|
import { IPermissions, IRootState, IWorkflowsState } from './Interface';
|
|
import { LOGIN_STATUS, ROLE } from './modules/userHelpers';
|
|
import { RouteConfigSingleView } from 'vue-router/types/router';
|
|
import { VIEWS } from './constants';
|
|
import { store } from './store';
|
|
import e from 'express';
|
|
|
|
Vue.use(Router);
|
|
|
|
interface IRouteConfig extends RouteConfigSingleView {
|
|
meta: {
|
|
nodeView?: boolean;
|
|
templatesEnabled?: boolean;
|
|
getRedirect?: (store: Store<IRootState>) => {name: string} | false;
|
|
permissions: IPermissions;
|
|
telemetry?: {
|
|
disabled?: true;
|
|
getProperties: (route: Route, store: Store<IRootState>) => object;
|
|
};
|
|
scrollOffset?: number;
|
|
};
|
|
}
|
|
|
|
function getTemplatesRedirect(store: Store<IRootState>) {
|
|
const isTemplatesEnabled: boolean = store.getters['settings/isTemplatesEnabled'];
|
|
if (!isTemplatesEnabled) {
|
|
return {name: VIEWS.NOT_FOUND};
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
const router = new Router({
|
|
mode: 'history',
|
|
// @ts-ignore
|
|
base: window.BASE_PATH === '/%BASE_PATH%/' ? '/' : window.BASE_PATH,
|
|
scrollBehavior(to, from, savedPosition) {
|
|
// saved position == null means the page is NOT visited from history (back button)
|
|
if (savedPosition === null && to.name === VIEWS.TEMPLATES && to.meta) {
|
|
// for templates view, reset scroll position in this case
|
|
to.meta.setScrollPosition(0);
|
|
}
|
|
},
|
|
routes: [
|
|
{
|
|
path: '/',
|
|
name: VIEWS.HOMEPAGE,
|
|
meta: {
|
|
getRedirect(store: Store<IRootState>) {
|
|
return { name: VIEWS.NEW_WORKFLOW };
|
|
},
|
|
permissions: {
|
|
allow: {
|
|
loginStatus: [LOGIN_STATUS.LoggedIn],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: '/collections/:id',
|
|
name: VIEWS.COLLECTION,
|
|
components: {
|
|
default: TemplatesCollectionView,
|
|
sidebar: MainSidebar,
|
|
},
|
|
meta: {
|
|
templatesEnabled: true,
|
|
telemetry: {
|
|
getProperties(route: Route, store: Store<IRootState>) {
|
|
return {
|
|
collection_id: route.params.id,
|
|
wf_template_repo_session_id: store.getters['templates/currentSessionId'],
|
|
};
|
|
},
|
|
},
|
|
getRedirect: getTemplatesRedirect,
|
|
permissions: {
|
|
allow: {
|
|
loginStatus: [LOGIN_STATUS.LoggedIn],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: '/execution/:id',
|
|
name: VIEWS.EXECUTION,
|
|
components: {
|
|
default: NodeView,
|
|
header: MainHeader,
|
|
sidebar: MainSidebar,
|
|
},
|
|
meta: {
|
|
nodeView: true,
|
|
permissions: {
|
|
allow: {
|
|
loginStatus: [LOGIN_STATUS.LoggedIn],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: '/templates/:id',
|
|
name: VIEWS.TEMPLATE,
|
|
components: {
|
|
default: TemplatesWorkflowView,
|
|
sidebar: MainSidebar,
|
|
},
|
|
meta: {
|
|
templatesEnabled: true,
|
|
getRedirect: getTemplatesRedirect,
|
|
telemetry: {
|
|
getProperties(route: Route, store: Store<IRootState>) {
|
|
return {
|
|
template_id: route.params.id,
|
|
wf_template_repo_session_id: store.getters['templates/currentSessionId'],
|
|
};
|
|
},
|
|
},
|
|
permissions: {
|
|
allow: {
|
|
loginStatus: [LOGIN_STATUS.LoggedIn],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: '/templates/',
|
|
name: VIEWS.TEMPLATES,
|
|
components: {
|
|
default: TemplatesSearchView,
|
|
sidebar: MainSidebar,
|
|
},
|
|
meta: {
|
|
templatesEnabled: true,
|
|
getRedirect: getTemplatesRedirect,
|
|
// Templates view remembers it's scroll position on back
|
|
scrollOffset: 0,
|
|
telemetry: {
|
|
getProperties(route: Route, store: Store<IRootState>) {
|
|
return {
|
|
wf_template_repo_session_id: store.getters['templates/currentSessionId'],
|
|
};
|
|
},
|
|
},
|
|
setScrollPosition(pos: number) {
|
|
this.scrollOffset = pos;
|
|
},
|
|
permissions: {
|
|
allow: {
|
|
loginStatus: [LOGIN_STATUS.LoggedIn],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: '/credentials',
|
|
name: VIEWS.CREDENTIALS,
|
|
components: {
|
|
default: CredentialsView,
|
|
sidebar: MainSidebar,
|
|
},
|
|
meta: {
|
|
permissions: {
|
|
allow: {
|
|
loginStatus: [LOGIN_STATUS.LoggedIn],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: '/workflow',
|
|
name: VIEWS.NEW_WORKFLOW,
|
|
components: {
|
|
default: NodeView,
|
|
header: MainHeader,
|
|
sidebar: MainSidebar,
|
|
},
|
|
meta: {
|
|
nodeView: true,
|
|
permissions: {
|
|
allow: {
|
|
loginStatus: [LOGIN_STATUS.LoggedIn],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: '/workflow/:name',
|
|
name: VIEWS.WORKFLOW,
|
|
components: {
|
|
default: NodeView,
|
|
header: MainHeader,
|
|
sidebar: MainSidebar,
|
|
},
|
|
meta: {
|
|
nodeView: true,
|
|
permissions: {
|
|
allow: {
|
|
loginStatus: [LOGIN_STATUS.LoggedIn],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: '/workflows/demo',
|
|
name: VIEWS.DEMO,
|
|
components: {
|
|
default: NodeView,
|
|
},
|
|
meta: {
|
|
permissions: {
|
|
allow: {
|
|
loginStatus: [LOGIN_STATUS.LoggedIn],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: '/workflows/templates/:id',
|
|
name: VIEWS.TEMPLATE_IMPORT,
|
|
components: {
|
|
default: NodeView,
|
|
header: MainHeader,
|
|
sidebar: MainSidebar,
|
|
},
|
|
meta: {
|
|
templatesEnabled: true,
|
|
getRedirect: getTemplatesRedirect,
|
|
permissions: {
|
|
allow: {
|
|
loginStatus: [LOGIN_STATUS.LoggedIn],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: '/signin',
|
|
name: VIEWS.SIGNIN,
|
|
components: {
|
|
default: SigninView,
|
|
},
|
|
meta: {
|
|
telemetry: {
|
|
pageCategory: 'auth',
|
|
},
|
|
permissions: {
|
|
allow: {
|
|
loginStatus: [LOGIN_STATUS.LoggedOut],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: '/signup',
|
|
name: VIEWS.SIGNUP,
|
|
components: {
|
|
default: SignupView,
|
|
},
|
|
meta: {
|
|
telemetry: {
|
|
pageCategory: 'auth',
|
|
},
|
|
permissions: {
|
|
allow: {
|
|
loginStatus: [LOGIN_STATUS.LoggedOut],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: '/setup',
|
|
name: VIEWS.SETUP,
|
|
components: {
|
|
default: SetupView,
|
|
},
|
|
meta: {
|
|
telemetry: {
|
|
pageCategory: 'auth',
|
|
},
|
|
permissions: {
|
|
allow: {
|
|
role: [ROLE.Default],
|
|
},
|
|
deny: {
|
|
shouldDeny: () => {
|
|
return store.getters['settings/isUserManagementEnabled'] === false;
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: '/forgot-password',
|
|
name: VIEWS.FORGOT_PASSWORD,
|
|
components: {
|
|
default: ForgotMyPasswordView,
|
|
},
|
|
meta: {
|
|
telemetry: {
|
|
pageCategory: 'auth',
|
|
},
|
|
permissions: {
|
|
allow: {
|
|
loginStatus: [LOGIN_STATUS.LoggedOut],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: '/change-password',
|
|
name: VIEWS.CHANGE_PASSWORD,
|
|
components: {
|
|
default: ChangePasswordView,
|
|
},
|
|
meta: {
|
|
telemetry: {
|
|
pageCategory: 'auth',
|
|
},
|
|
permissions: {
|
|
allow: {
|
|
loginStatus: [LOGIN_STATUS.LoggedOut],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: '/settings',
|
|
redirect: '/settings/personal',
|
|
},
|
|
{
|
|
path: '/settings/users',
|
|
name: VIEWS.USERS_SETTINGS,
|
|
components: {
|
|
default: SettingsUsersView,
|
|
},
|
|
meta: {
|
|
telemetry: {
|
|
pageCategory: 'settings',
|
|
getProperties(route: Route, store: Store<IRootState>) {
|
|
return {
|
|
feature: 'users',
|
|
};
|
|
},
|
|
},
|
|
permissions: {
|
|
allow: {
|
|
role: [ROLE.Default, ROLE.Owner],
|
|
},
|
|
deny: {
|
|
shouldDeny: () => {
|
|
return store.getters['settings/isUserManagementEnabled'] === false;
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: '/settings/personal',
|
|
name: VIEWS.PERSONAL_SETTINGS,
|
|
components: {
|
|
default: SettingsPersonalView,
|
|
},
|
|
meta: {
|
|
telemetry: {
|
|
pageCategory: 'settings',
|
|
getProperties(route: Route, store: Store<IRootState>) {
|
|
return {
|
|
feature: 'personal',
|
|
};
|
|
},
|
|
},
|
|
permissions: {
|
|
allow: {
|
|
loginStatus: [LOGIN_STATUS.LoggedIn],
|
|
},
|
|
deny: {
|
|
role: [ROLE.Default],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: '/settings/api',
|
|
name: VIEWS.API_SETTINGS,
|
|
components: {
|
|
default: SettingsApiView,
|
|
},
|
|
meta: {
|
|
telemetry: {
|
|
pageCategory: 'settings',
|
|
getProperties(route: Route, store: Store<IRootState>) {
|
|
return {
|
|
feature: 'api',
|
|
};
|
|
},
|
|
},
|
|
permissions: {
|
|
allow: {
|
|
loginStatus: [LOGIN_STATUS.LoggedIn],
|
|
},
|
|
deny: {
|
|
shouldDeny: () => {
|
|
return store.getters['settings/isPublicApiEnabled'] === false;
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: '/settings/community-nodes',
|
|
name: VIEWS.COMMUNITY_NODES,
|
|
components: {
|
|
default: SettingsCommunityNodesView,
|
|
},
|
|
meta: {
|
|
telemetry: {
|
|
pageCategory: 'settings',
|
|
},
|
|
permissions: {
|
|
allow: {
|
|
role: [ROLE.Default, ROLE.Owner],
|
|
},
|
|
deny: {
|
|
shouldDeny: () => {
|
|
return store.getters['settings/isCommunityNodesFeatureEnabled'] === false;
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: '/settings/coming-soon/:featureId',
|
|
name: VIEWS.FAKE_DOOR,
|
|
component: SettingsFakeDoorView,
|
|
props: true,
|
|
meta: {
|
|
telemetry: {
|
|
pageCategory: 'settings',
|
|
getProperties(route: Route, store: Store<IRootState>) {
|
|
return {
|
|
feature: route.params['featureId'],
|
|
};
|
|
},
|
|
},
|
|
permissions: {
|
|
allow: {
|
|
loginStatus: [LOGIN_STATUS.LoggedIn],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: '*',
|
|
name: VIEWS.NOT_FOUND,
|
|
component: ErrorView,
|
|
props: {
|
|
messageKey: 'error.pageNotFound',
|
|
errorCode: 404,
|
|
redirectTextKey: 'error.goBack',
|
|
redirectPage: VIEWS.HOMEPAGE,
|
|
},
|
|
meta: {
|
|
nodeView: true,
|
|
telemetry: {
|
|
disabled: true,
|
|
},
|
|
permissions: {
|
|
allow: {
|
|
// TODO: Once custom permissions are merged, this needs to be updated with index validation
|
|
loginStatus: [LOGIN_STATUS.LoggedIn, LOGIN_STATUS.LoggedOut],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
] as IRouteConfig[],
|
|
});
|
|
|
|
export default router;
|