Add Activepieces integration for workflow automation

- Add Activepieces fork with SmoothSchedule custom piece
- Create integrations app with Activepieces service layer
- Add embed token endpoint for iframe integration
- Create Automations page with embedded workflow builder
- Add sidebar visibility fix for embed mode
- Add list inactive customers endpoint to Public API
- Include SmoothSchedule triggers: event created/updated/cancelled
- Include SmoothSchedule actions: create/update/cancel events, list resources/services/customers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
poduck
2025-12-18 22:59:37 -05:00
parent 9848268d34
commit 3aa7199503
16292 changed files with 1284892 additions and 4708 deletions

View File

@@ -0,0 +1,28 @@
{
"No-code platform for web and mobile apps": "No-Code-Plattform für Web- und mobile Apps",
"Create Thing": "Ding erstellen",
"Delete Thing": "Ding löschen",
"Update Thing": "Ding aktualisieren",
"Get Thing": "Hole Ding",
"List Thing": "Listenansicht",
"Create a thing": "Erstelle eine Sache",
"Delete a thing": "Eine Sache löschen",
"Updates a thing": "Aktualisiert eine Sache",
"Get a thing by id": "Erhalte eine Sache durch Id",
"List things by type": "Dinge nach Typ auflisten",
"Typename": "Typename",
"Body": "Körper",
"Thing ID": "Ding ID",
"Constraint": "Einschränkung",
"Field": "Feld",
"Value": "Wert",
"Start from": "Start von",
"Limit": "Limit",
"equals or not equal": "gleich oder nicht gleich",
"text contains or not text contains": "text enthält oder nicht Text",
"greater than or less than": "größer als oder kleiner als",
"in or not in": "in oder nicht in",
"contains or not contains": "enthält oder nicht enthält",
"empty or not empty": "leer oder nicht leer",
"geographic_search": "geografisch_Suche"
}

View File

@@ -0,0 +1,28 @@
{
"No-code platform for web and mobile apps": "Plataforma sin código para aplicaciones web y móviles",
"Create Thing": "Crear cosa",
"Delete Thing": "Eliminar cosa",
"Update Thing": "Actualizar asunto",
"Get Thing": "Obtener cosa",
"List Thing": "Cosa de lista",
"Create a thing": "Crear una cosa",
"Delete a thing": "Eliminar una cosa",
"Updates a thing": "Actualiza una cosa",
"Get a thing by id": "Obtener una cosa por id",
"List things by type": "Listar cosas por tipo",
"Typename": "Typename",
"Body": "Cuerpo",
"Thing ID": "ID de Cosa",
"Constraint": "Restricción",
"Field": "Campo",
"Value": "Valor",
"Start from": "Empezar desde",
"Limit": "Límite",
"equals or not equal": "igual o no igual",
"text contains or not text contains": "el texto contiene o no contiene texto",
"greater than or less than": "mayor o menor que",
"in or not in": "en o no en",
"contains or not contains": "contiene o no contiene",
"empty or not empty": "vacío o no vacío",
"geographic_search": "búsqueda geográfica"
}

View File

@@ -0,0 +1,28 @@
{
"No-code platform for web and mobile apps": "Plateforme sans code pour les applications web et mobile",
"Create Thing": "Créer une chose",
"Delete Thing": "Supprimer la chose",
"Update Thing": "Mettre à jour la chose",
"Get Thing": "Obtenir des trucs",
"List Thing": "Lister les choses",
"Create a thing": "Créer une chose",
"Delete a thing": "Supprimer une chose",
"Updates a thing": "Met à jour une chose",
"Get a thing by id": "Obtenir une chose par id",
"List things by type": "Lister les choses par type",
"Typename": "Typename",
"Body": "Corps",
"Thing ID": "ID de la chose",
"Constraint": "Contrainte",
"Field": "Champ",
"Value": "Valeur",
"Start from": "Commencer à partir de",
"Limit": "Limite",
"equals or not equal": "est égal ou non égal",
"text contains or not text contains": "le texte contient ou non du texte",
"greater than or less than": "supérieur ou inférieur à",
"in or not in": "dans ou non dans",
"contains or not contains": "contient ou ne contient pas",
"empty or not empty": "vide ou non vide",
"geographic_search": "recherche géographique"
}

