diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/document-tag-entry/document-tag-entry.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/document-tag-entry/document-tag-entry.tsx index 75539a134..4cad6ba7a 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/document-tag-entry/document-tag-entry.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/document-tag-entry/document-tag-entry.tsx @@ -1,18 +1,9 @@ 'use client' -import { useMemo, useRef, useState } from 'react' +import { useMemo, useRef } from 'react' import { Plus } from 'lucide-react' -import { - Button, - Input, - Label, - Popover, - PopoverAnchor, - PopoverContent, - PopoverItem, - PopoverScrollArea, - Trash, -} from '@/components/emcn' +import { Button, Combobox, type ComboboxOption, Label, Trash } from '@/components/emcn' +import { Input } from '@/components/ui/input' import { FIELD_TYPE_LABELS, getPlaceholderForFieldType } from '@/lib/knowledge/constants' import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/formatted-text' import { TagDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown' @@ -74,9 +65,6 @@ export function DocumentTagEntry({ const emitTagSelection = useTagSelection(blockId, subBlock.id) - // State for tag name dropdown visibility - one for each row - const [dropdownStates, setDropdownStates] = useState>({}) - // Use preview value when in preview mode, otherwise use store value const currentValue = isPreview ? previewValue : storeValue @@ -194,7 +182,13 @@ export function DocumentTagEntry({ } const handleDeleteRow = (rowIndex: number) => { - if (isPreview || disabled || rows.length <= 1) return + if (isPreview || disabled) return + + if (rows.length <= 1) { + // Clear the single row instead of deleting + setStoreValue('') + return + } const updatedRows = rows.filter((_, idx) => idx !== rowIndex) const tableDataForStorage = updatedRows.map((row) => ({ @@ -227,88 +221,55 @@ export function DocumentTagEntry({ if (tagDefinitions.length === 0) { return ( -
- No tags defined for this knowledge base. -
- Define tags at the knowledge base level first. +
+
+

+ No tags defined for this knowledge base +

+

+ Define tags at the knowledge base level first +

+
) } const renderHeader = () => ( - - - Tag - Value - + + + + Tag + + + Value + ) const renderTagNameCell = (row: DocumentTagRow, rowIndex: number) => { const cellValue = row.cells.tagName || '' - const isOpen = dropdownStates[rowIndex] || false - - const setIsOpen = (open: boolean) => { - setDropdownStates((prev) => ({ ...prev, [rowIndex]: open })) - } // Show tags that are either available OR currently selected for this row const selectableTags = tagDefinitions.filter( (def) => def.displayName === cellValue || !usedTagNames.has(def.displayName.toLowerCase()) ) + const tagOptions: ComboboxOption[] = selectableTags.map((tag) => ({ + value: tag.displayName, + label: `${tag.displayName} (${FIELD_TYPE_LABELS[tag.fieldType] || 'Text'})`, + })) + return ( - - - -
!disabled && setIsOpen(true)} - > - -
- - {cellValue || Select tag} - -
-
-
- {selectableTags.length > 0 && ( - - - {selectableTags.map((tagDef) => ( - { - handleTagSelection(rowIndex, tagDef.displayName) - setIsOpen(false) - }} - > - {tagDef.displayName} - - {FIELD_TYPE_LABELS[tagDef.fieldType] || 'Text'} - - - ))} - - - )} -
+ + handleTagSelection(rowIndex, value)} + disabled={disabled || isLoading} + placeholder='Select tag' + className='!border-0 !bg-transparent hover:!bg-transparent px-[10px] py-[8px] font-medium text-sm leading-[21px] focus-visible:ring-0 focus-visible:ring-offset-0 [&>span]:truncate' + /> ) } @@ -333,7 +294,7 @@ export function DocumentTagEntry({ ) return ( - +
{ @@ -347,10 +308,10 @@ export function DocumentTagEntry({ disabled={disabled || !isTagSelected} autoComplete='off' placeholder={isTagSelected ? placeholder : 'Select a tag first'} - className='w-full border-0 text-transparent caret-foreground placeholder:text-muted-foreground/50 focus-visible:ring-0 focus-visible:ring-offset-0' + className='w-full border-0 bg-transparent px-[10px] py-[8px] font-medium text-sm text-transparent leading-[21px] caret-[var(--text-primary)] placeholder:text-[var(--text-muted)] focus-visible:ring-0 focus-visible:ring-offset-0' /> -
-
+
+
{formatDisplayText(cellValue, { accessiblePrefixes, highlightAll: !accessiblePrefixes, @@ -379,29 +340,32 @@ export function DocumentTagEntry({ } const renderDeleteButton = (rowIndex: number) => { - const canDelete = !isPreview && !disabled + if (isPreview || disabled) return null - return canDelete ? ( - + return ( + - ) : null + ) } return ( -
-
- +
+
+
{renderHeader()} - + {rows.map((row, rowIndex) => ( - + {renderTagNameCell(row, rowIndex)} {renderValueCell(row, rowIndex)} {renderDeleteButton(rowIndex)} @@ -411,15 +375,10 @@ export function DocumentTagEntry({
- {/* Add Row Button */} + {/* Add Tag Button */} {!isPreview && !disabled && (
- diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/knowledge-tag-filters/knowledge-tag-filters.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/knowledge-tag-filters/knowledge-tag-filters.tsx index 0c0cbf087..38bfef146 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/knowledge-tag-filters/knowledge-tag-filters.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/knowledge-tag-filters/knowledge-tag-filters.tsx @@ -2,19 +2,8 @@ import { useState } from 'react' import { Plus } from 'lucide-react' -import { - Button, - Combobox, - type ComboboxOption, - Input, - Label, - Popover, - PopoverAnchor, - PopoverContent, - PopoverItem, - PopoverScrollArea, - Trash, -} from '@/components/emcn' +import { Button, Combobox, type ComboboxOption, Label, Trash } from '@/components/emcn' +import { Input } from '@/components/ui/input' import { FIELD_TYPE_LABELS, getPlaceholderForFieldType } from '@/lib/knowledge/constants' import { type FilterFieldType, getOperatorsForFieldType } from '@/lib/knowledge/filters/types' import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/formatted-text' @@ -66,21 +55,15 @@ export function KnowledgeTagFilters({ previewValue, }: KnowledgeTagFiltersProps) { const [storeValue, setStoreValue] = useSubBlockValue(blockId, subBlock.id) - - // Hook for immediate tag/dropdown selections const emitTagSelection = useTagSelection(blockId, subBlock.id) - // Get the knowledge base ID from other sub-blocks const [knowledgeBaseIdValue] = useSubBlockValue(blockId, 'knowledgeBaseId') const knowledgeBaseId = knowledgeBaseIdValue || null - // Use KB tag definitions hook to get available tags const { tagDefinitions, isLoading } = useKnowledgeBaseTagDefinitions(knowledgeBaseId) - // Get accessible prefixes for variable highlighting const accessiblePrefixes = useAccessibleReferencePrefixes(blockId) - // State for managing tag dropdown const [activeTagDropdown, setActiveTagDropdown] = useState<{ rowIndex: number showTags: boolean @@ -89,15 +72,10 @@ export function KnowledgeTagFilters({ element?: HTMLElement | null } | null>(null) - // State for dropdown visibility - one for each row - const [dropdownStates, setDropdownStates] = useState>({}) - - // Parse the current value to extract filters const parseFilters = (filterValue: string | null): TagFilter[] => { if (!filterValue) return [] try { const parsed = JSON.parse(filterValue) - // Handle legacy format (without fieldType/operator) return parsed.map((f: TagFilter) => ({ ...f, fieldType: f.fieldType || 'text', @@ -111,7 +89,6 @@ export function KnowledgeTagFilters({ const currentValue = isPreview ? previewValue : storeValue const filters = parseFilters(currentValue || null) - // Transform filters to table format for display const rows: TagFilterRow[] = filters.length > 0 ? filters.map((filter) => ({ @@ -128,7 +105,7 @@ export function KnowledgeTagFilters({ : [ { id: 'empty-row-0', - cells: { tagName: '', fieldType: 'text', operator: 'eq', value: '' }, + cells: { tagName: '', fieldType: 'text', operator: '', value: '' }, }, ] @@ -138,17 +115,18 @@ export function KnowledgeTagFilters({ setStoreValue(value) } - /** Convert rows back to TagFilter format */ const rowsToFilters = (rowsToConvert: TagFilterRow[]): TagFilter[] => { - return rowsToConvert.map((row) => ({ - id: row.id, - tagName: row.cells.tagName || '', - tagSlot: row.cells.tagSlot, - fieldType: row.cells.fieldType || 'text', - operator: row.cells.operator || 'eq', - tagValue: row.cells.value || '', - valueTo: row.cells.valueTo, - })) + return rowsToConvert + .filter((row) => row.cells.tagName?.trim()) + .map((row) => ({ + id: row.id, + tagName: row.cells.tagName || '', + tagSlot: row.cells.tagSlot, + fieldType: row.cells.fieldType || 'text', + operator: row.cells.operator || 'eq', + tagValue: row.cells.value || '', + valueTo: row.cells.valueTo, + })) } const handleCellChange = (rowIndex: number, column: string, value: string | FilterFieldType) => { @@ -158,15 +136,13 @@ export function KnowledgeTagFilters({ if (idx === rowIndex) { const newCells = { ...row.cells, [column]: value } - // Reset operator when field type changes if (column === 'fieldType') { const operators = getOperatorsForFieldType(value as FilterFieldType) newCells.operator = operators[0]?.value || 'eq' - newCells.value = '' // Reset value when type changes + newCells.value = '' newCells.valueTo = undefined } - // Reset valueTo if operator is not 'between' if (column === 'operator' && value !== 'between') { newCells.valueTo = undefined } @@ -179,11 +155,9 @@ export function KnowledgeTagFilters({ updateFilters(rowsToFilters(updatedRows)) } - /** Handle tag name selection from dropdown */ const handleTagNameSelection = (rowIndex: number, tagName: string) => { if (isPreview || disabled) return - // Find the tag definition to get fieldType and tagSlot const tagDef = tagDefinitions.find((t) => t.displayName === tagName) const fieldType = (tagDef?.fieldType || 'text') as FilterFieldType const operators = getOperatorsForFieldType(fieldType) @@ -198,7 +172,7 @@ export function KnowledgeTagFilters({ tagSlot: tagDef?.tagSlot, fieldType, operator: operators[0]?.value || 'eq', - value: '', // Reset value when tag changes + value: '', valueTo: undefined, }, } @@ -242,7 +216,14 @@ export function KnowledgeTagFilters({ } const handleDeleteRow = (rowIndex: number) => { - if (isPreview || disabled || rows.length <= 1) return + if (isPreview || disabled) return + + if (rows.length <= 1) { + // Clear the single row instead of deleting + setStoreValue(null) + return + } + const updatedRows = rows.filter((_, idx) => idx !== rowIndex) updateFilters(rowsToFilters(updatedRows)) } @@ -261,80 +242,48 @@ export function KnowledgeTagFilters({ } const renderHeader = () => ( - - - Tag - Operator - Value - + + + + Tag + + + Operator + + + Value + ) const renderTagNameCell = (row: TagFilterRow, rowIndex: number) => { const cellValue = row.cells.tagName || '' - const isOpen = dropdownStates[rowIndex] || false - const setIsOpen = (open: boolean) => { - setDropdownStates((prev) => ({ ...prev, [rowIndex]: open })) - } + const tagOptions: ComboboxOption[] = tagDefinitions.map((tag) => ({ + value: tag.displayName, + label: `${tag.displayName} (${FIELD_TYPE_LABELS[tag.fieldType] || 'Text'})`, + })) return ( - - - -
!disabled && !isLoading && setIsOpen(true)} - > - -
- {cellValue || 'Select tag'} -
-
-
- {tagDefinitions.length > 0 && ( - - - {tagDefinitions.map((tag) => ( - { - handleTagNameSelection(rowIndex, tag.displayName) - setIsOpen(false) - }} - > - {tag.displayName} - - {FIELD_TYPE_LABELS[tag.fieldType] || 'Text'} - - - ))} - - - )} -
+ + handleTagNameSelection(rowIndex, value)} + disabled={disabled || isLoading} + placeholder='Select tag' + className='!border-0 !bg-transparent hover:!bg-transparent px-[10px] py-[8px] font-medium text-sm leading-[21px] focus-visible:ring-0 focus-visible:ring-offset-0 [&>span]:truncate' + /> ) } - /** Render operator cell */ const renderOperatorCell = (row: TagFilterRow, rowIndex: number) => { const fieldType = row.cells.fieldType || 'text' - const operator = row.cells.operator || 'eq' + const operator = row.cells.operator || '' const operators = getOperatorsForFieldType(fieldType) + const isOperatorDisabled = disabled || !row.cells.tagName const operatorOptions: ComboboxOption[] = operators.map((op) => ({ value: op.value, @@ -342,14 +291,14 @@ export function KnowledgeTagFilters({ })) return ( - + handleCellChange(rowIndex, 'operator', value)} - disabled={disabled || !row.cells.tagName} - placeholder='Operator' - size='sm' + disabled={isOperatorDisabled} + placeholder='Select operator' + className='!border-0 !bg-transparent hover:!bg-transparent px-[10px] py-[8px] font-medium text-sm leading-[21px] focus-visible:ring-0 focus-visible:ring-offset-0 [&>span]:truncate' /> ) @@ -364,7 +313,6 @@ export function KnowledgeTagFilters({ const isDisabled = disabled || !row.cells.tagName const placeholder = getPlaceholderForFieldType(fieldType) - // Single text input for all field types with variable support const renderInput = (value: string, column: 'value' | 'valueTo') => (
-
-
+
+
{formatDisplayText(value || '', { accessiblePrefixes, highlightAll: !accessiblePrefixes, @@ -425,11 +372,10 @@ export function KnowledgeTagFilters({
) - // Render with optional "between" second input if (isBetween) { return ( - -
+ +
{renderInput(cellValue, 'value')} to {renderInput(valueTo, 'valueTo')} @@ -438,23 +384,27 @@ export function KnowledgeTagFilters({ ) } - return {renderInput(cellValue, 'value')} + return ( + + {renderInput(cellValue, 'value')} + + ) } const renderDeleteButton = (rowIndex: number) => { - const canDelete = !isPreview && !disabled + if (isPreview || disabled) return null - return canDelete ? ( - + return ( + - ) : null + ) } if (isLoading) { @@ -462,13 +412,16 @@ export function KnowledgeTagFilters({ } return ( -
-
- +
+
+
{renderHeader()} - + {rows.map((row, rowIndex) => ( - + {renderTagNameCell(row, rowIndex)} {renderOperatorCell(row, rowIndex)} {renderValueCell(row, rowIndex)} @@ -502,7 +455,7 @@ export function KnowledgeTagFilters({ {/* Add Filter Button */} {!isPreview && !disabled && (
- diff --git a/apps/sim/blocks/blocks/knowledge.ts b/apps/sim/blocks/blocks/knowledge.ts index 92ad316d5..01a896c84 100644 --- a/apps/sim/blocks/blocks/knowledge.ts +++ b/apps/sim/blocks/blocks/knowledge.ts @@ -57,7 +57,6 @@ export const KnowledgeBlock: BlockConfig = { type: 'knowledge-tag-filters', placeholder: 'Add tag filters', condition: { field: 'operation', value: 'search' }, - mode: 'advanced', }, { id: 'documentId', diff --git a/apps/sim/components/emcn/components/date-picker/date-picker.tsx b/apps/sim/components/emcn/components/date-picker/date-picker.tsx index 3827a4a3e..c7acb6a7a 100644 --- a/apps/sim/components/emcn/components/date-picker/date-picker.tsx +++ b/apps/sim/components/emcn/components/date-picker/date-picker.tsx @@ -18,8 +18,13 @@ import * as React from 'react' import { cva, type VariantProps } from 'class-variance-authority' import { ChevronDown, ChevronLeft, ChevronRight } from 'lucide-react' +import { Button } from '@/components/emcn/components/button/button' +import { + Popover, + PopoverAnchor, + PopoverContent, +} from '@/components/emcn/components/popover/popover' import { cn } from '@/lib/core/utils/cn' -import { Popover, PopoverAnchor, PopoverContent } from '../popover/popover' /** * Variant styles for the date picker trigger button. @@ -388,13 +393,9 @@ const DatePicker = React.forwardRef( {/* Today Button */}
- +