Skip to content

Commit fbcee22

Browse files
committed
fix(ui): scroll guard, credentials UX, design token fixes, input padding
- logs: only scroll-into-view on keyboard nav, not on click selection - resource: stable scrollbar gutter, wider first column - credentials: toast success/error feedback, remove useMemo for personalEnvData, allow editing conflict rows, fix disabled state visibility, use --text-error token - integrations: use --text-error token for error state - input: increase right padding (px-2 → pl-2 pr-3)
1 parent c23f1cd commit fbcee22

5 files changed

Lines changed: 26 additions & 18 deletions

File tree

apps/sim/app/workspace/[workspaceId]/components/resource/resource.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,10 @@ export const ResourceTable = memo(function ResourceTable({
313313
</thead>
314314
</table>
315315
</div>
316-
<div className='min-h-0 flex-1 overflow-auto' onScroll={handleBodyScroll}>
316+
<div
317+
className='min-h-0 flex-1 overflow-auto [scrollbar-gutter:stable]'
318+
onScroll={handleBodyScroll}
319+
>
317320
<table className='w-full table-fixed text-small'>
318321
<ResourceColGroup columns={columns} hasCheckbox={hasCheckbox} />
319322
<tbody>
@@ -562,7 +565,7 @@ const ResourceColGroup = memo(function ResourceColGroup({
562565
key={col.id}
563566
style={
564567
colIdx === 0
565-
? { minWidth: 200 * (col.widthMultiplier ?? 1) }
568+
? { width: 400 * (col.widthMultiplier ?? 1) }
566569
: { width: 160 * (col.widthMultiplier ?? 1) }
567570
}
568571
/>

apps/sim/app/workspace/[workspaceId]/logs/logs.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ export default function Logs() {
290290
const logsRef = useRef<WorkflowLog[]>([])
291291
const selectedLogIndexRef = useRef(-1)
292292
const selectedLogIdRef = useRef<string | null>(null)
293+
const shouldScrollIntoViewRef = useRef(false)
293294
const logsRefetchRef = useRef<() => void>(() => {})
294295
const activeLogRefetchRef = useRef<() => void>(() => {})
295296
const logsQueryRef = useRef({ isFetching: false, hasNextPage: false, fetchNextPage: () => {} })
@@ -467,13 +468,15 @@ export default function Logs() {
467468
const idx = selectedLogIndexRef.current
468469
const currentLogs = logsRef.current
469470
if (idx < currentLogs.length - 1) {
471+
shouldScrollIntoViewRef.current = true
470472
dispatch({ type: 'SELECT_LOG', logId: currentLogs[idx + 1].id })
471473
}
472474
}, [])
473475

474476
const handleNavigatePrev = useCallback(() => {
475477
const idx = selectedLogIndexRef.current
476478
if (idx > 0) {
479+
shouldScrollIntoViewRef.current = true
477480
dispatch({ type: 'SELECT_LOG', logId: logsRef.current[idx - 1].id })
478481
}
479482
}, [])
@@ -594,7 +597,8 @@ export default function Logs() {
594597
})
595598

596599
useEffect(() => {
597-
if (!selectedLogId) return
600+
if (!selectedLogId || !shouldScrollIntoViewRef.current) return
601+
shouldScrollIntoViewRef.current = false
598602
const row = document.querySelector(`[data-row-id="${selectedLogId}"]`) as HTMLElement | null
599603
if (row) {
600604
row.scrollIntoView({ behavior: 'smooth', block: 'nearest' })
@@ -713,6 +717,7 @@ export default function Logs() {
713717

714718
if (currentIndex === -1 && (e.key === 'ArrowUp' || e.key === 'ArrowDown')) {
715719
e.preventDefault()
720+
shouldScrollIntoViewRef.current = true
716721
dispatch({ type: 'SELECT_LOG', logId: currentLogs[0].id })
717722
return
718723
}

apps/sim/app/workspace/[workspaceId]/settings/components/credentials/credentials-manager.tsx

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
Textarea,
2323
Tooltip,
2424
Trash,
25+
toast,
2526
} from '@/components/emcn'
2627
import { Input } from '@/components/ui'
2728
import { useSession } from '@/lib/auth/auth-client'
@@ -60,7 +61,6 @@ const logger = createLogger('SecretsManager')
6061

6162
const GRID_COLS = 'grid grid-cols-[minmax(0,1fr)_8px_minmax(0,1fr)_auto_auto] items-center'
6263
const COL_SPAN_ALL = 'col-span-5'
63-
const CONFLICT_CLASS = 'border-[var(--text-error)] bg-[var(--error-muted)]'
6464

6565
const ROLE_OPTIONS = [
6666
{ value: 'member', label: 'Member' },
@@ -402,7 +402,6 @@ export function CredentialsManager() {
402402
const isWorkspaceAdmin = workspacePermissions?.viewer?.isAdmin ?? false
403403

404404
const isLoading = isPersonalLoading || isWorkspaceLoading
405-
const variables = useMemo(() => personalEnvData || {}, [personalEnvData])
406405

407406
const [envVars, setEnvVars] = useState<UIEnvironmentVariable[]>([])
408407
const [newWorkspaceRows, setNewWorkspaceRows] = useState<UIEnvironmentVariable[]>([
@@ -591,7 +590,7 @@ export function CredentialsManager() {
591590
useEffect(() => {
592591
if (hasSavedRef.current) return
593592

594-
const existingVars = Object.values(variables)
593+
const existingVars = Object.values(personalEnvData || {})
595594
const initialVars = [
596595
...existingVars.map((envVar) => ({
597596
...envVar,
@@ -601,7 +600,7 @@ export function CredentialsManager() {
601600
]
602601
initialVarsRef.current = JSON.parse(JSON.stringify(initialVars))
603602
setEnvVars(JSON.parse(JSON.stringify(initialVars)))
604-
}, [variables])
603+
}, [personalEnvData])
605604

606605
useEffect(() => {
607606
if (!workspaceEnvData) return
@@ -1041,11 +1040,15 @@ export function CredentialsManager() {
10411040

10421041
setWorkspaceVars(mergedWorkspaceVars)
10431042
setNewWorkspaceRows([createEmptyEnvVar()])
1043+
if (mutations.length > 0) {
1044+
toast.success('Secrets saved')
1045+
}
10441046
} catch (error) {
10451047
hasSavedRef.current = false
10461048
initialVarsRef.current = prevInitialVars
10471049
initialWorkspaceVarsRef.current = prevInitialWorkspaceVars
10481050
logger.error('Failed to save environment variables:', error)
1051+
toast.error('Failed to save secrets')
10491052
} finally {
10501053
if (mutations.length > 0) {
10511054
queryClient.invalidateQueries({ queryKey: workspaceCredentialKeys.lists() })
@@ -1095,7 +1098,7 @@ export function CredentialsManager() {
10951098
onFocus={(e) => e.target.removeAttribute('readOnly')}
10961099
className={cn(
10971100
'h-9',
1098-
isConflict && CONFLICT_CLASS,
1101+
isConflict && 'border-[var(--text-error)]',
10991102
keyError && 'border-[var(--text-error)]'
11001103
)}
11011104
/>
@@ -1115,8 +1118,6 @@ export function CredentialsManager() {
11151118
onBlur={() => setFocusedValueIndex(null)}
11161119
onPaste={(e) => handlePaste(e, originalIndex)}
11171120
placeholder={isConflict ? 'Workspace override active' : 'Enter value'}
1118-
disabled={isConflict}
1119-
aria-disabled={isConflict}
11201121
name={`env_variable_value_${envVar.id || originalIndex}_${Math.random()}`}
11211122
autoComplete='off'
11221123
autoCapitalize='off'
@@ -1125,12 +1126,11 @@ export function CredentialsManager() {
11251126
style={maskedValueStyle}
11261127
className={cn(
11271128
'h-9',
1128-
!isComplete && 'col-span-2',
1129-
isConflict && 'cursor-not-allowed',
1130-
isConflict && CONFLICT_CLASS
1129+
(!isComplete || isConflict) && 'col-span-2',
1130+
isConflict && 'cursor-not-allowed opacity-50'
11311131
)}
11321132
/>
1133-
{isComplete && (
1133+
{isComplete && !isConflict && (
11341134
<Button
11351135
variant='default'
11361136
onClick={() => handleViewDetails(envVar.key, 'env_personal')}
@@ -1267,7 +1267,7 @@ export function CredentialsManager() {
12671267
</div>
12681268

12691269
{detailsError && (
1270-
<div className='rounded-lg border border-[color-mix(in_srgb,var(--status-red)_40%,transparent)] bg-[color-mix(in_srgb,var(--status-red)_10%,transparent)] px-2.5 py-2 text-[var(--status-red)] text-small'>
1270+
<div className='rounded-lg border border-[color-mix(in_srgb,var(--text-error)_40%,transparent)] bg-[color-mix(in_srgb,var(--text-error)_10%,transparent)] px-2.5 py-2 text-[var(--text-error)] text-small'>
12711271
{detailsError}
12721272
</div>
12731273
)}
@@ -1299,7 +1299,7 @@ export function CredentialsManager() {
12991299
</AvatarFallback>
13001300
</Avatar>
13011301
<div className='min-w-0'>
1302-
<p className='truncate font-medium text-[var(--text-primary)] text-sm'>
1302+
<p className='truncate font-medium text-[var(--text-primary)] text-small'>
13031303
{member.userName || member.userEmail || member.userId}
13041304
</p>
13051305
<p className='truncate text-[var(--text-tertiary)] text-caption'>

apps/sim/app/workspace/[workspaceId]/settings/components/integrations/integrations-manager.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1288,7 +1288,7 @@ export function IntegrationsManager() {
12881288
</div>
12891289

12901290
{detailsError && (
1291-
<div className='rounded-lg border border-[color-mix(in_srgb,var(--status-red)_40%,transparent)] bg-[color-mix(in_srgb,var(--status-red)_10%,transparent)] px-2.5 py-2 text-[var(--status-red)] text-small'>
1291+
<div className='rounded-lg border border-[color-mix(in_srgb,var(--text-error)_40%,transparent)] bg-[color-mix(in_srgb,var(--text-error)_10%,transparent)] px-2.5 py-2 text-[var(--text-error)] text-small'>
12921292
{detailsError}
12931293
</div>
12941294
)}

apps/sim/components/emcn/components/input/input.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { cn } from '@/lib/core/utils/cn'
2626
* Currently supports a 'default' variant.
2727
*/
2828
const inputVariants = cva(
29-
'flex w-full touch-manipulation rounded-sm border border-[var(--border-1)] bg-[var(--surface-5)] px-2 py-1.5 font-medium font-sans text-sm text-[var(--text-primary)] transition-colors placeholder:text-[var(--text-muted)] outline-none disabled:cursor-not-allowed disabled:opacity-50',
29+
'flex w-full touch-manipulation rounded-sm border border-[var(--border-1)] bg-[var(--surface-5)] pl-2 pr-3 py-1.5 font-medium font-sans text-sm text-[var(--text-primary)] transition-colors placeholder:text-[var(--text-muted)] outline-none disabled:cursor-not-allowed disabled:opacity-50',
3030
{
3131
variants: {
3232
variant: {

0 commit comments

Comments
 (0)