View File

@@ -0,0 +1,28 @@
{
"No-code platform for web and mobile apps": "ウェブおよびモバイルアプリ用のコードのないプラットフォーム",
"Create Thing": "物事を作成",
"Delete Thing": "Thing を削除する",
"Update Thing": "物事を更新",
"Get Thing": "物事を入手",
"List Thing": "物事一覧",
"Create a thing": "Thing を作成",
"Delete a thing": "Thing を削除する",
"Updates a thing": "Thing を更新",
"Get a thing by id": "ID で Thing を取得する",
"List things by type": "種類別に物事をリスト",
"Typename": "Typename",
"Body": "本文",
"Thing ID": "Thing ID",
"Constraint": "制約",
"Field": "フィールド",
"Value": "値",
"Start from": "開始",
"Limit": "制限",
"equals or not equal": "等しいか等しくないか",
"text contains or not text contains": "テキストに含まれるか含まないか",
"greater than or less than": "以下より大きい",
"in or not in": "中か中かどうかは",
"contains or not contains": "contains or not contains",
"empty or not empty": "空か空か",
"geographic_search": "地理検索"
}

View File

@@ -0,0 +1,28 @@
{
"No-code platform for web and mobile apps": "Geen code platform voor web- en mobiele apps",
"Create Thing": "Thing maken",
"Delete Thing": "Verwijder Thing",
"Update Thing": "Ding bijwerken",
"Get Thing": "Dingen ophalen",
"List Thing": "Lijst Thing",
"Create a thing": "Maak iets aan",
"Delete a thing": "Een ding verwijderen",
"Updates a thing": "Werkt iets bij",
"Get a thing by id": "Pak iets op id",
"List things by type": "Toon dingen per type",
"Typename": "Typename",
"Body": "Lichaam",
"Thing ID": "Ding ID",
"Constraint": "Beperking",
"Field": "Veld",
"Value": "Waarde",
"Start from": "Begint vanaf",
"Limit": "Limiet",
"equals or not equal": "gelijk aan of niet gelijk aan",
"text contains or not text contains": "tekst bevat of bevat geen tekst",
"greater than or less than": "groter dan of minder dan",
"in or not in": "binnen of niet in",
"contains or not contains": "bevat of niet",
"empty or not empty": "leeg of niet leeg",
"geographic_search": "geografisch zoeken"
}

View File

@@ -0,0 +1,28 @@
{
"No-code platform for web and mobile apps": "Plataforma sem código para aplicativos web e móveis",
"Create Thing": "Criar algo",
"Delete Thing": "Excluir Coisas",
"Update Thing": "Atualizar Coisas",
"Get Thing": "Pegar Coisas",
"List Thing": "Listar Coisas",
"Create a thing": "Criar uma coisa",
"Delete a thing": "Apagar uma coisa",
"Updates a thing": "Atualiza uma coisa",
"Get a thing by id": "Fazer uma coisa por id",
"List things by type": "Listar coisas por tipo",
"Typename": "Typename",
"Body": "Conteúdo",
"Thing ID": "Coisa ID",
"Constraint": "Restrição",
"Field": "Campo",
"Value": "Valor",
"Start from": "Começar de",
"Limit": "Limitar",
"equals or not equal": "igual ou diferente de",
"text contains or not text contains": "o texto contém ou não contém texto",
"greater than or less than": "maior ou menor que",
"in or not in": "dentro ou não em",
"contains or not contains": "contém ou não contém",
"empty or not empty": "vazio ou não vazio",
"geographic_search": "pesquisa_geográfica"
}

View File

