diff --git a/packages/frontend/editor-ui/src/app/components/WorkflowCard.vue b/packages/frontend/editor-ui/src/app/components/WorkflowCard.vue index 05bab772950..597a2434e0d 100644 --- a/packages/frontend/editor-ui/src/app/components/WorkflowCard.vue +++ b/packages/frontend/editor-ui/src/app/components/WorkflowCard.vue @@ -32,6 +32,7 @@ import { } from '@/features/collaboration/projects/projects.types'; import type { PathItem } from '@n8n/design-system/components/N8nBreadcrumbs/Breadcrumbs.vue'; import { useFoldersStore } from '@/features/core/folders/folders.store'; +import { useFavoritesStore } from '@/app/stores/favorites.store'; import { N8nActionToggle, @@ -61,6 +62,7 @@ const WORKFLOW_LIST_ITEM_ACTIONS = { ENABLE_MCP_ACCESS: 'enableMCPAccess', REMOVE_MCP_ACCESS: 'removeMCPAccess', UNPUBLISH: 'unpublish', + TOGGLE_FAVORITE: 'toggleFavorite', }; const props = withDefaults( @@ -118,6 +120,7 @@ const workflowsListStore = useWorkflowsListStore(); const projectsStore = useProjectsStore(); const foldersStore = useFoldersStore(); const mcpStore = useMCPStore(); +const favoritesStore = useFavoritesStore(); const workflowActivate = useWorkflowActivate(); const hiddenBreadcrumbsItemsAsync = ref>(new Promise(() => {})); const cachedHiddenBreadcrumbsItems = ref([]); @@ -188,6 +191,13 @@ const actions = computed(() => { }, ]; + items.push({ + label: favoritesStore.isFavorite(props.data.id) + ? locale.baseText('favorites.remove') + : locale.baseText('favorites.add'), + value: WORKFLOW_LIST_ITEM_ACTIONS.TOGGLE_FAVORITE, + }); + if ( workflowPermissions.value.read && canCreateWorkflow.value && @@ -392,6 +402,9 @@ async function onAction(action: string) { case WORKFLOW_LIST_ITEM_ACTIONS.UNPUBLISH: await unpublishWorkflow(); break; + case WORKFLOW_LIST_ITEM_ACTIONS.TOGGLE_FAVORITE: + await favoritesStore.toggleFavorite(props.data.id, 'workflow'); + break; } } diff --git a/packages/frontend/editor-ui/src/features/collaboration/projects/components/FavoritesSidebarCompact.vue b/packages/frontend/editor-ui/src/features/collaboration/projects/components/FavoritesSidebarCompact.vue new file mode 100644 index 00000000000..bad4dee2ed1 --- /dev/null +++ b/packages/frontend/editor-ui/src/features/collaboration/projects/components/FavoritesSidebarCompact.vue @@ -0,0 +1,183 @@ + + + + + diff --git a/packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectNavigation.vue b/packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectNavigation.vue index fb5050ddfdc..27b4cc8d1b4 100644 --- a/packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectNavigation.vue +++ b/packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectNavigation.vue @@ -4,7 +4,7 @@ import { VIEWS } from '@/app/constants'; import { sourceControlEventBus } from '@/features/integrations/sourceControl.ee/sourceControl.eventBus'; import { useUsersStore } from '@/features/settings/users/users.store'; import { useSettingsStore } from '@/app/stores/settings.store'; -import { type IconName, N8nIcon, N8nMenuItem, N8nText } from '@n8n/design-system'; +import { N8nMenuItem, N8nText } from '@n8n/design-system'; import type { IMenuItem } from '@n8n/design-system/types'; import { useI18n } from '@n8n/i18n'; import { computed, onBeforeMount, onBeforeUnmount } from 'vue'; @@ -13,6 +13,7 @@ import type { ProjectListItem } from '../projects.types'; import { CHAT_VIEW } from '@/features/ai/chatHub/constants'; import { useFavoritesStore } from '@/app/stores/favorites.store'; import { DATA_TABLE_DETAILS } from '@/features/core/dataTable/constants'; +import FavoritesSidebarCompact from './FavoritesSidebarCompact.vue'; import { hasPermission } from '@/app/utils/rbac/permissions'; @@ -127,7 +128,6 @@ const favoriteDataTableItems = computed(() => type FavoriteGroup = { type: string; - icon: IconName; items: IMenuItem[]; showIndividualIcons: boolean; }; @@ -138,7 +138,6 @@ const favoriteGroups = computed(() => { if (favoriteProjectItems.value.length > 0) { groups.push({ type: 'project', - icon: 'layers' as IconName, items: favoriteProjectItems.value, showIndividualIcons: true, }); @@ -146,7 +145,6 @@ const favoriteGroups = computed(() => { if (favoriteWorkflowItems.value.length > 0) { groups.push({ type: 'workflow', - icon: 'log-in' as IconName, items: favoriteWorkflowItems.value, showIndividualIcons: false, }); @@ -154,7 +152,6 @@ const favoriteGroups = computed(() => { if (favoriteDataTableItems.value.length > 0) { groups.push({ type: 'dataTable', - icon: 'table' as IconName, items: favoriteDataTableItems.value, showIndividualIcons: false, }); @@ -162,13 +159,7 @@ const favoriteGroups = computed(() => { return groups; }); -const allFavoriteItems = computed(() => [ - ...favoriteProjectItems.value, - ...favoriteWorkflowItems.value, - ...favoriteDataTableItems.value, -]); - -const hasFavorites = computed(() => allFavoriteItems.value.length > 0); +const hasFavorites = computed(() => favoritesStore.favorites.length > 0); const activeTabId = computed(() => { return ( @@ -249,47 +240,26 @@ onBeforeUnmount(() => { {{ locale.baseText('favorites.menu.title') }}
- + - +
@@ -389,27 +359,4 @@ onBeforeUnmount(() => { .groupSpacer { height: var(--spacing--4xs); } - -.favoriteGroup { - display: flex; - align-items: flex-start; -} - -.groupIcon { - // Same width as N8nMenuItem's icon slot; same height as one menu item row - // (icon height: --spacing--lg=24px + top/bottom padding: 2×--spacing--4xs=8px) - width: var(--spacing--lg); - height: calc(var(--spacing--lg) + 2 * var(--spacing--4xs)); - flex-shrink: 0; - margin-right: var(--spacing--4xs); - display: flex; - align-items: center; - justify-content: center; - color: var(--color--text--tint-1); -} - -.groupItems { - flex: 1; - min-width: 0; -}