> ## Documentation Index
> Fetch the complete documentation index at: https://docs.superblocks.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Google Drive

export const IntegrationSuccess = ({integrationName}) => {
  return <p>
      <b>{integrationName} connected!</b><br />You can now use{" "}
      <a href="/building-with-clark">Clark to build</a> with {integrationName}.
    </p>;
};

export const DynamicIntegrationActions = ({integrationId, maxActions = 20}) => {
  const [actions, setActions] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  const [showAll, setShowAll] = useState(false);
  useEffect(() => {
    const fetchIntegrationSpec = async () => {
      try {
        setLoading(true);
        const response = await fetch(`https://integrations.superblocks.com/openapi/${integrationId}.yaml`);
        if (!response.ok) {
          throw new Error(`Failed to fetch integration spec: ${response.status}`);
        }
        const yamlText = await response.text();
        const actions = parseActionsFromYaml(yamlText);
        setActions(actions);
        setError(null);
      } catch (err) {
        setError(err.message);
        setActions(getFallbackActions(integrationId));
      } finally {
        setLoading(false);
      }
    };
    fetchIntegrationSpec();
  }, [integrationId]);
  const parseActionsFromYaml = yamlText => {
    const actions = [];
    const summaryRegex = /summary:[ \t]+(.+)/g;
    let match;
    while ((match = summaryRegex.exec(yamlText)) !== null) {
      const summary = match[1].trim();
      if (summary && summary.length > 0) {
        actions.push({
          summary: summary,
          description: `API action: ${summary.toLowerCase()}`
        });
      }
    }
    return actions.slice(0, 50);
  };
  const getFallbackActions = integrationId => {
    const fallbackActions = {
      slack: [{
        summary: 'Send Message',
        description: 'Send a message to a Slack channel or user'
      }, {
        summary: 'Get Channel Info',
        description: 'Retrieve information about a Slack channel'
      }, {
        summary: 'List Channels',
        description: 'Get a list of channels in the workspace'
      }, {
        summary: 'Get User Info',
        description: 'Retrieve information about a Slack user'
      }, {
        summary: 'List Users',
        description: 'Get a list of users in the workspace'
      }],
      github: [{
        summary: 'Get Repository',
        description: 'Retrieve information about a repository'
      }, {
        summary: 'List Issues',
        description: 'Get a list of issues in a repository'
      }, {
        summary: 'Create Issue',
        description: 'Create a new issue in a repository'
      }, {
        summary: 'List Pull Requests',
        description: 'Get a list of pull requests'
      }, {
        summary: 'Create Pull Request',
        description: 'Create a new pull request'
      }]
    };
    return fallbackActions[integrationId] || [{
      summary: 'Generic Action',
      description: 'Perform actions with this integration'
    }];
  };
  if (loading) {
    return <div>
        <p>Loading actions for {integrationId}...</p>
        <div style={{
      display: 'inline-flex',
      flexWrap: 'wrap',
      gap: '0.5em',
      marginTop: '8px'
    }}>
          {[...Array(8)].map((_, i) => <div key={i} style={{
      alignItems: 'center',
      padding: '2px 6px',
      borderRadius: '32px',
      border: '1px solid var(--superblocks-accent-blue-subtle)',
      background: 'var(--superblocks-accent-blue-subtle)',
      height: '20px',
      width: `${Math.random() * 100 + 80}px`
    }}>
              <div style={{
      height: '12px',
      backgroundColor: '#e1e5e9',
      borderRadius: '4px',
      width: '100%'
    }}></div>
            </div>)}
        </div>
      </div>;
  }
  if (error) {}
  const displayedActions = showAll ? actions : actions.slice(0, maxActions);
  const hiddenCount = actions.length - maxActions;
  return <div>
      {actions.length === 0 && <p style={{
    color: 'red',
    fontWeight: 'bold'
  }}>
          No actions found for {integrationId}. Check console for errors.
        </p>}
      <div style={{
    display: 'inline-flex',
    flexWrap: 'wrap',
    gap: '0.5em',
    marginTop: '8px'
  }}>
        {displayedActions.map((action, index) => <div key={index} style={{
    alignItems: 'center',
    padding: '4px 8px',
    borderRadius: '32px',
    border: '1px solid var(--superblocks-accent-blue-subtle)',
    background: 'var(--superblocks-accent-blue-subtle)',
    transition: 'display 0.3ms ease-in'
  }}>
            <h4 style={{
    color: 'var(--ifm-color-primary-darker)',
    marginBottom: 'unset',
    alignSelf: 'center',
    fontSize: '12px',
    fontStyle: 'normal',
    fontWeight: '500',
    lineHeight: '16px',
    margin: '0',
    padding: '0'
  }}>
              {action.summary}
            </h4>
          </div>)}
        {showAll && <a href={`https://app.superblocks.com/integrations/${integrationId === 'openai' ? 'openai_v2' : integrationId}`} target="_blank" rel="noopener noreferrer" style={{
    color: 'var(--ifm-color-primary)',
    fontSize: '12px',
    textDecoration: 'none',
    border: 'none',
    borderBottom: 'none',
    padding: '4px 0',
    marginLeft: '8px',
    alignSelf: 'center',
    whiteSpace: 'nowrap'
  }}>
            See full list in app →
          </a>}
      </div>
      
      {!showAll && hiddenCount > 0 && <div style={{
    marginTop: '8px',
    textAlign: 'center'
  }}>
          <button onClick={() => setShowAll(true)} style={{
    background: 'none',
    border: 'none',
    color: '#6c7989',
    cursor: 'pointer',
    textDecoration: 'underline',
    fontSize: '14px'
  }}>
            and {hiddenCount} more
          </button>
        </div>}
      
      {showAll && hiddenCount > 0 && <div style={{
    marginTop: '8px',
    textAlign: 'center'
  }}>
          <button onClick={() => setShowAll(false)} style={{
    background: 'none',
    border: 'none',
    color: '#6c7989',
    cursor: 'pointer',
    textDecoration: 'underline',
    fontSize: '14px'
  }}>
            show less
          </button>
        </div>}
    </div>;
};