@@ -0,0 +1,29 @@
{
"Bubble": "Пузырь",
"No-code platform for web and mobile apps": "Нет кодовой платформы для веб-и мобильных приложений",
"Create Thing": "Создать вещь",
"Delete Thing": "Удалить вещи",
"Update Thing": "Обновить вещи",
"Get Thing": "Получить вещи",
"List Thing": "Список вещей",
"Create a thing": "Создать вещь",
"Delete a thing": "Удалить вещи",
"Updates a thing": "Обновляет вещи",
"Get a thing by id": "Получить вещь по id",
"List things by type": "Список по типу",
"Typename": "Typename",
"Body": "Тело",
"Thing ID": "Вещь ID",
"Constraint": "Ограничение",
"Field": "Поле",
"Value": "Значение",
"Start from": "Начать с",
"Limit": "Лимит",
"equals or not equal": "равно или не равно",
"text contains or not text contains": "текст содержит или нет текст",
"greater than or less than": "больше или меньше",
"in or not in": "в или нет",
"contains or not contains": "содержит или не содержит",
"empty or not empty": "пустой или не пустой",
"geographic_search": "географический поиск"
}

View File

@@ -0,0 +1,28 @@
{
"No-code platform for web and mobile apps": "No-code platform for web and mobile apps",
"Create Thing": "Create Thing",
"Delete Thing": "Delete Thing",
"Update Thing": "Update Thing",
"Get Thing": "Get Thing",
"List Thing": "List Thing",
"Create a thing": "Create a thing",
"Delete a thing": "Delete a thing",
"Updates a thing": "Updates a thing",
"Get a thing by id": "Get a thing by id",
"List things by type": "List things by type",
"Typename": "Typename",
"Body": "Body",
"Thing ID": "Thing ID",
"Constraint": "Constraint",
"Field": "Field",
"Value": "Value",
"Start from": "Start from",
"Limit": "Limit",
"equals or not equal": "equals or not equal",
"text contains or not text contains": "text contains or not text contains",
"greater than or less than": "greater than or less than",
"in or not in": "in or not in",
"contains or not contains": "contains or not contains",
"empty or not empty": "empty or not empty",
"geographic_search": "geographic_search"
}

View File

@@ -0,0 +1,29 @@
{
"Bubble": "Bubble",
"No-code platform for web and mobile apps": "No-code platform for web and mobile apps",
"Create Thing": "Create Thing",
"Delete Thing": "Delete Thing",
"Update Thing": "Update Thing",
"Get Thing": "Get Thing",
"List Thing": "List Thing",
"Create a thing": "Create a thing",
"Delete a thing": "Delete a thing",
"Updates a thing": "Updates a thing",
"Get a thing by id": "Get a thing by id",
"List things by type": "List things by type",
"Typename": "Typename",
"Body": "Body",
"Thing ID": "Thing ID",
"Constraint": "Constraint",
"Field": "Field",
"Value": "Value",
"Start from": "Start from",
"Limit": "Limit",
"equals or not equal": "equals or not equal",
"text contains or not text contains": "text contains or not text contains",
"greater than or less than": "greater than or less than",
"in or not in": "in or not in",
"contains or not contains": "contains or not contains",
"empty or not empty": "empty or not empty",
"geographic_search": "geographic_search"
}

View File

@@ -0,0 +1,28 @@
{
"No-code platform for web and mobile apps": "No-code platform for web and mobile apps",
"Create Thing": "Create Thing",
"Delete Thing": "Delete Thing",
"Update Thing": "Update Thing",
"Get Thing": "Get Thing",
"List Thing": "List Thing",
"Create a thing": "Create a thing",
"Delete a thing": "Delete a thing",
"Updates a thing": "Updates a thing",
"Get a thing by id": "Get a thing by id",
"List things by type": "List things by type",
"Typename": "Typename",
"Body": "正文内容",
"Thing ID": "Thing ID",
"Constraint": "Constraint",
"Field": "字段",
"Value": "值",
"Start from": "Start from",
"Limit": "Limit",
"equals or not equal": "equals or not equal",
"text contains or not text contains": "text contains or not text contains",
"greater than or less than": "greater than or less than",
"in or not in": "in or not in",
"contains or not contains": "contains or not contains",
"empty or not empty": "empty or not empty",
"geographic_search": "geographic_search"
}

View File

