2
0

feat(integration): Add Google Sheets integration

This commit is contained in:
Baptiste Arnaud
2022-01-18 18:25:18 +01:00
parent 2814a352b2
commit f49b5143cf
67 changed files with 2560 additions and 391 deletions

View 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,
}
}

View File

@ -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,

View 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,
}
}

View File

@ -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

View File

@ -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,

View File

@ -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: {

View File

@ -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({

View File

@ -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 = ''