feat(integration): ✨ Add Google Sheets integration
This commit is contained in:
22
apps/builder/services/credentials.ts
Normal file
22
apps/builder/services/credentials.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { Credentials } from 'db'
|
||||
import useSWR from 'swr'
|
||||
import { fetcher } from './utils'
|
||||
|
||||
export const useCredentials = ({
|
||||
userId,
|
||||
onError,
|
||||
}: {
|
||||
userId?: string
|
||||
onError: (error: Error) => void
|
||||
}) => {
|
||||
const { data, error, mutate } = useSWR<{ credentials: Credentials[] }, Error>(
|
||||
userId ? `/api/users/${userId}/credentials` : null,
|
||||
fetcher
|
||||
)
|
||||
if (error) onError(error)
|
||||
return {
|
||||
credentials: data?.credentials,
|
||||
isLoading: !error && !data,
|
||||
mutate,
|
||||
}
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
import { DashboardFolder } from '.prisma/client'
|
||||
import useSWR from 'swr'
|
||||
import { fetcher, sendRequest } from './utils'
|
||||
import { fetcher } from './utils'
|
||||
import { stringify } from 'qs'
|
||||
import { sendRequest } from 'utils'
|
||||
|
||||
export const useFolders = ({
|
||||
parentId,
|
||||
|
66
apps/builder/services/integrations.ts
Normal file
66
apps/builder/services/integrations.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import { sendRequest } from 'utils'
|
||||
import { stringify } from 'qs'
|
||||
import useSWR from 'swr'
|
||||
import { fetcher } from './utils'
|
||||
|
||||
export const getGoogleSheetsConsentScreenUrl = (
|
||||
redirectUrl: string,
|
||||
stepId: string
|
||||
) => {
|
||||
const queryParams = stringify({ redirectUrl, stepId })
|
||||
return `/api/credentials/google-sheets/consent-url?${queryParams}`
|
||||
}
|
||||
|
||||
export const createSheetsAccount = async (code: string) => {
|
||||
const queryParams = stringify({ code })
|
||||
return sendRequest({
|
||||
url: `/api/credentials/google-sheets/callback?${queryParams}`,
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
|
||||
export type Spreadsheet = { id: string; name: string }
|
||||
export const useSpreadsheets = ({
|
||||
credentialsId,
|
||||
onError,
|
||||
}: {
|
||||
credentialsId: string
|
||||
onError?: (error: Error) => void
|
||||
}) => {
|
||||
const queryParams = stringify({ credentialsId })
|
||||
const { data, error, mutate } = useSWR<{ files: Spreadsheet[] }, Error>(
|
||||
`/api/integrations/google-sheets/spreadsheets?${queryParams}`,
|
||||
fetcher
|
||||
)
|
||||
if (error) onError && onError(error)
|
||||
return {
|
||||
spreadsheets: data?.files,
|
||||
isLoading: !error && !data,
|
||||
mutate,
|
||||
}
|
||||
}
|
||||
|
||||
export type Sheet = { id: string; name: string; columns: string[] }
|
||||
export const useSheets = ({
|
||||
credentialsId,
|
||||
spreadsheetId,
|
||||
onError,
|
||||
}: {
|
||||
credentialsId?: string
|
||||
spreadsheetId?: string
|
||||
onError?: (error: Error) => void
|
||||
}) => {
|
||||
const queryParams = stringify({ credentialsId })
|
||||
const { data, error, mutate } = useSWR<{ sheets: Sheet[] }, Error>(
|
||||
!credentialsId || !spreadsheetId
|
||||
? null
|
||||
: `/api/integrations/google-sheets/spreadsheets/${spreadsheetId}/sheets?${queryParams}`,
|
||||
fetcher
|
||||
)
|
||||
if (error) onError && onError(error)
|
||||
return {
|
||||
sheets: data?.sheets,
|
||||
isLoading: !error && !data,
|
||||
mutate,
|
||||
}
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
import { PublicTypebot, Typebot } from 'models'
|
||||
import { sendRequest } from './utils'
|
||||
import shortId from 'short-uuid'
|
||||
import { HStack, Text } from '@chakra-ui/react'
|
||||
import { CalendarIcon } from 'assets/icons'
|
||||
import { StepIcon } from 'components/board/StepTypesList/StepIcon'
|
||||
import { isInputStep } from 'utils'
|
||||
import { isInputStep, sendRequest } from 'utils'
|
||||
|
||||
export const parseTypebotToPublicTypebot = (
|
||||
typebot: Typebot
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { Result } from 'models'
|
||||
import useSWRInfinite from 'swr/infinite'
|
||||
import { fetcher, sendRequest } from './utils'
|
||||
import { fetcher } from './utils'
|
||||
import { stringify } from 'qs'
|
||||
import { Answer } from 'db'
|
||||
import { sendRequest } from 'utils'
|
||||
|
||||
const getKey = (
|
||||
typebotId: string,
|
||||
|
@ -6,25 +6,24 @@ import {
|
||||
Settings,
|
||||
StartStep,
|
||||
Theme,
|
||||
BubbleStep,
|
||||
InputStep,
|
||||
BubbleStepType,
|
||||
InputStepType,
|
||||
ChoiceInputStep,
|
||||
LogicStepType,
|
||||
LogicStep,
|
||||
Step,
|
||||
ConditionStep,
|
||||
ComparisonOperators,
|
||||
LogicalOperator,
|
||||
DraggableStepType,
|
||||
DraggableStep,
|
||||
} from 'models'
|
||||
import shortId, { generate } from 'short-uuid'
|
||||
import { Typebot } from 'models'
|
||||
import useSWR from 'swr'
|
||||
import { fetcher, sendRequest, toKebabCase } from './utils'
|
||||
import { fetcher, toKebabCase } from './utils'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import { stringify } from 'qs'
|
||||
import { isChoiceInput, isConditionStep } from 'utils'
|
||||
import { isChoiceInput, isConditionStep, sendRequest } from 'utils'
|
||||
|
||||
export const useTypebots = ({
|
||||
folderId,
|
||||
@ -114,9 +113,9 @@ export const parseNewBlock = ({
|
||||
}
|
||||
|
||||
export const parseNewStep = (
|
||||
type: BubbleStepType | InputStepType | LogicStepType,
|
||||
type: DraggableStepType,
|
||||
blockId: string
|
||||
): BubbleStep | InputStep | LogicStep => {
|
||||
): DraggableStep => {
|
||||
const id = `s${shortId.generate()}`
|
||||
switch (type) {
|
||||
case BubbleStepType.TEXT: {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { User } from 'db'
|
||||
import { sendRequest } from './utils'
|
||||
import { sendRequest } from 'utils'
|
||||
|
||||
export const updateUser = async (id: string, user: User) =>
|
||||
sendRequest({
|
||||
|
@ -10,36 +10,6 @@ export const isMobile =
|
||||
typeof window !== 'undefined' &&
|
||||
window.matchMedia('only screen and (max-width: 760px)').matches
|
||||
|
||||
export const sendRequest = async <ResponseData>({
|
||||
url,
|
||||
method,
|
||||
body,
|
||||
}: {
|
||||
url: string
|
||||
method: string
|
||||
body?: Record<string, unknown>
|
||||
}): Promise<{ data?: ResponseData; error?: Error }> => {
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method,
|
||||
mode: 'cors',
|
||||
body: body ? JSON.stringify(body) : undefined,
|
||||
})
|
||||
if (!response.ok) throw new Error(response.statusText)
|
||||
const data = await response.json()
|
||||
return { data }
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return { error: e as Error }
|
||||
}
|
||||
}
|
||||
|
||||
export const insertItemInList = <T>(
|
||||
arr: T[],
|
||||
index: number,
|
||||
newItem: T
|
||||
): T[] => [...arr.slice(0, index), newItem, ...arr.slice(index)]
|
||||
|
||||
export const preventUserFromRefreshing = (e: BeforeUnloadEvent) => {
|
||||
e.preventDefault()
|
||||
e.returnValue = ''
|
||||
|
Reference in New Issue
Block a user