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:
@@ -0,0 +1,102 @@
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { HttpMethod, httpClient } from '@activepieces/pieces-common';
|
||||
import { cambaiAuth } from '../../index';
|
||||
import { API_BASE_URL, MAX_POLLING_ATTEMPTS, POLLING_INTERVAL_MS } from '../common';
|
||||
import { listFoldersDropdown } from '../common';
|
||||
|
||||
export const createTextToSound = createAction({
|
||||
auth: cambaiAuth,
|
||||
name: 'create_text_to_sound',
|
||||
displayName: 'Create Text-to-Sound',
|
||||
description: 'Convert input text into “sound effects” using an AI model.',
|
||||
props: {
|
||||
prompt: Property.LongText({
|
||||
displayName: 'Prompt',
|
||||
description: 'A clear, descriptive explanation of the desired audio effect. Concise prompts yield more accurate results.',
|
||||
required: true,
|
||||
}),
|
||||
duration: Property.Number({
|
||||
displayName: 'Duration (seconds)',
|
||||
description: 'The desired length of the audio in seconds (max 10). Defaults to 8 if not set.',
|
||||
required: false,
|
||||
}),
|
||||
project_name: Property.ShortText({
|
||||
displayName: 'Project Name',
|
||||
description: 'A memorable name for your project to help organize tasks in your Camb.ai workspace.',
|
||||
required: false,
|
||||
}),
|
||||
project_description: Property.LongText({
|
||||
displayName: 'Project Description',
|
||||
description: 'Provide details about your project\'s goals and specifications for documentation purposes.',
|
||||
required: false,
|
||||
}),
|
||||
folder_id: listFoldersDropdown,
|
||||
},
|
||||
async run(context) {
|
||||
const { auth } = context;
|
||||
const { prompt, duration, project_name, project_description, folder_id } = context.propsValue;
|
||||
|
||||
const payload: Record<string, unknown> = { prompt };
|
||||
if (duration) payload['duration'] = duration;
|
||||
if (project_name) payload['project_name'] = project_name;
|
||||
if (project_description) payload['project_description'] = project_description;
|
||||
if (folder_id) payload['folder_id'] = folder_id;
|
||||
|
||||
|
||||
const initialResponse = await httpClient.sendRequest<{ task_id: string }>({
|
||||
method: HttpMethod.POST,
|
||||
url: `${API_BASE_URL}/text-to-sound`,
|
||||
headers: {
|
||||
'x-api-key': auth.secret_text,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: payload,
|
||||
});
|
||||
|
||||
const taskId = initialResponse.body.task_id;
|
||||
let attempts = 0;
|
||||
let run_id: string | null = null;
|
||||
while (attempts < MAX_POLLING_ATTEMPTS) {
|
||||
const statusResponse = await httpClient.sendRequest<{
|
||||
status: string, run_id?: string
|
||||
}>({
|
||||
method: HttpMethod.GET,
|
||||
url: `${API_BASE_URL}/text-to-sound/${taskId}`,
|
||||
headers: {
|
||||
'x-api-key': auth.secret_text,
|
||||
},
|
||||
});
|
||||
|
||||
const status = statusResponse.body.status;
|
||||
|
||||
if (status === 'SUCCESS') {
|
||||
|
||||
run_id = statusResponse.body.run_id ?? null;
|
||||
break;
|
||||
}
|
||||
|
||||
if (status === 'FAILED') {
|
||||
|
||||
throw new Error(`Sound generation task failed: ${JSON.stringify(statusResponse.body)}`);
|
||||
}
|
||||
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, POLLING_INTERVAL_MS));
|
||||
attempts++;
|
||||
}
|
||||
|
||||
|
||||
if (!run_id) {
|
||||
throw new Error("Sound generation task timed out or failed to return a run_id.");
|
||||
}
|
||||
const audioResponse = await httpClient.sendRequest({
|
||||
method: HttpMethod.GET,
|
||||
url: `${API_BASE_URL}/text-to-sound-result/${run_id}`,
|
||||
headers: { 'x-api-key': auth.secret_text },
|
||||
responseType: 'arraybuffer',
|
||||
});
|
||||
|
||||
return { audio: audioResponse.body };
|
||||
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,119 @@
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { HttpMethod, httpClient, HttpResponse } from '@activepieces/pieces-common';
|
||||
import { cambaiAuth } from '../../index';
|
||||
import { API_BASE_URL, listSourceLanguagesDropdown, listVoicesDropdown ,POLLING_INTERVAL_MS,MAX_POLLING_ATTEMPTS } from '../common';
|
||||
import { listFoldersDropdown } from '../common';
|
||||
|
||||
export const createTextToSpeech = createAction({
|
||||
auth: cambaiAuth,
|
||||
name: 'create_text_to_speech',
|
||||
displayName: 'Create Text-to-Speech',
|
||||
description: 'Convert text into speech using a specified voice, language, gender, and age group.',
|
||||
props: {
|
||||
text: Property.LongText({
|
||||
displayName: 'Text',
|
||||
description: 'The text to be converted to speech.',
|
||||
required: true,
|
||||
}),
|
||||
language: listSourceLanguagesDropdown,
|
||||
voice_id: listVoicesDropdown,
|
||||
gender: Property.StaticDropdown({
|
||||
displayName: 'Gender',
|
||||
description: 'The gender of the speaker.',
|
||||
required: false,
|
||||
options: {
|
||||
options: [
|
||||
{ label: 'Male', value: 1 },
|
||||
{ label: 'Female', value: 2 },
|
||||
{ label: 'Neutral', value: 0 },
|
||||
{ label: 'Unspecified', value: 9 },
|
||||
],
|
||||
}
|
||||
}),
|
||||
age: Property.Number({
|
||||
displayName: 'Age',
|
||||
description: 'The age of the speaker to be generated.',
|
||||
required: false,
|
||||
}),
|
||||
project_name: Property.ShortText({
|
||||
displayName: 'Project Name',
|
||||
description: 'A memorable name for your project to help organize tasks in your Camb.ai workspace.',
|
||||
required: false,
|
||||
}),
|
||||
project_description: Property.LongText({
|
||||
displayName: 'Project Description',
|
||||
description: 'Provide details about your project\'s goals and specifications for documentation purposes.',
|
||||
required: false,
|
||||
}),
|
||||
folder_id: listFoldersDropdown,
|
||||
},
|
||||
async run(context) {
|
||||
const { auth } = context;
|
||||
const { text, language, voice_id, gender, age, project_name, project_description, folder_id } = context.propsValue;
|
||||
|
||||
const payload: Record<string, unknown> = { text, language: Number(language), voice_id: Number(voice_id) };
|
||||
if (gender !== undefined) payload['gender'] = gender;
|
||||
if (age) payload['age'] = age;
|
||||
if (project_name) payload['project_name'] = project_name;
|
||||
if (project_description) payload['project_description'] = project_description;
|
||||
if (folder_id) payload['folder_id'] = folder_id;
|
||||
|
||||
|
||||
const initialResponse = await httpClient.sendRequest<{ task_id: string }>({
|
||||
method: HttpMethod.POST,
|
||||
url: `${API_BASE_URL}/tts`,
|
||||
headers: { 'x-api-key': auth.secret_text, 'Content-Type': 'application/json' },
|
||||
body: payload,
|
||||
});
|
||||
const taskId = initialResponse.body.task_id;
|
||||
|
||||
let attempts = 0;
|
||||
let run_id: string | null = null;
|
||||
while (attempts < MAX_POLLING_ATTEMPTS) {
|
||||
const statusResponse = await httpClient.sendRequest<{ status: string; run_id?: string }>({
|
||||
method: HttpMethod.GET,
|
||||
url: `${API_BASE_URL}/tts/${taskId}`,
|
||||
headers: { 'x-api-key': auth.secret_text },
|
||||
});
|
||||
|
||||
if (statusResponse.body.status === 'SUCCESS') {
|
||||
|
||||
run_id = statusResponse.body.run_id ?? null;
|
||||
break;
|
||||
}
|
||||
if (statusResponse.body.status === 'FAILED') {
|
||||
|
||||
throw new Error(`Text-to-Speech task failed: ${JSON.stringify(statusResponse.body)}`);
|
||||
}
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, POLLING_INTERVAL_MS));
|
||||
attempts++;
|
||||
}
|
||||
|
||||
|
||||
if (!run_id) {
|
||||
throw new Error("Text-to-Speech task timed out or failed to return a run_id.");
|
||||
}
|
||||
|
||||
|
||||
const audioResponse: HttpResponse = await httpClient.sendRequest({
|
||||
method: HttpMethod.GET,
|
||||
url: `${API_BASE_URL}/tts-result/${run_id}`,
|
||||
headers: { 'x-api-key': auth.secret_text },
|
||||
responseType: 'arraybuffer',
|
||||
});
|
||||
|
||||
const fileName = `speech_${run_id}.wav`;
|
||||
const fileData = Buffer.from(audioResponse.body as ArrayBuffer);
|
||||
const fileUrl = await context.files.write({
|
||||
fileName,
|
||||
data: fileData,
|
||||
});
|
||||
|
||||
return {
|
||||
message: "Speech generated successfully.",
|
||||
audio_url: fileUrl,
|
||||
run_id: run_id,
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,130 @@
|
||||
import { createAction, Property, ApFile, DynamicPropsValue } from '@activepieces/pieces-framework';
|
||||
import { HttpMethod, httpClient, HttpMessageBody, HttpHeaders } from '@activepieces/pieces-common';
|
||||
import { cambaiAuth } from '../../index';
|
||||
import { API_BASE_URL, listSourceLanguagesDropdown, POLLING_INTERVAL_MS, LONG_MAX_POLLING_ATTEMPTS } from '../common';
|
||||
import FormData from 'form-data';
|
||||
import { listFoldersDropdown } from '../common';
|
||||
|
||||
export const createTranscription = createAction({
|
||||
auth: cambaiAuth,
|
||||
name: 'create_transcription',
|
||||
displayName: 'Create Transcription',
|
||||
description: 'Creates a task to process speech into readable text.',
|
||||
props: {
|
||||
language: listSourceLanguagesDropdown,
|
||||
source_type: Property.StaticDropdown({
|
||||
displayName: 'Media Source',
|
||||
description: 'Choose whether to upload a file or provide a URL.',
|
||||
required: true,
|
||||
defaultValue: 'file',
|
||||
options: {
|
||||
options: [
|
||||
{ label: 'Upload File', value: 'file' },
|
||||
{ label: 'File URL', value: 'url' },
|
||||
]
|
||||
},
|
||||
}),
|
||||
media: Property.DynamicProperties({
|
||||
auth: cambaiAuth,
|
||||
displayName: 'Media',
|
||||
required: true,
|
||||
refreshers: ['source_type'],
|
||||
props: async (context) => {
|
||||
const sourceType = (context['source_type'] as unknown as string);
|
||||
const fields: DynamicPropsValue = {};
|
||||
if (sourceType === 'file') {
|
||||
fields['media_file'] = Property.File({
|
||||
displayName: 'Media File',
|
||||
description: 'The media file (e.g., MP3, WAV, MP4) to transcribe. Max size: 20MB.',
|
||||
required: true,
|
||||
});
|
||||
} else if (sourceType === 'url') {
|
||||
fields['media_url'] = Property.ShortText({
|
||||
displayName: 'Media URL',
|
||||
description: 'A public URL to the media file to transcribe.',
|
||||
required: true,
|
||||
});
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
}),
|
||||
project_name: Property.ShortText({
|
||||
displayName: 'Project Name',
|
||||
description: 'A memorable name for your project to help organize tasks in your Camb.ai workspace.',
|
||||
required: false,
|
||||
}),
|
||||
project_description: Property.LongText({
|
||||
displayName: 'Project Description',
|
||||
description: 'Provide details about your project\'s goals and specifications for documentation purposes.',
|
||||
required: false,
|
||||
}),
|
||||
folder_id: listFoldersDropdown,
|
||||
},
|
||||
async run(context) {
|
||||
const { auth } = context;
|
||||
const { language, source_type, media, project_name, project_description, folder_id } = context.propsValue;
|
||||
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append('language', Number(language).toString());
|
||||
if (project_name) formData.append('project_name', project_name);
|
||||
if (project_description) formData.append('project_description', project_description);
|
||||
if (folder_id) formData.append('folder_id', folder_id.toString());
|
||||
|
||||
if (source_type === 'url') {
|
||||
if (!media['media_url']) throw new Error("Media URL is required when source is 'File URL'.");
|
||||
formData.append('media_url', media['media_url'] as string);
|
||||
} else {
|
||||
if (!media['media_file']) throw new Error("Media File is required when source is 'Upload File'.");
|
||||
const fileData = media['media_file'] as ApFile;
|
||||
formData.append('media_file', fileData.data, fileData.filename);
|
||||
}
|
||||
|
||||
|
||||
const requestBody = await formData.getBuffer();
|
||||
const headers: HttpHeaders = {
|
||||
'x-api-key': auth.secret_text,
|
||||
...formData.getHeaders(),
|
||||
};
|
||||
|
||||
const initialResponse = await httpClient.sendRequest<{ task_id: string }>({
|
||||
method: HttpMethod.POST,
|
||||
url: `${API_BASE_URL}/transcribe`,
|
||||
headers: headers,
|
||||
body: requestBody,
|
||||
});
|
||||
const taskId = initialResponse.body.task_id;
|
||||
let run_id: string | null = null;
|
||||
|
||||
let attempts = 0;
|
||||
while (attempts < LONG_MAX_POLLING_ATTEMPTS) {
|
||||
const statusResponse = await httpClient.sendRequest<{ status: string; run_id?: string }>({
|
||||
method: HttpMethod.GET,
|
||||
url: `${API_BASE_URL}/transcribe/${taskId}`,
|
||||
headers: { 'x-api-key': auth.secret_text },
|
||||
});
|
||||
|
||||
if (statusResponse.body.status === 'SUCCESS') {
|
||||
run_id = statusResponse.body.run_id ?? null;
|
||||
break;
|
||||
}
|
||||
if (statusResponse.body.status === 'FAILED') {
|
||||
throw new Error(`Transcription task failed: ${JSON.stringify(statusResponse.body)}`);
|
||||
}
|
||||
await new Promise(resolve => setTimeout(resolve, POLLING_INTERVAL_MS));
|
||||
attempts++;
|
||||
}
|
||||
|
||||
if (!run_id) {
|
||||
throw new Error("Transcription task timed out or failed to return a task_id.");
|
||||
}
|
||||
const resultResponse = await httpClient.sendRequest<{ transcriptions: string[] }>({
|
||||
method: HttpMethod.GET,
|
||||
url: `${API_BASE_URL}/transcription-result/${run_id}`,
|
||||
headers: { 'x-api-key': auth.secret_text },
|
||||
});
|
||||
|
||||
return resultResponse.body;
|
||||
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,113 @@
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { HttpMethod, httpClient } from '@activepieces/pieces-common';
|
||||
import { cambaiAuth } from '../../index';
|
||||
import { API_BASE_URL, listSourceLanguagesDropdown, listTargetLanguagesDropdown ,POLLING_INTERVAL_MS,MAX_POLLING_ATTEMPTS} from '../common';
|
||||
|
||||
export const createTranslation = createAction({
|
||||
auth: cambaiAuth,
|
||||
name: 'create_translation',
|
||||
displayName: 'Create Translation',
|
||||
description: 'Translate text from a source language to a target language.',
|
||||
props: {
|
||||
texts: Property.LongText({
|
||||
displayName: 'Text to Translate',
|
||||
description: 'The text to be translated. You can enter multiple lines; each line will be treated as a separate text segment.',
|
||||
required: true,
|
||||
}),
|
||||
source_language: listSourceLanguagesDropdown,
|
||||
target_language: listTargetLanguagesDropdown,
|
||||
formality: Property.StaticDropdown({
|
||||
displayName: 'Formality',
|
||||
description: 'Adjust the formality level to match your context.',
|
||||
required: false,
|
||||
options: {
|
||||
options: [
|
||||
{ label: 'Formal', value: 1 },
|
||||
{ label: 'Informal', value: 2 },
|
||||
]
|
||||
}
|
||||
}),
|
||||
gender: Property.StaticDropdown({
|
||||
displayName: 'Gender',
|
||||
description: 'Specify grammatical gender preferences when relevant in the target language.',
|
||||
required: false,
|
||||
options: {
|
||||
options: [
|
||||
{ label: 'Male', value: 1 },
|
||||
{ label: 'Female', value: 2 },
|
||||
{ label: 'Neutral', value: 0 },
|
||||
{ label: 'Unspecified', value: 9 },
|
||||
],
|
||||
}
|
||||
}),
|
||||
age: Property.Number({
|
||||
displayName: 'Audience Age',
|
||||
description: 'Helps adjust vocabulary and expressions to be age-appropriate.',
|
||||
required: false,
|
||||
}),
|
||||
project_name: Property.ShortText({
|
||||
displayName: 'Project Name',
|
||||
description: 'A memorable name for your project to help organize tasks in your Camb.ai workspace.',
|
||||
required: false,
|
||||
}),
|
||||
},
|
||||
async run(context) {
|
||||
const { auth } = context;
|
||||
const { texts, source_language, target_language, formality, gender, age, project_name } = context.propsValue;
|
||||
|
||||
const payload: Record<string, unknown> = {
|
||||
texts: texts.split('\n').filter(line => line.trim().length > 0),
|
||||
source_language: Number(source_language),
|
||||
target_language: Number(target_language),
|
||||
};
|
||||
if (formality !== undefined) payload['formality'] = formality;
|
||||
if (gender !== undefined) payload['gender'] = gender;
|
||||
if (age) payload['age'] = age;
|
||||
if (project_name) payload['project_name'] = project_name;
|
||||
|
||||
const initialResponse = await httpClient.sendRequest<{ task_id: string }>({
|
||||
method: HttpMethod.POST,
|
||||
url: `${API_BASE_URL}/translate`,
|
||||
headers: { 'x-api-key': auth.secret_text, 'Content-Type': 'application/json' },
|
||||
body: payload,
|
||||
});
|
||||
const taskId = initialResponse.body.task_id;
|
||||
let run_id: string | null = null;
|
||||
|
||||
let attempts = 0;
|
||||
while (attempts < MAX_POLLING_ATTEMPTS) {
|
||||
const statusResponse = await httpClient.sendRequest<{ status: string; run_id?: string }>({
|
||||
method: HttpMethod.GET,
|
||||
url: `${API_BASE_URL}/translate/${taskId}`,
|
||||
headers: { 'x-api-key': auth.secret_text },
|
||||
});
|
||||
|
||||
if (statusResponse.body.status === 'SUCCESS') {
|
||||
|
||||
run_id = statusResponse.body.run_id ?? null;
|
||||
break;
|
||||
}
|
||||
if (statusResponse.body.status === 'ERROR' || statusResponse.body.status === 'FAILED') {
|
||||
|
||||
throw new Error(`Translation task failed: ${JSON.stringify(statusResponse.body)}`);
|
||||
}
|
||||
await new Promise(resolve => setTimeout(resolve, POLLING_INTERVAL_MS));
|
||||
attempts++;
|
||||
}
|
||||
|
||||
|
||||
if (!run_id) {
|
||||
throw new Error("Translation task timed out or failed to return a task_id.");
|
||||
}
|
||||
|
||||
const resultResponse = await httpClient.sendRequest<{ translations: string[] }>({
|
||||
method: HttpMethod.GET,
|
||||
url: `${API_BASE_URL}/translation-result/${run_id}`,
|
||||
headers: { 'x-api-key': auth.secret_text },
|
||||
});
|
||||
|
||||
return resultResponse.body;
|
||||
|
||||
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,165 @@
|
||||
import { Property } from "@activepieces/pieces-framework";
|
||||
import { HttpMethod, httpClient } from "@activepieces/pieces-common";
|
||||
import { cambaiAuth } from "../..";
|
||||
|
||||
|
||||
export const API_BASE_URL = "https://client.camb.ai/apis";
|
||||
export const POLLING_INTERVAL_MS = 5000;
|
||||
export const LONG_POLLING_INTERVAL_MS = 10000;
|
||||
export const MAX_POLLING_ATTEMPTS = 10;
|
||||
export const LONG_MAX_POLLING_ATTEMPTS = 120;
|
||||
|
||||
type Voice = {
|
||||
id: number;
|
||||
voice_name: string;
|
||||
};
|
||||
|
||||
type Folder = {
|
||||
folder_id: number;
|
||||
folder_name: string;
|
||||
};
|
||||
|
||||
type Language = {
|
||||
id: number;
|
||||
language: string;
|
||||
short_name: string;
|
||||
};
|
||||
|
||||
|
||||
export const listVoicesDropdown = Property.Dropdown({
|
||||
auth: cambaiAuth,
|
||||
displayName: 'Voice',
|
||||
description: 'Select the voice to generate the speech.',
|
||||
required: true,
|
||||
refreshers: [],
|
||||
options: async ({ auth }) => {
|
||||
if (!auth) {
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
placeholder: 'Please authenticate first',
|
||||
};
|
||||
}
|
||||
const response = await httpClient.sendRequest<Voice[]>({
|
||||
method: HttpMethod.GET,
|
||||
url: `${API_BASE_URL}/list-voices`,
|
||||
headers: {
|
||||
'x-api-key': auth.secret_text,
|
||||
},
|
||||
});
|
||||
const voices = response.body ?? [];
|
||||
return {
|
||||
disabled: false,
|
||||
options: voices.map((voice) => ({
|
||||
label: voice.voice_name,
|
||||
value: voice.id,
|
||||
})),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
export const listSourceLanguagesDropdown = Property.Dropdown({
|
||||
auth: cambaiAuth,
|
||||
displayName: 'Source Language',
|
||||
description: 'Select the original language of the input text.',
|
||||
required: true,
|
||||
refreshers: [],
|
||||
options: async ({ auth }) => {
|
||||
if (!auth) {
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
placeholder: 'Please authenticate first',
|
||||
};
|
||||
}
|
||||
const response = await httpClient.sendRequest<Language[]>({
|
||||
method: HttpMethod.GET,
|
||||
url: `${API_BASE_URL}/source-languages`,
|
||||
headers: {
|
||||
'x-api-key': auth.secret_text,
|
||||
},
|
||||
});
|
||||
const languages = response.body ?? [];
|
||||
return {
|
||||
disabled: false,
|
||||
options: languages.map((lang) => ({
|
||||
label: `${lang.language} (${lang.short_name})`,
|
||||
value: lang.id,
|
||||
})),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
export const listTargetLanguagesDropdown = Property.Dropdown({
|
||||
displayName: 'Target Language',
|
||||
description: 'Select the language to translate the text into.',
|
||||
auth: cambaiAuth,
|
||||
required: true,
|
||||
refreshers: [],
|
||||
options: async ({ auth }) => {
|
||||
if (!auth) {
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
placeholder: 'Please authenticate first',
|
||||
};
|
||||
}
|
||||
const response = await httpClient.sendRequest<Language[]>({
|
||||
method: HttpMethod.GET,
|
||||
url: `${API_BASE_URL}/target-languages`,
|
||||
headers: {
|
||||
'x-api-key': auth.secret_text,
|
||||
},
|
||||
});
|
||||
const languages = response.body ?? [];
|
||||
return {
|
||||
disabled: false,
|
||||
options: languages.map((lang) => ({
|
||||
label: `${lang.language} (${lang.short_name})`,
|
||||
value: lang.id,
|
||||
})),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export const listFoldersDropdown = Property.Dropdown({
|
||||
displayName: 'Folder',
|
||||
auth: cambaiAuth,
|
||||
description: 'Select the folder to save the task in.',
|
||||
required: false,
|
||||
refreshers: [],
|
||||
options: async ({ auth }) => {
|
||||
if (!auth) {
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
placeholder: 'Please authenticate first',
|
||||
};
|
||||
}
|
||||
try {
|
||||
const response = await httpClient.sendRequest<Folder[]>({
|
||||
method: HttpMethod.GET,
|
||||
url: `${API_BASE_URL}/folders`,
|
||||
headers: {
|
||||
'x-api-key': auth.secret_text,
|
||||
},
|
||||
});
|
||||
const folders = response.body ?? [];
|
||||
return {
|
||||
disabled: false,
|
||||
options: folders.map((folder) => ({
|
||||
label: folder.folder_name,
|
||||
value: folder.folder_id,
|
||||
})),
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
placeholder: "Could not load folders."
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user