@@ -0,0 +1,64 @@
import {
createPiece,
PieceAuth,
Property,
} from '@activepieces/pieces-framework';
import { PieceCategory } from '@activepieces/shared';
import { bubbleCreateThingAction } from './lib/actions/create-thing';
import { bubbleDeleteThingAction } from './lib/actions/delete-thing';
import { bubbleGetThingAction } from './lib/actions/get-thing';
import { bubbleListThingsAction } from './lib/actions/list-things';
import { bubbleUpdateThingAction } from './lib/actions/update-thing';
export const bubbleAuth = PieceAuth.CustomAuth({
description: `Enter Bubble Connection Details
In the bubble editor click Settings > API
1. Your app name is https://appname.bubbleapps.io
2. Enter/Generate an API key
`,
props: {
appname: Property.ShortText({
displayName: 'App name',
description: 'Enter the app name',
required: true,
}),
token: PieceAuth.SecretText({
displayName: 'API Token',
description: 'Enter the access token',
required: true,
}),
},
// Optional Validation
validate: async ({ auth }) => {
if (auth) {
return {
valid: true,
};
}
return {
valid: false,
error: 'Please enter a valid app name and token',
};
},
required: true,
});
export const bubble = createPiece({
displayName: 'Bubble',
description: 'No-code platform for web and mobile apps',
auth: PieceAuth.None(),
minimumSupportedRelease: '0.30.0',
logoUrl: 'https://cdn.activepieces.com/pieces/bubble.png',
categories: [PieceCategory.DEVELOPER_TOOLS],
authors: ["TaskMagicKyle","kishanprmr","abuaboud"],
actions: [
bubbleCreateThingAction,
bubbleDeleteThingAction,
bubbleUpdateThingAction,
bubbleGetThingAction,
bubbleListThingsAction,
],
triggers: [],
});

View File

@@ -0,0 +1,42 @@
import { createAction } from '@activepieces/pieces-framework';
import {
AuthenticationType,
httpClient,
HttpMethod,
} from '@activepieces/pieces-common';
import { bubbleAuth } from '../../index';
import { bubbleCommon } from '../common';
export const bubbleCreateThingAction = createAction({
auth: bubbleAuth,
name: 'bubble_create_thing',
displayName: 'Create Thing',
description: 'Create a thing',
props: {
typename: bubbleCommon.typename,
fields: bubbleCommon.fields,
},
async run(context) {
const { appname, token } = context.auth.props;
const { typename, fields } = context.propsValue;
const server_url = `https://${appname}.bubbleapps.io/api/1.1/obj/${typename}`;
const response = await httpClient.sendRequest({
method: HttpMethod.POST,
url: server_url,
headers: {
'user-agent': 'activepieces',
Authorization: `Bearer ${token}`,
},
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token,
},
body: fields,
});
return response.body;
},
});

View File

@@ -0,0 +1,41 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import {
AuthenticationType,
httpClient,
HttpMethod,
} from '@activepieces/pieces-common';
import { bubbleAuth } from '../../index';
import { bubbleCommon } from '../common';
export const bubbleDeleteThingAction = createAction({
auth: bubbleAuth,
name: 'bubble_delete_thing',
displayName: 'Delete Thing',
description: 'Delete a thing',
props: {
typename: bubbleCommon.typename,
thing_id: bubbleCommon.thing_id,
},
async run(context) {
const { appname, token } = context.auth.props;
const { typename, thing_id } = context.propsValue;
const server_url = `https://${appname}.bubbleapps.io/api/1.1/obj/${typename}/${thing_id}`;
const response = await httpClient.sendRequest({
method: HttpMethod.DELETE,
url: server_url,
headers: {
'user-agent': 'activepieces',
Authorization: `Bearer ${token}`,
},
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token,
},
});
return response.body;
},
});

View File