export const DynamicIntegrationSetup = ({integrationId, title}) => {
  const [setupContent, setSetupContent] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  useEffect(() => {
    const fetchIntegrationSpec = async () => {
      try {
        setLoading(true);
        const response = await fetch(`https://integrations.superblocks.com/openapi/${integrationId}.yaml`);
        if (!response.ok) {
          throw new Error(`Failed to fetch integration spec: ${response.status}`);
        }
        const yamlText = await response.text();
        const infoStartIndex = yamlText.indexOf('\ninfo:');
        if (infoStartIndex === -1) {
          const altIndex = yamlText.indexOf('info:');
          if (altIndex !== 0) {
            throw new Error('Could not find info section in YAML');
          }
        }
        let afterInfo, infoSection;
        if (infoStartIndex !== -1) {
          afterInfo = yamlText.slice(infoStartIndex + 6);
        } else {
          afterInfo = yamlText.slice(5);
        }
        const lines = afterInfo.split('\n');
        let infoEndIndex = afterInfo.length;
        for (let i = 0; i < lines.length; i++) {
          const line = lines[i];
          if (line.trim() && !line.match(/^\s/)) {
            infoEndIndex = afterInfo.indexOf(line);
            break;
          }
        }
        infoSection = afterInfo.slice(0, infoEndIndex).trim();
        let overviewText = null;
        const multilineMatchDash = infoSection.match(/description:\s*\|\-\s*\n([\s\S]*?)(?=\n\s+(?:termsOfService|version|contact|license|x-public-description):|$)/);
        if (multilineMatchDash) {
          overviewText = multilineMatchDash[1].replace(/^\s+|\s+$/g, '');
        } else {
          const multilineMatch = infoSection.match(/description:\s*\|\s*\n([\s\S]*?)(?=\n\s+(?:termsOfService|version|contact|license|x-public-description):|$)/);
          if (multilineMatch) {
            overviewText = multilineMatch[1].replace(/^\s+|\s+$/g, '');
          } else {
            const simpleMatch = infoSection.match(/description:\s*([\s\S]*?)(?=\n\s+(?:termsOfService|version|contact|license|x-public-description):|$)/);
            if (simpleMatch) {
              overviewText = simpleMatch[1].trim();
            }
          }
        }
        if (!overviewText) {
          throw new Error('Could not extract description/overview from YAML spec');
        }
        const parsedOverview = parseOverview(overviewText);
        if (!parsedOverview.setup) {
          throw new Error('Could not parse setup section from overview');
        }
        const titleMatch = infoSection.match(/title:\s*(.+)/);
        const specTitle = titleMatch ? titleMatch[1].trim() : title;
        const content = `${parsedOverview.setup}

### Add integration
Once you have an access token, you're ready to set up your Superblocks integration.

1. In the web app, navigate to the [Integrations](https://app.superblocks.com/integrations) page
2. Click on the **${specTitle}** tile
3. **Name** the integration 
4. Paste your credentials into the relevant fields
5. Optionally, add more configurations to set credentials for [different environments](/development-lifecycle/build/profiles)
6. Click **Create**`;
        setSetupContent(content);
        setError(null);
      } catch (err) {
        setError(err.message);
        setSetupContent(null);
      } finally {
        setLoading(false);
      }
    };
    fetchIntegrationSpec();
  }, [integrationId, title]);
  const parseOverview = overview => {
    const startIndex = overview.indexOf('## Get started');
    if (startIndex === -1) {
      const nextSectionMatch = overview.match(/\n##\s+/);
      if (!nextSectionMatch) {
        return {
          description: overview.trim(),
          setup: null,
          usage: null
        };
      } else {
        const nextSectionIndex = nextSectionMatch.index;
        return {
          description: overview.slice(0, nextSectionIndex).trim(),
          setup: null,
          usage: overview.slice(nextSectionIndex).trim()
        };
      }
    }
    const lines = overview.split('\n');
    let getStartedLineIndex = -1;
    for (let i = 0; i < lines.length; i++) {
      if (lines[i].trim().startsWith('## Get started')) {
        getStartedLineIndex = i;
        break;
      }
    }
    if (getStartedLineIndex === -1) {
      return {
        description: overview.slice(0, startIndex).trim(),
        setup: overview.slice(startIndex).replace('## Get started', '### Create an access token').trim(),
        usage: null
      };
    }
    let nextSectionLineIndex = -1;
    for (let i = getStartedLineIndex + 1; i < lines.length; i++) {
      const trimmedLine = lines[i].trim();
      if (trimmedLine.startsWith('##') && !trimmedLine.includes('Get started')) {
        nextSectionLineIndex = i;
        break;
      }
    }
    let endIndex;
    if (nextSectionLineIndex === -1) {
      endIndex = overview.length;
    } else {
      let currentPos = 0;
      for (let i = 0; i < nextSectionLineIndex; i++) {
        currentPos += lines[i].length + 1;
      }
      endIndex = currentPos;
    }
    return {
      description: overview.slice(0, startIndex).trim(),
      setup: overview.slice(startIndex, endIndex).replace('## Get started', '### Create an access token').trim(),
      usage: overview.slice(endIndex).trim()
    };
  };
  if (loading) {
    return <div>
        <div style={{
      height: '20px',
      backgroundColor: '#e1e5e9',
      borderRadius: '4px',
      marginBottom: '16px',
      width: '40%'
    }}></div>
        <div style={{
      height: '16px',
      backgroundColor: '#e1e5e9',
      borderRadius: '4px',
      marginBottom: '8px'
    }}></div>
        <div style={{
      height: '16px',
      backgroundColor: '#e1e5e9',
      borderRadius: '4px',
      marginBottom: '8px'
    }}></div>
        <div style={{
      height: '16px',
      backgroundColor: '#e1e5e9',
      borderRadius: '4px',
      marginBottom: '8px'
    }}></div>
        <div style={{
      height: '16px',
      backgroundColor: '#e1e5e9',
      borderRadius: '4px',
      width: '60%'
    }}></div>
      </div>;
  }
  if (error) {
    return <div style={{
      padding: '12px',
      backgroundColor: '#fee',
      border: '1px solid #fcc',
      borderRadius: '4px',
      color: '#c00'
    }}>
        <strong>Error parsing setup:</strong> {error}
      </div>;
  }
  if (!setupContent) {
    return <div style={{
      padding: '12px',
      backgroundColor: '#fee',
      border: '1px solid #fcc',
      borderRadius: '4px',
      color: '#c00'
    }}>
        <strong>Error:</strong> No setup content found for {integrationId}
      </div>;
  }
  const renderMarkdown = content => {
    const lines = content.split('\n');
    const elements = [];
    let currentOrderedList = [];
    let currentUnorderedList = [];
    let lastOrderedItemIndex = -1;
    let lastElementWasHeader = false;
    for (let i = 0; i < lines.length; i++) {
      const originalLine = lines[i];
      const line = originalLine.trim();
      const indentLevel = originalLine.length - originalLine.trimStart().length;
      if (!line) {
        if (currentOrderedList.length > 0) {
          elements.push(<ol key={`ol-${i}`}>{currentOrderedList}</ol>);
          currentOrderedList = [];
          lastOrderedItemIndex = -1;
          lastElementWasHeader = false;
        }
        if (currentUnorderedList.length > 0) {
          elements.push(<ul key={`ul-${i}`}>{currentUnorderedList}</ul>);
          currentUnorderedList = [];
          lastElementWasHeader = false;
        }
        let nextNonEmptyLine = null;
        for (let j = i + 1; j < lines.length; j++) {
          const nextLine = lines[j].trim();
          if (nextLine) {
            nextNonEmptyLine = nextLine;
            break;
          }
        }
        const isHeader = nextNonEmptyLine && (nextNonEmptyLine.startsWith('# ') || nextNonEmptyLine.startsWith('## ') || nextNonEmptyLine.startsWith('### '));
        const isLearnMore = nextNonEmptyLine && nextNonEmptyLine.toLowerCase().includes('learn more about');
        const noteCheckText = nextNonEmptyLine.replace(/\*\*/g, '').toLowerCase();
        const isNote = nextNonEmptyLine && (noteCheckText.startsWith('note:') || noteCheckText.includes('note:'));
        if (isNote) {
          elements.push(<br key={`br-before-note-${i}`} />);
        } else if (!lastElementWasHeader && !isHeader && !isLearnMore) {
          elements.push(<br key={`br-${i}`} />);
        }
        continue;
      }
      if (line.startsWith('### ')) {
        if (currentOrderedList.length > 0) {
          elements.push(<ol key={`ol-${i}`}>{currentOrderedList}</ol>);
          currentOrderedList = [];
          lastOrderedItemIndex = -1;
        }
        if (currentUnorderedList.length > 0) {
          elements.push(<ul key={`ul-${i}`}>{currentUnorderedList}</ul>);
          currentUnorderedList = [];
        }
        elements.push(<h3 key={`h3-${i}`}>{line.substring(4)}</h3>);
        lastElementWasHeader = true;
        continue;
      }
      if (line.startsWith('## ')) {
        if (currentOrderedList.length > 0) {
          elements.push(<ol key={`ol-${i}`}>{currentOrderedList}</ol>);
          currentOrderedList = [];
          lastOrderedItemIndex = -1;
        }
        if (currentUnorderedList.length > 0) {
          elements.push(<ul key={`ul-${i}`}>{currentUnorderedList}</ul>);
          currentUnorderedList = [];
        }
        elements.push(<h2 key={`h2-${i}`}>{line.substring(3)}</h2>);
        lastElementWasHeader = true;
        continue;
      }
      if (line.startsWith('# ')) {
        if (currentOrderedList.length > 0) {
          elements.push(<ol key={`ol-${i}`}>{currentOrderedList}</ol>);
          currentOrderedList = [];
          lastOrderedItemIndex = -1;
        }
        if (currentUnorderedList.length > 0) {
          elements.push(<ul key={`ul-${i}`}>{currentUnorderedList}</ul>);
          currentUnorderedList = [];
        }
        elements.push(<h1 key={`h1-${i}`}>{line.substring(2)}</h1>);
        lastElementWasHeader = true;
        continue;
      }
      const trimmedLineForOrdered = line.trimStart();
      if (trimmedLineForOrdered.match(/^\d+\.\s/)) {
        if (currentUnorderedList.length > 0 && lastOrderedItemIndex >= 0) {
          const nestedList = <ul key={`nested-ul-${i}`}>{currentUnorderedList}</ul>;
          const lastItem = currentOrderedList[lastOrderedItemIndex];
          currentOrderedList[lastOrderedItemIndex] = <li key={`li-nested-${lastOrderedItemIndex}`}>
              <span dangerouslySetInnerHTML={{
            __html: lastItem.props.dangerouslySetInnerHTML.__html
          }} />
              {nestedList}
            </li>;
          currentUnorderedList = [];
        }
        const listItem = trimmedLineForOrdered.replace(/^\d+\.\s/, '');
        const processedItem = processInlineMarkdown(listItem);
        currentOrderedList.push(<li key={`li-${i}`} dangerouslySetInnerHTML={{
          __html: processedItem
        }} />);
        lastOrderedItemIndex = currentOrderedList.length - 1;
        lastElementWasHeader = false;
        continue;
      }
      const trimmedLine = line.trimStart();
      if (trimmedLine.startsWith('- ') || trimmedLine.startsWith('* ')) {
        if (indentLevel > 0 && currentOrderedList.length > 0) {
          const listItem = trimmedLine.substring(2);
          const processedItem = processInlineMarkdown(listItem);
          currentUnorderedList.push(<li key={`li-${i}`} dangerouslySetInnerHTML={{
            __html: processedItem
          }} />);
          lastElementWasHeader = false;
          continue;
        }
        const listItem = trimmedLine.substring(2);
        const processedItem = processInlineMarkdown(listItem);
        currentUnorderedList.push(<li key={`li-${i}`} dangerouslySetInnerHTML={{
          __html: processedItem
        }} />);
        lastElementWasHeader = false;
        continue;
      }
      if (currentUnorderedList.length > 0 && lastOrderedItemIndex >= 0 && currentOrderedList.length > 0) {
        const nestedList = <ul key={`nested-ul-${i}`}>{currentUnorderedList}</ul>;
        const lastItem = currentOrderedList[lastOrderedItemIndex];
        currentOrderedList[lastOrderedItemIndex] = <li key={`li-nested-${lastOrderedItemIndex}`}>
            <span dangerouslySetInnerHTML={{
          __html: lastItem.props.dangerouslySetInnerHTML.__html
        }} />
            {nestedList}
          </li>;
        currentUnorderedList = [];
        lastOrderedItemIndex = -1;
      }
      if (currentOrderedList.length > 0) {
        elements.push(<ol key={`ol-${i}`}>{currentOrderedList}</ol>);
        currentOrderedList = [];
        lastOrderedItemIndex = -1;
      }
      if (currentUnorderedList.length > 0) {
        elements.push(<ul key={`ul-${i}`}>{currentUnorderedList}</ul>);
        currentUnorderedList = [];
      }
      const processedLine = processInlineMarkdown(line);
      elements.push(<p key={`p-${i}`} dangerouslySetInnerHTML={{
        __html: processedLine
      }} />);
      lastElementWasHeader = false;
    }
    if (currentUnorderedList.length > 0 && lastOrderedItemIndex >= 0 && currentOrderedList.length > 0) {
      const nestedList = <ul key="nested-ul-final">{currentUnorderedList}</ul>;
      const lastItem = currentOrderedList[lastOrderedItemIndex];
      currentOrderedList[lastOrderedItemIndex] = <li key={`li-nested-final-${lastOrderedItemIndex}`}>
          <span dangerouslySetInnerHTML={{
        __html: lastItem.props.dangerouslySetInnerHTML.__html
      }} />
          {nestedList}
        </li>;
      currentUnorderedList = [];
    }
    if (currentOrderedList.length > 0) {
      elements.push(<ol key="ol-final">{currentOrderedList}</ol>);
    }
    if (currentUnorderedList.length > 0) {
      elements.push(<ul key="ul-final">{currentUnorderedList}</ul>);
    }
    return elements;
  };
  const processInlineMarkdown = text => {
    return text.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>').replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>').replace(/`([^`]+)`/g, '<code>$1</code>');
  };
  return <div>
      {setupContent && renderMarkdown(setupContent)}
    </div>;
};

export const DynamicIntegrationOverview = ({integrationId}) => {
  const [overview, setOverview] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  useEffect(() => {
    const fetchIntegrationSpec = async () => {
      try {
        setLoading(true);
        const response = await fetch(`https://integrations.superblocks.com/openapi/${integrationId}.yaml`);
        if (!response.ok) {
          throw new Error(`Failed to fetch integration spec: ${response.status}`);
        }
        const yamlText = await response.text();
        const infoStartIndex = yamlText.indexOf('\ninfo:');
        if (infoStartIndex === -1) {
          const altIndex = yamlText.indexOf('info:');
          if (altIndex === 0) {
            const afterInfo = yamlText.slice(5);
            const lines = afterInfo.split('\n');
            let infoEndIndex = afterInfo.length;
            for (let i = 0; i < lines.length; i++) {
              const line = lines[i];
              if (line.trim() && !line.match(/^\s/)) {
                infoEndIndex = afterInfo.indexOf(line);
                break;
              }
            }
            var infoSection = afterInfo.slice(0, infoEndIndex).trim();
          } else {
            throw new Error('Could not find info section in YAML');
          }
        } else {
          const afterInfo = yamlText.slice(infoStartIndex + 6);
          const lines = afterInfo.split('\n');
          let infoEndIndex = afterInfo.length;
          for (let i = 0; i < lines.length; i++) {
            const line = lines[i];
            if (line.trim() && !line.match(/^\s/)) {
              infoEndIndex = afterInfo.indexOf(line);
              break;
            }
          }
          var infoSection = afterInfo.slice(0, infoEndIndex).trim();
        }
        let overviewText = null;
        const multilineMatchDash = infoSection.match(/description:\s*\|\-\s*\n([\s\S]*?)(?=\n\s+(?:termsOfService|version|contact|license|x-public-description):|$)/);
        if (multilineMatchDash) {
          overviewText = multilineMatchDash[1].trim();
        } else {
          const multilineMatch = infoSection.match(/description:\s*\|\s*\n([\s\S]*?)(?=\n\s+(?:termsOfService|version|contact|license|x-public-description):|$)/);
          if (multilineMatch) {
            overviewText = multilineMatch[1].trim();
          } else {
            const simpleMatch = infoSection.match(/description:\s*([\s\S]*?)(?=\n\s+(?:termsOfService|version|contact|license|x-public-description):|$)/);
            if (simpleMatch) {
              overviewText = simpleMatch[1].trim();
            }
          }
        }
        if (!overviewText) {
          throw new Error('Could not extract description/overview from YAML spec');
        }
        const parsedOverview = parseOverview(overviewText);
        if (!parsedOverview.description) {
          throw new Error('Could not parse description from overview');
        }
        setOverview({
          description: parsedOverview.description
        });
        setError(null);
      } catch (err) {
        setError(err.message);
        setOverview(null);
      } finally {
        setLoading(false);
      }
    };
    fetchIntegrationSpec();
  }, [integrationId]);
  const parseOverview = overview => {
    const HEADER_OFFSET = 3;
    const startIndex = overview.indexOf('## Get started');
    let endIndex = overview.slice(startIndex + HEADER_OFFSET).indexOf("\n##");
    if (startIndex === -1 && endIndex === -1) {
      return {
        description: overview.trim(),
        setup: null,
        usage: null
      };
    } else if (startIndex === -1) {
      endIndex = endIndex + startIndex + HEADER_OFFSET;
      return {
        description: overview.slice(0, endIndex).trim(),
        setup: null,
        usage: overview.slice(endIndex).trim()
      };
    } else if (endIndex === -1) {
      return {
        description: overview.slice(0, startIndex).trim(),
        setup: overview.slice(startIndex).replace('## Get started', '### Create an access token').trim(),
        usage: null
      };
    } else {
      endIndex = endIndex + startIndex + HEADER_OFFSET;
      return {
        description: overview.slice(0, startIndex).trim(),
        setup: overview.slice(startIndex, endIndex).replace('## Get started', '### Create an access token').trim(),
        usage: overview.slice(endIndex).trim()
      };
    }
  };
  if (loading) {
    return <div>
        <div style={{
      height: '20px',
      backgroundColor: '#e1e5e9',
      borderRadius: '4px',
      marginBottom: '16px',
      width: '60%'
    }}></div>
        <div style={{
      height: '16px',
      backgroundColor: '#e1e5e9',
      borderRadius: '4px',
      marginBottom: '8px'
    }}></div>
        <div style={{
      height: '16px',
      backgroundColor: '#e1e5e9',
      borderRadius: '4px',
      marginBottom: '8px',
      width: '80%'
    }}></div>
        <div style={{
      height: '16px',
      backgroundColor: '#e1e5e9',
      borderRadius: '4px',
      width: '70%'
    }}></div>
      </div>;
  }
  if (error) {
    return <div style={{
      padding: '12px',
      backgroundColor: '#fee',
      border: '1px solid #fcc',
      borderRadius: '4px',
      color: '#c00'
    }}>
        <strong>Error parsing overview:</strong> {error}
      </div>;
  }
  if (!overview || !overview.description) {
    return <div style={{
      padding: '12px',
      backgroundColor: '#fee',
      border: '1px solid #fcc',
      borderRadius: '4px',
      color: '#c00'
    }}>
        <strong>Error:</strong> No overview description found for {integrationId}
      </div>;
  }
  return <div>
      <p>{overview.description}</p>
    </div>;
};

