Skip to content

Commit aad98c3

Browse files
committed
🛂 Improve editor authorization feedback
Closes #844, closes #839
1 parent 617c500 commit aad98c3

File tree

7 files changed

+62
-37
lines changed

7 files changed

+62
-37
lines changed

apps/builder/src/features/account/UserProvider.tsx

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useSession } from 'next-auth/react'
1+
import { signOut, useSession } from 'next-auth/react'
22
import { useRouter } from 'next/router'
33
import { createContext, ReactNode, useEffect, useState } from 'react'
44
import { isDefined, isNotDefined } from '@typebot.io/lib'
@@ -15,9 +15,13 @@ export const userContext = createContext<{
1515
user?: User
1616
isLoading: boolean
1717
currentWorkspaceId?: string
18+
logOut: () => void
1819
updateUser: (newUser: Partial<User>) => void
1920
}>({
2021
isLoading: false,
22+
logOut: () => {
23+
console.log('logOut not implemented')
24+
},
2125
updateUser: () => {
2226
console.log('updateUser not implemented')
2327
},
@@ -91,6 +95,11 @@ export const UserProvider = ({ children }: { children: ReactNode }) => {
9195
env.NEXT_PUBLIC_E2E_TEST ? 0 : debounceTimeout
9296
)
9397

98+
const logOut = () => {
99+
signOut()
100+
setUser(undefined)
101+
}
102+
94103
useEffect(() => {
95104
return () => {
96105
saveUser.flush()
@@ -103,6 +112,7 @@ export const UserProvider = ({ children }: { children: ReactNode }) => {
103112
user,
104113
isLoading: status === 'loading',
105114
currentWorkspaceId,
115+
logOut,
106116
updateUser,
107117
}}
108118
>

apps/builder/src/features/auth/components/SignInForm.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export const SignInForm = ({
5555

5656
useEffect(() => {
5757
if (status === 'authenticated') {
58-
router.replace(router.query.callbackUrl?.toString() ?? '/typebots')
58+
router.replace(router.query.redirectPath?.toString() ?? '/typebots')
5959
return
6060
}
6161
;(async () => {

apps/builder/src/features/credentials/components/CredentialsDropdown.tsx

+15-10
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { useRouter } from 'next/router'
1515
import { useToast } from '../../../hooks/useToast'
1616
import { Credentials } from '@typebot.io/schemas'
1717
import { trpc } from '@/lib/trpc'
18+
import { useWorkspace } from '@/features/workspace/WorkspaceProvider'
1819

1920
type Props = Omit<ButtonProps, 'type'> & {
2021
type: Credentials['type']
@@ -38,6 +39,7 @@ export const CredentialsDropdown = ({
3839
}: Props) => {
3940
const router = useRouter()
4041
const { showToast } = useToast()
42+
const { currentRole } = useWorkspace()
4143
const { data, refetch } = trpc.credentials.listCredentials.useQuery({
4244
workspaceId,
4345
type,
@@ -107,6 +109,7 @@ export const CredentialsDropdown = ({
107109
textAlign="left"
108110
leftIcon={<PlusIcon />}
109111
onClick={onCreateNewClick}
112+
isDisabled={currentRole === 'GUEST'}
110113
{...props}
111114
>
112115
Add {credentialsName}
@@ -165,16 +168,18 @@ export const CredentialsDropdown = ({
165168
/>
166169
</MenuItem>
167170
))}
168-
<MenuItem
169-
maxW="500px"
170-
overflow="hidden"
171-
whiteSpace="nowrap"
172-
textOverflow="ellipsis"
173-
icon={<PlusIcon />}
174-
onClick={onCreateNewClick}
175-
>
176-
Connect new
177-
</MenuItem>
171+
{currentRole === 'GUEST' ? null : (
172+
<MenuItem
173+
maxW="500px"
174+
overflow="hidden"
175+
whiteSpace="nowrap"
176+
textOverflow="ellipsis"
177+
icon={<PlusIcon />}
178+
onClick={onCreateNewClick}
179+
>
180+
Connect new
181+
</MenuItem>
182+
)}
178183
</Stack>
179184
</MenuList>
180185
</Menu>

apps/builder/src/features/dashboard/components/DashboardHeader.tsx

+2-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import React from 'react'
22
import { HStack, Flex, Button, useDisclosure } from '@chakra-ui/react'
33
import { HardDriveIcon, SettingsIcon } from '@/components/icons'
4-
import { signOut } from 'next-auth/react'
54
import { useUser } from '@/features/account/hooks/useUser'
65
import { isNotDefined } from '@typebot.io/lib'
76
import Link from 'next/link'
@@ -13,15 +12,11 @@ import { WorkspaceSettingsModal } from '@/features/workspace/components/Workspac
1312

1413
export const DashboardHeader = () => {
1514
const scopedT = useScopedI18n('dashboard.header')
16-
const { user } = useUser()
15+
const { user, logOut } = useUser()
1716
const { workspace, switchWorkspace, createWorkspace } = useWorkspace()
1817

1918
const { isOpen, onOpen, onClose } = useDisclosure()
2019

21-
const handleLogOut = () => {
22-
signOut()
23-
}
24-
2520
const handleCreateNewWorkspace = () =>
2621
createWorkspace(user?.name ?? undefined)
2722

@@ -59,7 +54,7 @@ export const DashboardHeader = () => {
5954
</Button>
6055
<WorkspaceDropdown
6156
currentWorkspace={workspace}
62-
onLogoutClick={handleLogOut}
57+
onLogoutClick={logOut}
6358
onCreateNewWorkspaceClick={handleCreateNewWorkspace}
6459
onWorkspaceSelected={switchWorkspace}
6560
/>

apps/builder/src/features/editor/providers/TypebotProvider.tsx

+10-4
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ export const TypebotProvider = ({
168168

169169
const saveTypebot = useCallback(
170170
async (updates?: Partial<Typebot>) => {
171-
if (!localTypebot || !typebot) return
171+
if (!localTypebot || !typebot || typebotData?.isReadOnly) return
172172
const typebotToSave = { ...localTypebot, ...updates }
173173
if (dequal(omit(typebot, 'updatedAt'), omit(typebotToSave, 'updatedAt')))
174174
return
@@ -180,7 +180,13 @@ export const TypebotProvider = ({
180180
setLocalTypebot({ ...newTypebot })
181181
return newTypebot
182182
},
183-
[localTypebot, setLocalTypebot, typebot, updateTypebot]
183+
[
184+
localTypebot,
185+
setLocalTypebot,
186+
typebot,
187+
typebotData?.isReadOnly,
188+
updateTypebot,
189+
]
184190
)
185191

186192
useAutoSave(
@@ -212,15 +218,15 @@ export const TypebotProvider = ({
212218
)
213219

214220
useEffect(() => {
215-
if (!localTypebot || !typebot) return
221+
if (!localTypebot || !typebot || typebotData?.isReadOnly) return
216222
if (!areTypebotsEqual(localTypebot, typebot)) {
217223
window.addEventListener('beforeunload', preventUserFromRefreshing)
218224
}
219225

220226
return () => {
221227
window.removeEventListener('beforeunload', preventUserFromRefreshing)
222228
}
223-
}, [localTypebot, typebot])
229+
}, [localTypebot, typebot, typebotData?.isReadOnly])
224230

225231
const updateLocalTypebot = async ({
226232
updates,

apps/builder/src/features/results/components/table/ResultsTable.tsx

+17-13
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
} from '@chakra-ui/react'
1010
import { AlignLeftTextIcon } from '@/components/icons'
1111
import { ResultHeaderCell, ResultsTablePreferences } from '@typebot.io/schemas'
12-
import React, { useEffect, useRef, useState } from 'react'
12+
import React, { useCallback, useEffect, useRef, useState } from 'react'
1313
import { LoadingRows } from './LoadingRows'
1414
import {
1515
useReactTable,
@@ -48,7 +48,7 @@ export const ResultsTable = ({
4848
onResultExpandIndex,
4949
}: ResultsTableProps) => {
5050
const background = useColorModeValue('white', colors.gray[900])
51-
const { updateTypebot } = useTypebot()
51+
const { updateTypebot, isReadOnly } = useTypebot()
5252
const [rowSelection, setRowSelection] = useState<Record<string, boolean>>({})
5353
const [isTableScrolled, setIsTableScrolled] = useState(false)
5454
const bottomElement = useRef<HTMLDivElement | null>(null)
@@ -185,6 +185,14 @@ export const ResultsTable = ({
185185
getCoreRowModel: getCoreRowModel(),
186186
})
187187

188+
const handleObserver = useCallback(
189+
(entities: IntersectionObserverEntry[]) => {
190+
const target = entities[0]
191+
if (target.isIntersecting) onScrollToBottom()
192+
},
193+
[onScrollToBottom]
194+
)
195+
188196
useEffect(() => {
189197
if (!bottomElement.current) return
190198
const options: IntersectionObserverInit = {
@@ -197,21 +205,17 @@ export const ResultsTable = ({
197205
return () => {
198206
observer.disconnect()
199207
}
200-
// eslint-disable-next-line react-hooks/exhaustive-deps
201-
}, [bottomElement.current])
202-
203-
const handleObserver = (entities: IntersectionObserverEntry[]) => {
204-
const target = entities[0]
205-
if (target.isIntersecting) onScrollToBottom()
206-
}
208+
}, [handleObserver])
207209

208210
return (
209211
<Stack maxW="1600px" px="4" overflowY="hidden" spacing={6}>
210212
<HStack w="full" justifyContent="flex-end">
211-
<SelectionToolbar
212-
selectedResultsId={Object.keys(rowSelection)}
213-
onClearSelection={() => setRowSelection({})}
214-
/>
213+
{isReadOnly ? null : (
214+
<SelectionToolbar
215+
selectedResultsId={Object.keys(rowSelection)}
216+
onClearSelection={() => setRowSelection({})}
217+
/>
218+
)}
215219
<TableSettingsButton
216220
resultHeader={resultHeader}
217221
columnVisibility={columnsVisibility}

apps/builder/src/features/workspace/WorkspaceProvider.tsx

+6-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export const WorkspaceProvider = ({
3838
typebotId,
3939
children,
4040
}: WorkspaceContextProps) => {
41-
const { pathname, query, push } = useRouter()
41+
const { pathname, query, push, isReady: isRouterReady } = useRouter()
4242
const { user } = useUser()
4343
const userId = user?.id
4444
const [workspaceId, setWorkspaceId] = useState<string | undefined>()
@@ -102,6 +102,8 @@ export const WorkspaceProvider = ({
102102

103103
useEffect(() => {
104104
if (
105+
pathname === '/signin' ||
106+
!isRouterReady ||
105107
!workspaces ||
106108
workspaces.length === 0 ||
107109
workspaceId ||
@@ -122,7 +124,10 @@ export const WorkspaceProvider = ({
122124
setWorkspaceIdInLocalStorage(newWorkspaceId)
123125
setWorkspaceId(newWorkspaceId)
124126
}, [
127+
isRouterReady,
125128
members,
129+
pathname,
130+
query,
126131
query.workspaceId,
127132
typebot?.workspaceId,
128133
typebotId,

0 commit comments

Comments
 (0)