@@ -0,0 +1,44 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import {
AuthenticationType,
httpClient,
HttpMethod,
} from '@activepieces/pieces-common';
import { bubbleAuth } from '../../index';
import { bubbleCommon } from '../common';
export const bubbleGetThingAction = createAction({
auth: bubbleAuth,
name: 'bubble_get_thing',
displayName: 'Get Thing',
description: 'Get a thing by id',
props: {
typename: bubbleCommon.typename,
thing_id: bubbleCommon.thing_id,
},
async run(context) {
const { appname, token } = context.auth.props;
const { typename, thing_id } = context.propsValue;
const server_url = `https://${appname}.bubbleapps.io/api/1.1/obj/${typename}`;
const response = await httpClient.sendRequest({
method: HttpMethod.GET,
url: server_url,
headers: {
'user-agent': 'activepieces',
Authorization: `Bearer ${token}`,
},
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token,
},
body: {
constraint: `id = ${thing_id}`,
},
});
return response.body;
},
});

View File

@@ -0,0 +1,101 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import {
AuthenticationType,
httpClient,
HttpMethod,
} from '@activepieces/pieces-common';
import { bubbleAuth } from '../../index';
import { bubbleCommon } from '../common';
export const bubbleListThingsAction = createAction({
auth: bubbleAuth,
name: 'bubble_list_things',
displayName: 'List Thing',
description: 'List things by type',
props: {
typename: bubbleCommon.typename,
constraint: Property.StaticDropdown({
displayName: 'Constraint',
required: true,
options: {
options: [
{
label: 'equals or not equal',
value: 'equals or not equal',
},
{
label: 'text contains or not text contains',
value: 'text contains or not text contains',
},
{
label: 'greater than or less than',
value: 'greater than or less than',
},
{
label: 'in or not in',
value: 'in or not in',
},
{
label: 'contains or not contains',
value: 'contains or not contains',
},
{
label: 'empty or not empty',
value: 'empty or not empty',
},
{
label: 'geographic_search',
value: 'geographic_search',
},
],
},
}),
field: Property.ShortText({
displayName: 'Field',
required: true,
}),
value: Property.ShortText({
displayName: 'Value',
required: true,
}),
cursor: Property.Number({
displayName: 'Start from',
required: true,
}),
limit: Property.Number({
displayName: 'Limit',
required: true,
}),
},
async run(context) {
const { appname, token } = context.auth.props;
const { typename, constraint, field, value, cursor, limit } =
context.propsValue;
const server_url = `https://${appname}.bubbleapps.io/api/1.1/obj/${typename}`;
const response = await httpClient.sendRequest({
method: HttpMethod.POST,
url: server_url,
headers: {
'user-agent': 'activepieces',
Authorization: `Bearer ${token}`,
},
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token,
},
body: {
constraint: constraint,
field: field,
value: value,
cursor: cursor,
limit: limit,
},
});
return response.body;
},
});

View File

@@ -0,0 +1,43 @@
import { createAction } from '@activepieces/pieces-framework';
import {
AuthenticationType,
httpClient,
HttpMethod,
} from '@activepieces/pieces-common';
import { bubbleAuth } from '../../index';
import { bubbleCommon } from '../common';
export const bubbleUpdateThingAction = createAction({
auth: bubbleAuth,
name: 'bubble_update_thing',
displayName: 'Update Thing',
description: 'Updates a thing',
props: {
typename: bubbleCommon.typename,
thing_id: bubbleCommon.thing_id,
fields: bubbleCommon.fields,
},
async run(context) {
const { appname, token } = context.auth.props;
const { typename, thing_id } = context.propsValue;
const server_url = `https://${appname}.bubbleapps.io/api/1.1/obj/${typename}/${thing_id}`;
const response = await httpClient.sendRequest({
method: HttpMethod.PATCH,
url: server_url,
headers: {
'user-agent': 'activepieces',
Authorization: `Bearer ${token}`,
},
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token,
},
body: context.propsValue.fields,
});
return response.body;
},
});

View File

@@ -0,0 +1,16 @@
import { Property } from '@activepieces/pieces-framework';
export const bubbleCommon = {
typename: Property.ShortText({
displayName: 'Typename',
required: true,
}),
fields: Property.Json({
displayName: 'Body',
required: false,
}),
thing_id: Property.ShortText({
displayName: 'Thing ID',
required: true,
}),
};