Skip to content

Commit 407f9c7

Browse files
committed
added tables to permission groups, updated subblock types
1 parent 7b0e030 commit 407f9c7

File tree

18 files changed

+448
-246
lines changed

18 files changed

+448
-246
lines changed

apps/sim/app/api/permission-groups/[id]/route.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const configSchema = z.object({
1919
allowedModelProviders: z.array(z.string()).nullable().optional(),
2020
hideTraceSpans: z.boolean().optional(),
2121
hideKnowledgeBaseTab: z.boolean().optional(),
22+
hideTablesTab: z.boolean().optional(),
2223
hideCopilot: z.boolean().optional(),
2324
hideApiKeysTab: z.boolean().optional(),
2425
hideEnvironmentTab: z.boolean().optional(),

apps/sim/app/api/permission-groups/route.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const configSchema = z.object({
2020
allowedModelProviders: z.array(z.string()).nullable().optional(),
2121
hideTraceSpans: z.boolean().optional(),
2222
hideKnowledgeBaseTab: z.boolean().optional(),
23+
hideTablesTab: z.boolean().optional(),
2324
hideCopilot: z.boolean().optional(),
2425
hideApiKeysTab: z.boolean().optional(),
2526
hideEnvironmentTab: z.boolean().optional(),

apps/sim/app/workspace/[workspaceId]/tables/page.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { redirect } from 'next/navigation'
22
import { getSession } from '@/lib/auth'
33
import { verifyWorkspaceMembership } from '@/app/api/workflows/utils'
4+
import { getUserPermissionConfig } from '@/ee/access-control/utils/permission-check'
45
import { TablesView } from './components'
56

67
interface TablesPageProps {
@@ -22,5 +23,10 @@ export default async function TablesPage({ params }: TablesPageProps) {
2223
redirect('/')
2324
}
2425

26+
const permissionConfig = await getUserPermissionConfig(session.user.id)
27+
if (permissionConfig?.hideTablesTab) {
28+
redirect(`/workspace/${workspaceId}`)
29+
}
30+
2531
return <TablesView />
2632
}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/filter-builder/components/empty-state.tsx

Lines changed: 0 additions & 19 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
1-
import { X } from 'lucide-react'
2-
import { Button, Combobox, type ComboboxOption, Input } from '@/components/emcn'
1+
import { useRef } from 'react'
2+
import { Plus } from 'lucide-react'
3+
import {
4+
Badge,
5+
Button,
6+
Combobox,
7+
type ComboboxOption,
8+
Input,
9+
Label,
10+
Trash,
11+
} from '@/components/emcn'
312
import { cn } from '@/lib/core/utils/cn'
413
import type { FilterRule } from '@/lib/table/query-builder/constants'
514
import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/formatted-text'
6-
import { SubBlockInputController } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/sub-block-input-controller'
15+
import { TagDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown'
16+
import type { useSubBlockInput } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-input'
717
import { useAccessibleReferencePrefixes } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-accessible-reference-prefixes'
818

919
interface FilterRuleRowProps {
@@ -17,121 +27,196 @@ interface FilterRuleRowProps {
1727
isReadOnly: boolean
1828
isPreview: boolean
1929
disabled: boolean
30+
onAdd: () => void
2031
onRemove: (id: string) => void
2132
onUpdate: (id: string, field: keyof FilterRule, value: string) => void
33+
onToggleCollapse: (id: string) => void
34+
inputController: ReturnType<typeof useSubBlockInput>
2235
}
2336

2437
export function FilterRuleRow({
2538
blockId,
26-
subBlockId,
2739
rule,
2840
index,
2941
columns,
3042
comparisonOptions,
3143
logicalOptions,
3244
isReadOnly,
33-
isPreview,
34-
disabled,
45+
onAdd,
3546
onRemove,
3647
onUpdate,
48+
onToggleCollapse,
49+
inputController,
3750
}: FilterRuleRowProps) {
3851
const accessiblePrefixes = useAccessibleReferencePrefixes(blockId)
52+
const valueInputRef = useRef<HTMLInputElement>(null)
53+
const overlayRef = useRef<HTMLDivElement>(null)
3954

40-
return (
41-
<div className='flex items-center gap-[6px]'>
42-
<Button
43-
variant='ghost'
44-
size='sm'
45-
onClick={() => onRemove(rule.id)}
55+
const syncOverlayScroll = (scrollLeft: number) => {
56+
if (overlayRef.current) overlayRef.current.scrollLeft = scrollLeft
57+
}
58+
59+
const cellKey = `filter-${rule.id}-value`
60+
const fieldState = inputController.fieldHelpers.getFieldState(cellKey)
61+
const handlers = inputController.fieldHelpers.createFieldHandlers(
62+
cellKey,
63+
rule.value,
64+
(newValue) => onUpdate(rule.id, 'value', newValue)
65+
)
66+
const tagSelectHandler = inputController.fieldHelpers.createTagSelectHandler(
67+
cellKey,
68+
rule.value,
69+
(newValue) => onUpdate(rule.id, 'value', newValue)
70+
)
71+
72+
const getOperatorLabel = (value: string) => {
73+
const option = comparisonOptions.find((op) => op.value === value)
74+
return option?.label || value
75+
}
76+
77+
const getColumnLabel = (value: string) => {
78+
const option = columns.find((col) => col.value === value)
79+
return option?.label || value
80+
}
81+
82+
const renderHeader = () => (
83+
<div
84+
className='flex cursor-pointer items-center justify-between rounded-t-[4px] bg-[var(--surface-4)] px-[10px] py-[5px]'
85+
onClick={() => onToggleCollapse(rule.id)}
86+
>
87+
<div className='flex min-w-0 flex-1 items-center gap-[8px]'>
88+
<span className='block truncate font-medium text-[14px] text-[var(--text-tertiary)]'>
89+
{rule.collapsed && rule.column ? getColumnLabel(rule.column) : `Condition ${index + 1}`}
90+
</span>
91+
{rule.collapsed && rule.column && (
92+
<Badge variant='type' size='sm'>
93+
{getOperatorLabel(rule.operator)}
94+
</Badge>
95+
)}
96+
</div>
97+
<div className='flex items-center gap-[8px] pl-[8px]' onClick={(e) => e.stopPropagation()}>
98+
<Button variant='ghost' onClick={onAdd} disabled={isReadOnly} className='h-auto p-0'>
99+
<Plus className='h-[14px] w-[14px]' />
100+
<span className='sr-only'>Add Condition</span>
101+
</Button>
102+
<Button
103+
variant='ghost'
104+
onClick={() => onRemove(rule.id)}
105+
disabled={isReadOnly}
106+
className='h-auto p-0 text-[var(--text-error)] hover:text-[var(--text-error)]'
107+
>
108+
<Trash className='h-[14px] w-[14px]' />
109+
<span className='sr-only'>Delete Condition</span>
110+
</Button>
111+
</div>
112+
</div>
113+
)
114+
115+
const renderValueInput = () => (
116+
<div className='relative'>
117+
<Input
118+
ref={valueInputRef}
119+
value={rule.value}
120+
onChange={handlers.onChange}
121+
onKeyDown={handlers.onKeyDown}
122+
onDrop={handlers.onDrop}
123+
onDragOver={handlers.onDragOver}
124+
onFocus={handlers.onFocus}
125+
onScroll={(e) => syncOverlayScroll(e.currentTarget.scrollLeft)}
126+
onPaste={() =>
127+
setTimeout(() => {
128+
if (valueInputRef.current) {
129+
syncOverlayScroll(valueInputRef.current.scrollLeft)
130+
}
131+
}, 0)
132+
}
46133
disabled={isReadOnly}
47-
className='h-[24px] w-[24px] shrink-0 p-0 text-[var(--text-tertiary)] hover:text-[var(--text-primary)]'
134+
autoComplete='off'
135+
placeholder='Enter value'
136+
className='allow-scroll w-full overflow-auto text-transparent caret-foreground'
137+
/>
138+
<div
139+
ref={overlayRef}
140+
className={cn(
141+
'absolute inset-0 flex items-center overflow-x-auto bg-transparent px-[8px] py-[6px] font-medium font-sans text-sm',
142+
!isReadOnly && 'pointer-events-none'
143+
)}
48144
>
49-
<X className='h-[12px] w-[12px]' />
50-
</Button>
145+
<div className='w-full whitespace-pre' style={{ minWidth: 'fit-content' }}>
146+
{formatDisplayText(
147+
rule.value,
148+
accessiblePrefixes ? { accessiblePrefixes } : { highlightAll: true }
149+
)}
150+
</div>
151+
</div>
152+
{fieldState.showTags && (
153+
<TagDropdown
154+
visible={fieldState.showTags}
155+
onSelect={tagSelectHandler}
156+
blockId={blockId}
157+
activeSourceBlockId={fieldState.activeSourceBlockId}
158+
inputValue={rule.value}
159+
cursorPosition={fieldState.cursorPosition}
160+
onClose={() => inputController.fieldHelpers.hideFieldDropdowns(cellKey)}
161+
inputRef={valueInputRef.current ? { current: valueInputRef.current } : undefined}
162+
/>
163+
)}
164+
</div>
165+
)
51166

52-
<div className='w-[80px] shrink-0'>
53-
{index === 0 ? (
54-
<Combobox
55-
size='sm'
56-
options={[{ value: 'where', label: 'where' }]}
57-
value='where'
58-
disabled
59-
/>
60-
) : (
167+
const renderContent = () => (
168+
<div className='flex flex-col gap-[8px] border-[var(--border-1)] border-t px-[10px] pt-[6px] pb-[10px]'>
169+
{index > 0 && (
170+
<div className='flex flex-col gap-[6px]'>
171+
<Label className='text-[13px]'>Logic</Label>
61172
<Combobox
62-
size='sm'
63173
options={logicalOptions}
64174
value={rule.logicalOperator}
65175
onChange={(v) => onUpdate(rule.id, 'logicalOperator', v as 'and' | 'or')}
66176
disabled={isReadOnly}
67177
/>
68-
)}
69-
</div>
178+
</div>
179+
)}
70180

71-
<div className='w-[100px] shrink-0'>
181+
<div className='flex flex-col gap-[6px]'>
182+
<Label className='text-[13px]'>Column</Label>
72183
<Combobox
73-
size='sm'
74184
options={columns}
75185
value={rule.column}
76186
onChange={(v) => onUpdate(rule.id, 'column', v)}
77-
placeholder='Column'
78187
disabled={isReadOnly}
188+
placeholder='Select column'
79189
/>
80190
</div>
81191

82-
<div className='w-[110px] shrink-0'>
192+
<div className='flex flex-col gap-[6px]'>
193+
<Label className='text-[13px]'>Operator</Label>
83194
<Combobox
84-
size='sm'
85195
options={comparisonOptions}
86196
value={rule.operator}
87197
onChange={(v) => onUpdate(rule.id, 'operator', v)}
88198
disabled={isReadOnly}
199+
placeholder='Select operator'
89200
/>
90201
</div>
91202

92-
<div className='relative min-w-[80px] flex-1'>
93-
<SubBlockInputController
94-
blockId={blockId}
95-
subBlockId={`${subBlockId}_filter_${rule.id}`}
96-
config={{ id: `filter_value_${rule.id}`, type: 'short-input' }}
97-
value={rule.value}
98-
onChange={(newValue) => onUpdate(rule.id, 'value', newValue)}
99-
isPreview={isPreview}
100-
disabled={disabled}
101-
>
102-
{({ ref, value: ctrlValue, onChange, onKeyDown, onDrop, onDragOver }) => {
103-
const formattedText = formatDisplayText(ctrlValue, {
104-
accessiblePrefixes,
105-
highlightAll: !accessiblePrefixes,
106-
})
107-
108-
return (
109-
<div className='relative'>
110-
<Input
111-
ref={ref as React.RefObject<HTMLInputElement>}
112-
className='h-[28px] w-full overflow-auto text-[12px] text-transparent caret-foreground [-ms-overflow-style:none] [scrollbar-width:none] placeholder:text-muted-foreground/50 [&::-webkit-scrollbar]:hidden'
113-
value={ctrlValue}
114-
onChange={onChange as (e: React.ChangeEvent<HTMLInputElement>) => void}
115-
onKeyDown={onKeyDown as (e: React.KeyboardEvent<HTMLInputElement>) => void}
116-
onDrop={onDrop as (e: React.DragEvent<HTMLInputElement>) => void}
117-
onDragOver={onDragOver as (e: React.DragEvent<HTMLInputElement>) => void}
118-
placeholder='Value'
119-
disabled={isReadOnly}
120-
autoComplete='off'
121-
/>
122-
<div
123-
className={cn(
124-
'pointer-events-none absolute inset-0 flex items-center overflow-x-auto bg-transparent px-[8px] py-[6px] font-medium font-sans text-[12px] text-foreground [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden',
125-
(isPreview || disabled) && 'opacity-50'
126-
)}
127-
>
128-
<div className='min-w-fit whitespace-pre'>{formattedText}</div>
129-
</div>
130-
</div>
131-
)
132-
}}
133-
</SubBlockInputController>
203+
<div className='flex flex-col gap-[6px]'>
204+
<Label className='text-[13px]'>Value</Label>
205+
{renderValueInput()}
134206
</div>
135207
</div>
136208
)
209+
210+
return (
211+
<div
212+
data-filter-id={rule.id}
213+
className={cn(
214+
'rounded-[4px] border border-[var(--border-1)]',
215+
rule.collapsed ? 'overflow-hidden' : 'overflow-visible'
216+
)}
217+
>
218+
{renderHeader()}
219+
{!rule.collapsed && renderContent()}
220+
</div>
221+
)
137222
}

0 commit comments

Comments
 (0)