export const Alert = ({type, title, children}) => {
  const getIcon = () => {
    switch (type) {
      case 'info':
        return "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20' fill='none'%3E%3Cpath d='M10 0C4.477 0 0 4.477 0 10s4.477 10 10 10 10-4.477 10-10S15.523 0 10 0zm0 15c-.552 0-1-.448-1-1s.448-1 1-1 1 .448 1 1-.448 1-1 1zm1-3H9V6h2v6z' fill='%230099FF'/%3E%3C/svg%3E";
      case 'success':
        return "data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 20 20' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M10 0C4.477 0 0 4.477 0 10s4.477 10 10 10 10-4.477 10-10S15.523 0 10 0zm4.293 6.293L9 11.586 5.707 8.293c-.391-.391-1.024-.391-1.414 0s-.391 1.024 0 1.414l4 4c.391.391 1.024.391 1.414 0l6-6c.391-.391.391-1.024 0-1.414s-1.024-.391-1.414 0z' fill='%230CC26D'/%3E%3C/svg%3E";
      case 'warning':
        return "data:image/svg+xml;charset=utf-8;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHhtbDpzcGFjZT0ncHJlc2VydmUnIHdpZHRoPScxMDgwJyBoZWlnaHQ9JzEwODAnPjxyZWN0IHdpZHRoPScxMDAlJyBoZWlnaHQ9JzEwMCUnIGZpbGw9J3RyYW5zcGFyZW50Jy8+PHBhdGggZD0nTTEzLjc5NCAxMC43NSA4LjMgMS4yNWExLjUgMS41IDAgMCAwLTIuNiAwbC01LjQ5NCA5LjVBMS40OTQgMS40OTQgMCAwIDAgMS41IDEzaDExYTEuNDkzIDEuNDkzIDAgMCAwIDEuMjk0LTIuMjVNNi41IDUuNWEuNS41IDAgMCAxIDEgMFY4YS41LjUgMCAwIDEtMSAwek03IDExYS43NS43NSAwIDEgMSAwLTEuNS43NS43NSAwIDAgMSAwIDEuNScgc3R5bGU9J3N0cm9rZTpub25lO3N0cm9rZS13aWR0aDoxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1kYXNob2Zmc2V0OjA7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7ZmlsbDojZmY5ZjM1O2ZpbGwtcnVsZTpub256ZXJvO29wYWNpdHk6MScgdHJhbnNmb3JtPSd0cmFuc2xhdGUoLjAyIDE5LjMwNSlzY2FsZSg3Ny4xNCknLz48L3N2Zz4=";
      case 'danger':
        return "data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 20 20' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M10 0C4.477 0 0 4.477 0 10s4.477 10 10 10 10-4.477 10-10S15.523 0 10 0zm5.707 4.293L10 9.586 4.293 4.293c-.391-.391-1.024-.391-1.414 0s-.391 1.024 0 1.414L8.586 11l-5.707 5.293c-.391.391-.391 1.024 0 1.414s1.024.391 1.414 0L10 12.414l5.707 5.293c.391.391 1.024.391 1.414 0s.391-1.024 0-1.414L11.414 11l5.707-5.293c.391-.391.391-1.024 0-1.414s-1.024-.391-1.414 0z' fill='%23F45252'/%3E%3C/svg%3E";
      case 'note':
        return "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20' fill='none'%3E%3Cpath d='M10 0C4.477 0 0 4.477 0 10s4.477 10 10 10 10-4.477 10-10S15.523 0 10 0zm0 15c-.552 0-1-.448-1-1s.448-1 1-1 1 .448 1 1-.448 1-1 1zm1-3H9V6h2v6z' fill='%230099FF'/%3E%3C/svg%3E";
      default:
        return "";
    }
  };
  return <div className={`alert alert--${type}`}>
      <div className="alert-icon" style={{
    backgroundImage: `url("${getIcon()}")`,
    backgroundRepeat: 'no-repeat',
    backgroundPosition: 'center center',
    backgroundSize: '20px',
    width: '24px',
    height: '24px',
    position: 'absolute',
    left: '16px',
    top: '16px'
  }} />
      <div className="alert-content">
        {title && <div className="alert-title">{title}</div>}
        <div className="alert-body">{children}</div>
      </div>
    </div>;
};

<DynamicIntegrationOverview integrationId="googledrive" />

## Setting up Google Drive

<DynamicIntegrationSetup integrationId="googledrive" title="Google Drive" />

<Alert type="success">
  <IntegrationSuccess integrationName="Google Drive" />
</Alert>

## Use Google Drive in APIs

Once your Google Drive integration is created, you can start calling Google Drive actions in Superblocks APIs. In addition to generic HTTP requests, Google Drive supports the following actions.

### Supported actions

<DynamicIntegrationActions integrationId="googledrive" maxActions={15} />
