From 77bb048307c68c5a5303a727a43b7f3814e8f73a Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Thu, 12 Feb 2026 18:04:02 -0800 Subject: [PATCH] share button --- apps/sim/app/api/credentials/[id]/route.ts | 6 +- .../credentials/credentials-manager.tsx | 181 +++++++++--------- 2 files changed, 98 insertions(+), 89 deletions(-) diff --git a/apps/sim/app/api/credentials/[id]/route.ts b/apps/sim/app/api/credentials/[id]/route.ts index fe1f10f66..182fe7d40 100644 --- a/apps/sim/app/api/credentials/[id]/route.ts +++ b/apps/sim/app/api/credentials/[id]/route.ts @@ -113,11 +113,15 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{ updates.description = parseResult.data.description ?? null } + if (parseResult.data.displayName !== undefined && access.credential.type === 'oauth') { + updates.displayName = parseResult.data.displayName + } + if (Object.keys(updates).length === 0) { if (access.credential.type === 'oauth') { return NextResponse.json( { - error: 'OAuth credential editing is limited to description only.', + error: 'No updatable fields provided.', }, { status: 400 } ) diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/credentials/credentials-manager.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/credentials/credentials-manager.tsx index d51fe7090..79673bfbc 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/credentials/credentials-manager.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/credentials/credentials-manager.tsx @@ -183,6 +183,7 @@ export function CredentialsManager() { const [selectedEnvValueDraft, setSelectedEnvValueDraft] = useState('') const [isEditingEnvValue, setIsEditingEnvValue] = useState(false) const [selectedDescriptionDraft, setSelectedDescriptionDraft] = useState('') + const [selectedDisplayNameDraft, setSelectedDisplayNameDraft] = useState('') const [showCreateOAuthRequiredModal, setShowCreateOAuthRequiredModal] = useState(false) const { data: session } = useSession() const currentUserId = session?.user?.id || '' @@ -325,26 +326,68 @@ export function CredentialsManager() { if (!selectedCredential || selectedCredential.type === 'oauth') return false return selectedEnvValueDraft !== selectedEnvCurrentValue }, [selectedCredential, selectedEnvValueDraft, selectedEnvCurrentValue]) - useEffect(() => { - if (!selectedCredential || !isSelectedAdmin) return - if (selectedDescriptionDraft === (selectedCredential.description || '')) return - const timer = setTimeout(async () => { - try { + const isDescriptionDirty = useMemo(() => { + if (!selectedCredential) return false + return selectedDescriptionDraft !== (selectedCredential.description || '') + }, [selectedCredential, selectedDescriptionDraft]) + + const isDisplayNameDirty = useMemo(() => { + if (!selectedCredential) return false + return selectedDisplayNameDraft !== selectedCredential.displayName + }, [selectedCredential, selectedDisplayNameDraft]) + + const isDetailsDirty = isEnvValueDirty || isDescriptionDirty || isDisplayNameDirty + const [isSavingDetails, setIsSavingDetails] = useState(false) + + const handleSaveDetails = async () => { + if (!selectedCredential || !isSelectedAdmin || !isDetailsDirty) return + setDetailsError(null) + setIsSavingDetails(true) + + try { + if (isDisplayNameDirty || isDescriptionDirty) { await updateCredential.mutateAsync({ credentialId: selectedCredential.id, - description: selectedDescriptionDraft.trim() || null, + ...(isDisplayNameDirty && selectedCredential.type === 'oauth' + ? { displayName: selectedDisplayNameDraft.trim() } + : {}), + ...(isDescriptionDirty ? { description: selectedDescriptionDraft.trim() || null } : {}), }) - await refetchCredentials() - } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Failed to update description' - setDetailsError(message) - logger.error('Failed to autosave credential description', error) } - }, 600) - return () => clearTimeout(timer) - }, [selectedDescriptionDraft]) + if (isEnvValueDirty && canEditSelectedEnvValue) { + const envKey = selectedCredential.envKey || '' + if (envKey) { + if (selectedCredential.type === 'env_workspace') { + await upsertWorkspaceEnvironment.mutateAsync({ + workspaceId, + variables: { [envKey]: selectedEnvValueDraft }, + }) + } else { + const personalVariables = Object.entries(personalEnvironment).reduce( + (acc, [key, value]) => ({ + ...acc, + [key]: value.value, + }), + {} as Record + ) + await savePersonalEnvironment.mutateAsync({ + variables: { ...personalVariables, [envKey]: selectedEnvValueDraft }, + }) + } + } + } + + await refetchCredentials() + } catch (error: unknown) { + const message = error instanceof Error ? error.message : 'Failed to save changes' + setDetailsError(message) + logger.error('Failed to save credential details', error) + } finally { + setIsSavingDetails(false) + } + } useEffect(() => { if (createType !== 'oauth') return @@ -397,11 +440,13 @@ export function CredentialsManager() { setSelectedEnvValueDraft('') setIsEditingEnvValue(false) setSelectedDescriptionDraft('') + setSelectedDisplayNameDraft('') return } setDetailsError(null) setSelectedDescriptionDraft(selectedCredential.description || '') + setSelectedDisplayNameDraft(selectedCredential.displayName) if (selectedCredential.type === 'oauth') { setSelectedEnvValueDraft('') @@ -462,54 +507,6 @@ export function CredentialsManager() { ) }, [selectedCredential, isSelectedAdmin, currentUserId]) - useEffect(() => { - if (!selectedCredential || selectedCredential.type === 'oauth') return - if (!canEditSelectedEnvValue || !isEditingEnvValue) return - if (selectedEnvValueDraft === selectedEnvCurrentValue) return - - const envKey = selectedCredential.envKey || '' - if (!envKey) return - - const timer = setTimeout(async () => { - try { - setDetailsError(null) - const nextValue = selectedEnvValueDraft - - if (selectedCredential.type === 'env_workspace') { - await upsertWorkspaceEnvironment.mutateAsync({ - workspaceId, - variables: { - [envKey]: nextValue, - }, - }) - } else { - const personalVariables = Object.entries(personalEnvironment).reduce( - (acc, [key, value]) => ({ - ...acc, - [key]: value.value, - }), - {} as Record - ) - - await savePersonalEnvironment.mutateAsync({ - variables: { - ...personalVariables, - [envKey]: nextValue, - }, - }) - } - - await refetchCredentials() - } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Failed to update secret value' - setDetailsError(message) - logger.error('Failed to autosave environment credential value', error) - } - }, 600) - - return () => clearTimeout(timer) - }, [selectedEnvValueDraft]) - const handleCreateCredential = async () => { if (!workspaceId) return setCreateError(null) @@ -955,6 +952,13 @@ export function CredentialsManager() { {isSelectedAdmin && (
+ {selectedCredential.type === 'env_personal' && ( @@ -966,10 +970,10 @@ export function CredentialsManager() { - Promote to workspace secret + Promote to Workspace Secret )} - {selectedCredential.type !== 'env_workspace' && + {selectedCredential.type === 'oauth' && (workspaceUserOptions.length > 0 || isShareingWithWorkspace) && ( @@ -980,34 +984,34 @@ export function CredentialsManager() { isShareingWithWorkspace || workspaceUserOptions.length === 0 } > - Share with workspace + - {isShareingWithWorkspace - ? 'Sharing...' - : `Add all ${workspaceUserOptions.length} remaining workspace member${workspaceUserOptions.length === 1 ? '' : 's'}`} + {isShareingWithWorkspace ? 'Sharing...' : 'Share with workspace'} )} - {selectedCredential.type === 'oauth' && ( - - )} - {selectedCredential.type !== 'oauth' && ( - - )} + + + + + + {selectedCredential.type === 'oauth' + ? 'Disconnect account' + : 'Delete credential'} + +
)} @@ -1018,9 +1022,10 @@ export function CredentialsManager() { setSelectedDisplayNameDraft(event.target.value)} autoComplete='off' - disabled + disabled={!isSelectedAdmin} className='mt-[6px]' />