refactor: centralized filter validation and generalized 'empty selection' checks

This commit is contained in:
Björn Steinhagen
2026-03-02 15:34:36 +02:00
parent f47f19c02d
commit 6fc3df4a0d
3 changed files with 66 additions and 39 deletions
+2 -1
View File
@@ -217,7 +217,8 @@ const updateFilter = (filter: ISendFilter) => {
}
const isSaveDisabled = computed(() => {
return !store.validateSendFilter(newFilter.value || props.modelCard.sendFilter).valid
const filterToCheck = newFilter.value || props.modelCard.sendFilter
return !store.validateSendFilter(filterToCheck).valid
})
const saveFilter = async () => {
+57
View File
@@ -1,6 +1,24 @@
import { computed } from 'vue'
import type {
ISendFilter,
SendFilterSelect,
RevitCategoriesSendFilter,
RevitViewsSendFilter
} from '~/lib/models/card/send'
import { ValidationHelpers } from '@speckle/ui-components'
import type { GenericValidateFunction } from 'vee-validate'
export const isSelectFilter = (f: ISendFilter): f is SendFilterSelect =>
f.type === 'Select' || 'selectedItems' in f
export const isRevitCategoriesFilter = (
f: ISendFilter
): f is RevitCategoriesSendFilter =>
f.id === 'revitCategories' || f.id === 'archicadLayers'
export const isRevitViewsFilter = (f: ISendFilter): f is RevitViewsSendFilter =>
f.id === 'revitViews'
export const isEmail = ValidationHelpers.isEmail
export const isOneOrMultipleEmails = ValidationHelpers.isOneOrMultipleEmails
@@ -42,3 +60,42 @@ export function useModelNameValidationRules() {
isValidModelName
])
}
export type FilterValidationResult = { valid: boolean; reason?: string }
export function validateFilter(
filter: ISendFilter | undefined,
context: { selectionCount: number }
): FilterValidationResult {
if (!filter) return { valid: false, reason: 'No filter selected' }
// Selection Filter check
if (filter.name === 'Selection' || filter.id === 'selection') {
return context.selectionCount > 0
? { valid: true }
: { valid: false, reason: 'No objects selected to publish' }
}
// List-based filters (Rhino Layers, etc.)
if (isSelectFilter(filter)) {
return (filter.selectedItems?.length ?? 0) > 0
? { valid: true }
: { valid: false, reason: 'No items selected to publish' }
}
// Category-based filters
if (isRevitCategoriesFilter(filter)) {
return (filter.selectedCategories?.length ?? 0) > 0
? { valid: true }
: { valid: false, reason: 'No categories selected to publish' }
}
// View-based filters
if (isRevitViewsFilter(filter)) {
return filter.selectedView?.trim()
? { valid: true }
: { valid: false, reason: 'No view selected to publish' }
}
return { valid: true }
}
+7 -38
View File
@@ -11,9 +11,10 @@ import type {
ISendFilterSelectItem,
ISenderModelCard,
RevitViewsSendFilter,
RevitCategoriesSendFilter,
SendFilterSelect
} from '~/lib/models/card/send'
import { useSelectionStore } from '~/store/selection'
import { validateFilter } from '~/lib/validation'
import type { ToastNotification } from '@speckle/ui-components'
import { ToastNotificationType } from '@speckle/ui-components'
import type { Nullable } from '@speckle/shared'
@@ -23,7 +24,6 @@ import { defineStore } from 'pinia'
import type { CardSetting } from '~/lib/models/card/setting'
import type { DUIAccount } from '~/store/accounts'
import { useAccountStore } from '~/store/accounts'
import { useSelectionStore } from '~/store/selection'
import {
useUpdateConnector,
type Version
@@ -370,43 +370,12 @@ export const useHostAppStore = defineStore('hostAppStore', () => {
*/
app.$sendBinding?.on('refreshSendFilters', () => void refreshSendFilters())
const validateSendFilter = (
filter?: ISendFilter
): { valid: boolean; reason?: string } => {
if (!filter) return { valid: false, reason: 'No filter selected' }
const validateSendFilter = (filter?: ISendFilter) => {
const selectionStore = useSelectionStore()
if (filter.name === 'Selection' || filter.id === 'selection') {
const selectionStore = useSelectionStore()
if (
!selectionStore.selectionInfo.selectedObjectIds ||
selectionStore.selectionInfo.selectedObjectIds.length === 0
) {
return { valid: false, reason: 'No objects selected to publish' }
}
}
if (filter.type === 'Select' || filter.id === 'navisworksSavedSets') {
const f = filter as SendFilterSelect
if (!f.selectedItems || f.selectedItems.length === 0) {
return { valid: false, reason: 'No items selected to publish' }
}
}
if (filter.id === 'revitCategories' || filter.id === 'archicadLayers') {
const f = filter as RevitCategoriesSendFilter
if (!f.selectedCategories || f.selectedCategories.length === 0) {
return { valid: false, reason: 'No categories selected to publish' }
}
}
if (filter.id === 'revitViews') {
const f = filter as RevitViewsSendFilter
if (!f.selectedView || f.selectedView.trim() === '') {
return { valid: false, reason: 'No view selected to publish' }
}
}
return { valid: true }
return validateFilter(filter, {
selectionCount: selectionStore.selectionInfo.selectedObjectIds?.length ?? 0
})
}
/**