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,33 @@
|
||||
{
|
||||
"extends": [
|
||||
"../../../../.eslintrc.base.json"
|
||||
],
|
||||
"ignorePatterns": [
|
||||
"!**/*"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"*.ts",
|
||||
"*.tsx",
|
||||
"*.js",
|
||||
"*.jsx"
|
||||
],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"*.ts",
|
||||
"*.tsx"
|
||||
],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"*.js",
|
||||
"*.jsx"
|
||||
],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# pieces-lofty
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Building
|
||||
|
||||
Run `nx build pieces-lofty` to build the library.
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "@activepieces/piece-lofty",
|
||||
"version": "0.0.1",
|
||||
"type": "commonjs",
|
||||
"main": "./src/index.js",
|
||||
"types": "./src/index.d.ts",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"name": "pieces-lofty",
|
||||
"$schema": "../../../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "packages/pieces/community/lofty/src",
|
||||
"projectType": "library",
|
||||
"release": {
|
||||
"version": {
|
||||
"manifestRootsToUpdate": [
|
||||
"dist/{projectRoot}"
|
||||
],
|
||||
"currentVersionResolver": "git-tag",
|
||||
"fallbackCurrentVersionResolver": "disk"
|
||||
}
|
||||
},
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@nx/js:tsc",
|
||||
"outputs": [
|
||||
"{options.outputPath}"
|
||||
],
|
||||
"options": {
|
||||
"outputPath": "dist/packages/pieces/community/lofty",
|
||||
"tsConfig": "packages/pieces/community/lofty/tsconfig.lib.json",
|
||||
"packageJson": "packages/pieces/community/lofty/package.json",
|
||||
"main": "packages/pieces/community/lofty/src/index.ts",
|
||||
"assets": [
|
||||
"packages/pieces/community/lofty/*.md",
|
||||
{
|
||||
"input": "packages/pieces/community/lofty/src/i18n",
|
||||
"output": "./src/i18n",
|
||||
"glob": "**/!(i18n.json)"
|
||||
}
|
||||
],
|
||||
"buildableProjectDepsInPackageJsonType": "dependencies",
|
||||
"updateBuildableProjectDepsInPackageJson": true
|
||||
},
|
||||
"dependsOn": [
|
||||
"prebuild",
|
||||
"^build"
|
||||
]
|
||||
},
|
||||
"nx-release-publish": {
|
||||
"options": {
|
||||
"packageRoot": "dist/{projectRoot}"
|
||||
}
|
||||
},
|
||||
"prebuild": {
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
],
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"cwd": "packages/pieces/community/lofty",
|
||||
"command": "bun install --no-save --silent"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nx/eslint:lint",
|
||||
"outputs": [
|
||||
"{options.outputFile}"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { createPiece } from '@activepieces/pieces-framework';
|
||||
import { loftyAuth } from './lib/common/auth';
|
||||
import { createLead } from './lib/actions/create-lead';
|
||||
import { PieceCategory } from '@activepieces/shared';
|
||||
import { createTransaction } from './lib/actions/create-transaction';
|
||||
import { updateLead } from './lib/actions/update-lead';
|
||||
import { updateTransaction } from './lib/actions/update-transaction';
|
||||
import { createCustomApiCallAction } from '@activepieces/pieces-common';
|
||||
|
||||
export const lofty = createPiece({
|
||||
displayName: 'Lofty',
|
||||
auth: loftyAuth,
|
||||
minimumSupportedRelease: '0.36.1',
|
||||
logoUrl: 'https://cdn.activepieces.com/pieces/lofty.png',
|
||||
categories: [PieceCategory.SALES_AND_CRM],
|
||||
description:
|
||||
'Lofty is a product of the technology company Lofty Inc, which offers a complete tech solution for real estate agents.',
|
||||
authors: ['sanket-a11y'],
|
||||
actions: [
|
||||
createLead,
|
||||
createTransaction,
|
||||
updateLead,
|
||||
updateTransaction,
|
||||
createCustomApiCallAction({
|
||||
auth: loftyAuth,
|
||||
baseUrl: () => 'https://api.lofty.ai/v1',
|
||||
authMapping: async (auth) => ({
|
||||
Authorization: `token ${auth.secret_text}`,
|
||||
}),
|
||||
}),
|
||||
],
|
||||
triggers: [],
|
||||
});
|
||||
@@ -0,0 +1,255 @@
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { loftyAuth } from '../common/auth';
|
||||
import { makeRequest } from '../common/client';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
|
||||
export const createLead = createAction({
|
||||
auth: loftyAuth,
|
||||
name: 'createLead',
|
||||
displayName: 'Create Lead',
|
||||
description: 'Create a lead in Lofty',
|
||||
props: {
|
||||
firstName: Property.ShortText({
|
||||
displayName: 'First name',
|
||||
required: true,
|
||||
}),
|
||||
lastName: Property.ShortText({
|
||||
displayName: 'Last name',
|
||||
required: false,
|
||||
}),
|
||||
emails: Property.Array({
|
||||
displayName: 'Email',
|
||||
description: 'Lead email address(s)',
|
||||
required: true,
|
||||
}),
|
||||
phone: Property.ShortText({
|
||||
displayName: 'Phone',
|
||||
required: false,
|
||||
}),
|
||||
birthday: Property.ShortText({
|
||||
displayName: 'Birthday',
|
||||
description: 'YYYY-MM-DD',
|
||||
required: false,
|
||||
}),
|
||||
mailingAddress: Property.ShortText({
|
||||
displayName: 'Street or Complete Address (Mailing Address)',
|
||||
required: false,
|
||||
}),
|
||||
mailingCity: Property.ShortText({
|
||||
displayName: 'City (Mailing Address)',
|
||||
required: false,
|
||||
}),
|
||||
mailingState: Property.ShortText({
|
||||
displayName: 'State (Mailing Address)',
|
||||
required: false,
|
||||
}),
|
||||
mailingZipcode: Property.ShortText({
|
||||
displayName: 'Zip Code (Mailing Address)',
|
||||
required: false,
|
||||
}),
|
||||
leadType: Property.ShortText({
|
||||
displayName: 'Lead Type',
|
||||
required: false,
|
||||
}),
|
||||
leadOwnershipLevel: Property.ShortText({
|
||||
displayName: 'Lead Ownership Level',
|
||||
required: false,
|
||||
}),
|
||||
source: Property.ShortText({
|
||||
displayName: 'Source',
|
||||
required: false,
|
||||
}),
|
||||
segment: Property.ShortText({
|
||||
displayName: 'Segment',
|
||||
required: false,
|
||||
}),
|
||||
tags: Property.Array({
|
||||
displayName: 'Tag(s)',
|
||||
required: false,
|
||||
}),
|
||||
note: Property.LongText({
|
||||
displayName: 'Note',
|
||||
required: false,
|
||||
}),
|
||||
subscriptionSmartPlans: Property.Checkbox({
|
||||
displayName: 'Subscription - Smart Plans',
|
||||
required: false,
|
||||
}),
|
||||
subscriptionPropertyAlert: Property.Checkbox({
|
||||
displayName: 'Subscription - Property Alert',
|
||||
required: false,
|
||||
}),
|
||||
subscriptionMarketReport: Property.Checkbox({
|
||||
displayName: 'Subscription - Market Report',
|
||||
required: false,
|
||||
}),
|
||||
minPriceBuyer: Property.Number({
|
||||
displayName: 'Min Price (Buyer)',
|
||||
required: false,
|
||||
}),
|
||||
maxPriceBuyer: Property.Number({
|
||||
displayName: 'Max Price (Buyer)',
|
||||
required: false,
|
||||
}),
|
||||
locationDeprecated: Property.ShortText({
|
||||
displayName: 'Location (Deprecated)',
|
||||
required: false,
|
||||
}),
|
||||
buyerCity: Property.ShortText({
|
||||
displayName: 'City (Buyer)',
|
||||
required: false,
|
||||
}),
|
||||
buyerState: Property.ShortText({
|
||||
displayName: 'State (Buyer)',
|
||||
required: false,
|
||||
}),
|
||||
buyerZipcode: Property.ShortText({
|
||||
displayName: 'Zipcode (Buyer)',
|
||||
required: false,
|
||||
}),
|
||||
buyerAddress: Property.ShortText({
|
||||
displayName: 'Address (Buyer)',
|
||||
required: false,
|
||||
}),
|
||||
minBedsBuyer: Property.Number({
|
||||
displayName: 'Min Beds (Buyer)',
|
||||
required: false,
|
||||
}),
|
||||
minBathsBuyer: Property.Number({
|
||||
displayName: 'Min Baths (Buyer)',
|
||||
required: false,
|
||||
}),
|
||||
bedsSeller: Property.Number({
|
||||
displayName: 'Beds (Seller)',
|
||||
required: false,
|
||||
}),
|
||||
bathsSeller: Property.Number({
|
||||
displayName: 'Baths (Seller)',
|
||||
required: false,
|
||||
}),
|
||||
priceSeller: Property.Number({
|
||||
displayName: 'Price (Seller)',
|
||||
required: false,
|
||||
}),
|
||||
addressSeller: Property.ShortText({
|
||||
displayName: 'Address (Seller)',
|
||||
required: false,
|
||||
}),
|
||||
sqftSeller: Property.Number({
|
||||
displayName: 'SqFt (Seller)',
|
||||
required: false,
|
||||
}),
|
||||
citySeller: Property.ShortText({
|
||||
displayName: 'City (Seller)',
|
||||
required: false,
|
||||
}),
|
||||
stateSeller: Property.ShortText({
|
||||
displayName: 'State (Seller)',
|
||||
required: false,
|
||||
}),
|
||||
zipcodeSeller: Property.ShortText({
|
||||
displayName: 'Zipcode (Seller)',
|
||||
required: false,
|
||||
}),
|
||||
sendWelcomeEmail: Property.Checkbox({
|
||||
displayName: 'Send Welcome Email',
|
||||
required: false,
|
||||
}),
|
||||
callOptIn: Property.Checkbox({
|
||||
displayName: 'Call Opt In',
|
||||
required: false,
|
||||
}),
|
||||
textOptIn: Property.Checkbox({
|
||||
displayName: 'Text Opt In',
|
||||
required: false,
|
||||
}),
|
||||
emailOptIn: Property.Checkbox({
|
||||
displayName: 'Email Opt In',
|
||||
required: false,
|
||||
}),
|
||||
numberConsent: Property.Checkbox({
|
||||
displayName: 'Number Consent',
|
||||
required: false,
|
||||
}),
|
||||
},
|
||||
async run({ auth, propsValue }) {
|
||||
const payload: any = {
|
||||
emails: propsValue.emails,
|
||||
};
|
||||
|
||||
if (propsValue.firstName) payload.first_name = propsValue.firstName;
|
||||
if (propsValue.lastName) payload.last_name = propsValue.lastName;
|
||||
if (propsValue.phone) payload.phone = propsValue.phone;
|
||||
if (propsValue.source) payload.source = propsValue.source;
|
||||
if (propsValue.birthday) payload.birthday = propsValue.birthday;
|
||||
if (propsValue.mailingAddress)
|
||||
payload.mailing_address = propsValue.mailingAddress;
|
||||
if (propsValue.mailingCity) payload.mailing_city = propsValue.mailingCity;
|
||||
if (propsValue.mailingState)
|
||||
payload.mailing_state = propsValue.mailingState;
|
||||
if (propsValue.mailingZipcode)
|
||||
payload.mailing_zipcode = propsValue.mailingZipcode;
|
||||
if (propsValue.leadType) payload.lead_type = propsValue.leadType;
|
||||
if (propsValue.leadOwnershipLevel)
|
||||
payload.lead_ownership_level = propsValue.leadOwnershipLevel;
|
||||
if (propsValue.segment) payload.segment = propsValue.segment;
|
||||
if (propsValue.tags) payload.tags = propsValue.tags;
|
||||
if (propsValue.note) payload.note = propsValue.note;
|
||||
if (propsValue.subscriptionSmartPlans !== undefined)
|
||||
payload.subscription_smart_plans = propsValue.subscriptionSmartPlans;
|
||||
if (propsValue.subscriptionPropertyAlert !== undefined)
|
||||
payload.subscription_property_alert =
|
||||
propsValue.subscriptionPropertyAlert;
|
||||
if (propsValue.subscriptionMarketReport !== undefined)
|
||||
payload.subscription_market_report = propsValue.subscriptionMarketReport;
|
||||
if (propsValue.minPriceBuyer !== undefined)
|
||||
payload.min_price_buyer = propsValue.minPriceBuyer;
|
||||
if (propsValue.maxPriceBuyer !== undefined)
|
||||
payload.max_price_buyer = propsValue.maxPriceBuyer;
|
||||
if (propsValue.locationDeprecated)
|
||||
payload.location_deprecated = propsValue.locationDeprecated;
|
||||
if (propsValue.buyerCity) payload.buyer_city = propsValue.buyerCity;
|
||||
if (propsValue.buyerState) payload.buyer_state = propsValue.buyerState;
|
||||
if (propsValue.buyerZipcode)
|
||||
payload.buyer_zipcode = propsValue.buyerZipcode;
|
||||
if (propsValue.buyerAddress)
|
||||
payload.buyer_address = propsValue.buyerAddress;
|
||||
if (propsValue.minBedsBuyer !== undefined)
|
||||
payload.min_beds_buyer = propsValue.minBedsBuyer;
|
||||
if (propsValue.minBathsBuyer !== undefined)
|
||||
payload.min_baths_buyer = propsValue.minBathsBuyer;
|
||||
if (propsValue.bedsSeller !== undefined)
|
||||
payload.beds_seller = propsValue.bedsSeller;
|
||||
if (propsValue.bathsSeller !== undefined)
|
||||
payload.baths_seller = propsValue.bathsSeller;
|
||||
if (propsValue.priceSeller !== undefined)
|
||||
payload.price_seller = propsValue.priceSeller;
|
||||
if (propsValue.addressSeller)
|
||||
payload.address_seller = propsValue.addressSeller;
|
||||
if (propsValue.sqftSeller !== undefined)
|
||||
payload.sqft_seller = propsValue.sqftSeller;
|
||||
if (propsValue.citySeller) payload.city_seller = propsValue.citySeller;
|
||||
if (propsValue.stateSeller) payload.state_seller = propsValue.stateSeller;
|
||||
if (propsValue.zipcodeSeller)
|
||||
payload.zipcode_seller = propsValue.zipcodeSeller;
|
||||
if (propsValue.sendWelcomeEmail !== undefined)
|
||||
payload.send_welcome_email = propsValue.sendWelcomeEmail;
|
||||
if (propsValue.callOptIn !== undefined)
|
||||
payload.call_opt_in = propsValue.callOptIn;
|
||||
if (propsValue.textOptIn !== undefined)
|
||||
payload.text_opt_in = propsValue.textOptIn;
|
||||
if (propsValue.emailOptIn !== undefined)
|
||||
payload.email_opt_in = propsValue.emailOptIn;
|
||||
if (propsValue.numberConsent !== undefined)
|
||||
payload.number_consent = propsValue.numberConsent;
|
||||
|
||||
const response = await makeRequest(
|
||||
auth.secret_text,
|
||||
HttpMethod.POST,
|
||||
'/leads',
|
||||
payload
|
||||
);
|
||||
|
||||
return response;
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,118 @@
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { loftyAuth } from '../common/auth';
|
||||
import { makeRequest } from '../common/client';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import { leadIdDropdown } from '../common/props';
|
||||
|
||||
export const createTransaction = createAction({
|
||||
auth: loftyAuth,
|
||||
name: 'createTransaction',
|
||||
displayName: 'Create Transaction',
|
||||
description: 'Create a transaction for a Lofty lead',
|
||||
props: {
|
||||
leadId: leadIdDropdown,
|
||||
transactionDate: Property.ShortText({
|
||||
displayName: 'Transaction Date',
|
||||
description: 'YYYY-MM-DD',
|
||||
required: false,
|
||||
}),
|
||||
amount: Property.Number({
|
||||
displayName: 'Amount',
|
||||
required: false,
|
||||
}),
|
||||
transactionType: Property.ShortText({
|
||||
displayName: 'Transaction Type',
|
||||
description: 'e.g., sale, rent',
|
||||
required: false,
|
||||
}),
|
||||
propertyAddress: Property.ShortText({
|
||||
displayName: 'Property Address',
|
||||
required: false,
|
||||
}),
|
||||
propertyCity: Property.ShortText({
|
||||
displayName: 'Property City',
|
||||
required: false,
|
||||
}),
|
||||
propertyState: Property.ShortText({
|
||||
displayName: 'Property State',
|
||||
required: false,
|
||||
}),
|
||||
propertyZipcode: Property.ShortText({
|
||||
displayName: 'Property Zipcode',
|
||||
required: false,
|
||||
}),
|
||||
beds: Property.Number({
|
||||
displayName: 'Beds',
|
||||
required: false,
|
||||
}),
|
||||
baths: Property.Number({
|
||||
displayName: 'Baths',
|
||||
required: false,
|
||||
}),
|
||||
sqft: Property.Number({
|
||||
displayName: 'SqFt',
|
||||
required: false,
|
||||
}),
|
||||
mlsNumber: Property.ShortText({
|
||||
displayName: 'MLS Number',
|
||||
required: false,
|
||||
}),
|
||||
closingDate: Property.ShortText({
|
||||
displayName: 'Closing Date',
|
||||
description: 'YYYY-MM-DD',
|
||||
required: false,
|
||||
}),
|
||||
agentId: Property.ShortText({
|
||||
displayName: 'Agent ID',
|
||||
required: false,
|
||||
}),
|
||||
agentName: Property.ShortText({
|
||||
displayName: 'Agent Name',
|
||||
required: false,
|
||||
}),
|
||||
agentEmail: Property.ShortText({
|
||||
displayName: 'Agent Email',
|
||||
required: false,
|
||||
}),
|
||||
source: Property.ShortText({
|
||||
displayName: 'Source',
|
||||
required: false,
|
||||
}),
|
||||
note: Property.LongText({
|
||||
displayName: 'Note',
|
||||
required: false,
|
||||
}),
|
||||
},
|
||||
async run({ auth, propsValue }) {
|
||||
const { leadId } = propsValue;
|
||||
|
||||
const payload: any = {};
|
||||
|
||||
if (propsValue.transactionDate) payload.transaction_date = propsValue.transactionDate;
|
||||
if (propsValue.amount !== undefined) payload.amount = propsValue.amount;
|
||||
if (propsValue.transactionType) payload.transaction_type = propsValue.transactionType;
|
||||
if (propsValue.propertyAddress) payload.property_address = propsValue.propertyAddress;
|
||||
if (propsValue.propertyCity) payload.property_city = propsValue.propertyCity;
|
||||
if (propsValue.propertyState) payload.property_state = propsValue.propertyState;
|
||||
if (propsValue.propertyZipcode) payload.property_zipcode = propsValue.propertyZipcode;
|
||||
if (propsValue.beds !== undefined) payload.beds = propsValue.beds;
|
||||
if (propsValue.baths !== undefined) payload.baths = propsValue.baths;
|
||||
if (propsValue.sqft !== undefined) payload.sqft = propsValue.sqft;
|
||||
if (propsValue.mlsNumber) payload.mls_number = propsValue.mlsNumber;
|
||||
if (propsValue.closingDate) payload.closing_date = propsValue.closingDate;
|
||||
if (propsValue.agentId) payload.agent_id = propsValue.agentId;
|
||||
if (propsValue.agentName) payload.agent_name = propsValue.agentName;
|
||||
if (propsValue.agentEmail) payload.agent_email = propsValue.agentEmail;
|
||||
if (propsValue.source) payload.source = propsValue.source;
|
||||
if (propsValue.note) payload.note = propsValue.note;
|
||||
|
||||
const response = await makeRequest(
|
||||
auth.secret_text,
|
||||
HttpMethod.POST,
|
||||
`/leads/${leadId}/transaction`,
|
||||
payload
|
||||
);
|
||||
|
||||
return response;
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,258 @@
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { loftyAuth } from '../common/auth';
|
||||
import { makeRequest } from '../common/client';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import { leadIdDropdown } from '../common/props';
|
||||
|
||||
export const updateLead = createAction({
|
||||
auth: loftyAuth,
|
||||
name: 'updateLead',
|
||||
displayName: 'Update Lead',
|
||||
description: 'Update a lead in Lofty',
|
||||
props: {
|
||||
leadId: leadIdDropdown,
|
||||
firstName: Property.ShortText({
|
||||
displayName: 'First name',
|
||||
required: false,
|
||||
}),
|
||||
lastName: Property.ShortText({
|
||||
displayName: 'Last name',
|
||||
required: false,
|
||||
}),
|
||||
emails: Property.Array({
|
||||
displayName: 'Email',
|
||||
description: 'Lead email address(s)',
|
||||
required: false,
|
||||
}),
|
||||
phone: Property.ShortText({
|
||||
displayName: 'Phone',
|
||||
required: false,
|
||||
}),
|
||||
birthday: Property.ShortText({
|
||||
displayName: 'Birthday',
|
||||
description: 'YYYY-MM-DD',
|
||||
required: false,
|
||||
}),
|
||||
mailingAddress: Property.ShortText({
|
||||
displayName: 'Street or Complete Address (Mailing Address)',
|
||||
required: false,
|
||||
}),
|
||||
mailingCity: Property.ShortText({
|
||||
displayName: 'City (Mailing Address)',
|
||||
required: false,
|
||||
}),
|
||||
mailingState: Property.ShortText({
|
||||
displayName: 'State (Mailing Address)',
|
||||
required: false,
|
||||
}),
|
||||
mailingZipcode: Property.ShortText({
|
||||
displayName: 'Zip Code (Mailing Address)',
|
||||
required: false,
|
||||
}),
|
||||
leadType: Property.ShortText({
|
||||
displayName: 'Lead Type',
|
||||
required: false,
|
||||
}),
|
||||
leadOwnershipLevel: Property.ShortText({
|
||||
displayName: 'Lead Ownership Level',
|
||||
required: false,
|
||||
}),
|
||||
source: Property.ShortText({
|
||||
displayName: 'Source',
|
||||
required: false,
|
||||
}),
|
||||
segment: Property.ShortText({
|
||||
displayName: 'Segment',
|
||||
required: false,
|
||||
}),
|
||||
tags: Property.Array({
|
||||
displayName: 'Tag(s)',
|
||||
required: false,
|
||||
}),
|
||||
note: Property.LongText({
|
||||
displayName: 'Note',
|
||||
required: false,
|
||||
}),
|
||||
subscriptionSmartPlans: Property.Checkbox({
|
||||
displayName: 'Subscription - Smart Plans',
|
||||
required: false,
|
||||
}),
|
||||
subscriptionPropertyAlert: Property.Checkbox({
|
||||
displayName: 'Subscription - Property Alert',
|
||||
required: false,
|
||||
}),
|
||||
subscriptionMarketReport: Property.Checkbox({
|
||||
displayName: 'Subscription - Market Report',
|
||||
required: false,
|
||||
}),
|
||||
minPriceBuyer: Property.Number({
|
||||
displayName: 'Min Price (Buyer)',
|
||||
required: false,
|
||||
}),
|
||||
maxPriceBuyer: Property.Number({
|
||||
displayName: 'Max Price (Buyer)',
|
||||
required: false,
|
||||
}),
|
||||
locationDeprecated: Property.ShortText({
|
||||
displayName: 'Location (Deprecated)',
|
||||
required: false,
|
||||
}),
|
||||
buyerCity: Property.ShortText({
|
||||
displayName: 'City (Buyer)',
|
||||
required: false,
|
||||
}),
|
||||
buyerState: Property.ShortText({
|
||||
displayName: 'State (Buyer)',
|
||||
required: false,
|
||||
}),
|
||||
buyerZipcode: Property.ShortText({
|
||||
displayName: 'Zipcode (Buyer)',
|
||||
required: false,
|
||||
}),
|
||||
buyerAddress: Property.ShortText({
|
||||
displayName: 'Address (Buyer)',
|
||||
required: false,
|
||||
}),
|
||||
minBedsBuyer: Property.Number({
|
||||
displayName: 'Min Beds (Buyer)',
|
||||
required: false,
|
||||
}),
|
||||
minBathsBuyer: Property.Number({
|
||||
displayName: 'Min Baths (Buyer)',
|
||||
required: false,
|
||||
}),
|
||||
bedsSeller: Property.Number({
|
||||
displayName: 'Beds (Seller)',
|
||||
required: false,
|
||||
}),
|
||||
bathsSeller: Property.Number({
|
||||
displayName: 'Baths (Seller)',
|
||||
required: false,
|
||||
}),
|
||||
priceSeller: Property.Number({
|
||||
displayName: 'Price (Seller)',
|
||||
required: false,
|
||||
}),
|
||||
addressSeller: Property.ShortText({
|
||||
displayName: 'Address (Seller)',
|
||||
required: false,
|
||||
}),
|
||||
sqftSeller: Property.Number({
|
||||
displayName: 'SqFt (Seller)',
|
||||
required: false,
|
||||
}),
|
||||
citySeller: Property.ShortText({
|
||||
displayName: 'City (Seller)',
|
||||
required: false,
|
||||
}),
|
||||
stateSeller: Property.ShortText({
|
||||
displayName: 'State (Seller)',
|
||||
required: false,
|
||||
}),
|
||||
zipcodeSeller: Property.ShortText({
|
||||
displayName: 'Zipcode (Seller)',
|
||||
required: false,
|
||||
}),
|
||||
sendWelcomeEmail: Property.Checkbox({
|
||||
displayName: 'Send Welcome Email',
|
||||
required: false,
|
||||
}),
|
||||
callOptIn: Property.Checkbox({
|
||||
displayName: 'Call Opt In',
|
||||
required: false,
|
||||
}),
|
||||
textOptIn: Property.Checkbox({
|
||||
displayName: 'Text Opt In',
|
||||
required: false,
|
||||
}),
|
||||
emailOptIn: Property.Checkbox({
|
||||
displayName: 'Email Opt In',
|
||||
required: false,
|
||||
}),
|
||||
numberConsent: Property.Checkbox({
|
||||
displayName: 'Number Consent',
|
||||
required: false,
|
||||
}),
|
||||
},
|
||||
async run({ auth, propsValue }) {
|
||||
const { leadId } = propsValue;
|
||||
|
||||
const payload: any = {};
|
||||
|
||||
if (propsValue.firstName) payload.first_name = propsValue.firstName;
|
||||
if (propsValue.lastName) payload.last_name = propsValue.lastName;
|
||||
if (propsValue.emails) payload.emails = propsValue.emails;
|
||||
if (propsValue.phone) payload.phone = propsValue.phone;
|
||||
if (propsValue.source) payload.source = propsValue.source;
|
||||
if (propsValue.birthday) payload.birthday = propsValue.birthday;
|
||||
if (propsValue.mailingAddress)
|
||||
payload.mailing_address = propsValue.mailingAddress;
|
||||
if (propsValue.mailingCity) payload.mailing_city = propsValue.mailingCity;
|
||||
if (propsValue.mailingState)
|
||||
payload.mailing_state = propsValue.mailingState;
|
||||
if (propsValue.mailingZipcode)
|
||||
payload.mailing_zipcode = propsValue.mailingZipcode;
|
||||
if (propsValue.leadType) payload.lead_type = propsValue.leadType;
|
||||
if (propsValue.leadOwnershipLevel)
|
||||
payload.lead_ownership_level = propsValue.leadOwnershipLevel;
|
||||
if (propsValue.segment) payload.segment = propsValue.segment;
|
||||
if (propsValue.tags) payload.tags = propsValue.tags;
|
||||
if (propsValue.note) payload.note = propsValue.note;
|
||||
if (propsValue.subscriptionSmartPlans !== undefined)
|
||||
payload.subscription_smart_plans = propsValue.subscriptionSmartPlans;
|
||||
if (propsValue.subscriptionPropertyAlert !== undefined)
|
||||
payload.subscription_property_alert =
|
||||
propsValue.subscriptionPropertyAlert;
|
||||
if (propsValue.subscriptionMarketReport !== undefined)
|
||||
payload.subscription_market_report = propsValue.subscriptionMarketReport;
|
||||
if (propsValue.minPriceBuyer !== undefined)
|
||||
payload.min_price_buyer = propsValue.minPriceBuyer;
|
||||
if (propsValue.maxPriceBuyer !== undefined)
|
||||
payload.max_price_buyer = propsValue.maxPriceBuyer;
|
||||
if (propsValue.locationDeprecated)
|
||||
payload.location_deprecated = propsValue.locationDeprecated;
|
||||
if (propsValue.buyerCity) payload.buyer_city = propsValue.buyerCity;
|
||||
if (propsValue.buyerState) payload.buyer_state = propsValue.buyerState;
|
||||
if (propsValue.buyerZipcode)
|
||||
payload.buyer_zipcode = propsValue.buyerZipcode;
|
||||
if (propsValue.buyerAddress)
|
||||
payload.buyer_address = propsValue.buyerAddress;
|
||||
if (propsValue.minBedsBuyer !== undefined)
|
||||
payload.min_beds_buyer = propsValue.minBedsBuyer;
|
||||
if (propsValue.minBathsBuyer !== undefined)
|
||||
payload.min_baths_buyer = propsValue.minBathsBuyer;
|
||||
if (propsValue.bedsSeller !== undefined)
|
||||
payload.beds_seller = propsValue.bedsSeller;
|
||||
if (propsValue.bathsSeller !== undefined)
|
||||
payload.baths_seller = propsValue.bathsSeller;
|
||||
if (propsValue.priceSeller !== undefined)
|
||||
payload.price_seller = propsValue.priceSeller;
|
||||
if (propsValue.addressSeller)
|
||||
payload.address_seller = propsValue.addressSeller;
|
||||
if (propsValue.sqftSeller !== undefined)
|
||||
payload.sqft_seller = propsValue.sqftSeller;
|
||||
if (propsValue.citySeller) payload.city_seller = propsValue.citySeller;
|
||||
if (propsValue.stateSeller) payload.state_seller = propsValue.stateSeller;
|
||||
if (propsValue.zipcodeSeller)
|
||||
payload.zipcode_seller = propsValue.zipcodeSeller;
|
||||
if (propsValue.sendWelcomeEmail !== undefined)
|
||||
payload.send_welcome_email = propsValue.sendWelcomeEmail;
|
||||
if (propsValue.callOptIn !== undefined)
|
||||
payload.call_opt_in = propsValue.callOptIn;
|
||||
if (propsValue.textOptIn !== undefined)
|
||||
payload.text_opt_in = propsValue.textOptIn;
|
||||
if (propsValue.emailOptIn !== undefined)
|
||||
payload.email_opt_in = propsValue.emailOptIn;
|
||||
if (propsValue.numberConsent !== undefined)
|
||||
payload.number_consent = propsValue.numberConsent;
|
||||
|
||||
const response = await makeRequest(
|
||||
auth.secret_text,
|
||||
HttpMethod.PUT,
|
||||
`/leads/${leadId}`,
|
||||
payload
|
||||
);
|
||||
|
||||
return response;
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,125 @@
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { loftyAuth } from '../common/auth';
|
||||
import { makeRequest } from '../common/client';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import { leadIdDropdown, transactionIdDropdown } from '../common/props';
|
||||
|
||||
export const updateTransaction = createAction({
|
||||
auth: loftyAuth,
|
||||
name: 'updateTransaction',
|
||||
displayName: 'Update Transaction',
|
||||
description: 'Update a transaction for a Lofty lead',
|
||||
props: {
|
||||
leadId: leadIdDropdown,
|
||||
transactionId: transactionIdDropdown,
|
||||
transactionDate: Property.ShortText({
|
||||
displayName: 'Transaction Date',
|
||||
description: 'YYYY-MM-DD',
|
||||
required: false,
|
||||
}),
|
||||
amount: Property.Number({
|
||||
displayName: 'Amount',
|
||||
required: false,
|
||||
}),
|
||||
transactionType: Property.ShortText({
|
||||
displayName: 'Transaction Type',
|
||||
description: 'e.g., sale, rent',
|
||||
required: false,
|
||||
}),
|
||||
propertyAddress: Property.ShortText({
|
||||
displayName: 'Property Address',
|
||||
required: false,
|
||||
}),
|
||||
propertyCity: Property.ShortText({
|
||||
displayName: 'Property City',
|
||||
required: false,
|
||||
}),
|
||||
propertyState: Property.ShortText({
|
||||
displayName: 'Property State',
|
||||
required: false,
|
||||
}),
|
||||
propertyZipcode: Property.ShortText({
|
||||
displayName: 'Property Zipcode',
|
||||
required: false,
|
||||
}),
|
||||
beds: Property.Number({
|
||||
displayName: 'Beds',
|
||||
required: false,
|
||||
}),
|
||||
baths: Property.Number({
|
||||
displayName: 'Baths',
|
||||
required: false,
|
||||
}),
|
||||
sqft: Property.Number({
|
||||
displayName: 'SqFt',
|
||||
required: false,
|
||||
}),
|
||||
mlsNumber: Property.ShortText({
|
||||
displayName: 'MLS Number',
|
||||
required: false,
|
||||
}),
|
||||
closingDate: Property.ShortText({
|
||||
displayName: 'Closing Date',
|
||||
description: 'YYYY-MM-DD',
|
||||
required: false,
|
||||
}),
|
||||
agentId: Property.ShortText({
|
||||
displayName: 'Agent ID',
|
||||
required: false,
|
||||
}),
|
||||
agentName: Property.ShortText({
|
||||
displayName: 'Agent Name',
|
||||
required: false,
|
||||
}),
|
||||
agentEmail: Property.ShortText({
|
||||
displayName: 'Agent Email',
|
||||
required: false,
|
||||
}),
|
||||
source: Property.ShortText({
|
||||
displayName: 'Source',
|
||||
required: false,
|
||||
}),
|
||||
note: Property.LongText({
|
||||
displayName: 'Note',
|
||||
required: false,
|
||||
}),
|
||||
},
|
||||
async run({ auth, propsValue }) {
|
||||
const { leadId, transactionId } = propsValue;
|
||||
|
||||
const payload: any = {};
|
||||
|
||||
if (propsValue.transactionDate)
|
||||
payload.transaction_date = propsValue.transactionDate;
|
||||
if (propsValue.amount !== undefined) payload.amount = propsValue.amount;
|
||||
if (propsValue.transactionType)
|
||||
payload.transaction_type = propsValue.transactionType;
|
||||
if (propsValue.propertyAddress)
|
||||
payload.property_address = propsValue.propertyAddress;
|
||||
if (propsValue.propertyCity)
|
||||
payload.property_city = propsValue.propertyCity;
|
||||
if (propsValue.propertyState)
|
||||
payload.property_state = propsValue.propertyState;
|
||||
if (propsValue.propertyZipcode)
|
||||
payload.property_zipcode = propsValue.propertyZipcode;
|
||||
if (propsValue.beds !== undefined) payload.beds = propsValue.beds;
|
||||
if (propsValue.baths !== undefined) payload.baths = propsValue.baths;
|
||||
if (propsValue.sqft !== undefined) payload.sqft = propsValue.sqft;
|
||||
if (propsValue.mlsNumber) payload.mls_number = propsValue.mlsNumber;
|
||||
if (propsValue.closingDate) payload.closing_date = propsValue.closingDate;
|
||||
if (propsValue.agentId) payload.agent_id = propsValue.agentId;
|
||||
if (propsValue.agentName) payload.agent_name = propsValue.agentName;
|
||||
if (propsValue.agentEmail) payload.agent_email = propsValue.agentEmail;
|
||||
if (propsValue.source) payload.source = propsValue.source;
|
||||
if (propsValue.note) payload.note = propsValue.note;
|
||||
|
||||
const response = await makeRequest(
|
||||
auth.secret_text,
|
||||
HttpMethod.PUT,
|
||||
`/leads/${leadId}/transaction/${transactionId}`,
|
||||
payload
|
||||
);
|
||||
|
||||
return response;
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
import { PieceAuth } from '@activepieces/pieces-framework';
|
||||
|
||||
export const loftyAuth = PieceAuth.SecretText({
|
||||
displayName: 'API Key',
|
||||
description: 'API Key for Lofty',
|
||||
required: true,
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
import { HttpMethod, httpClient } from '@activepieces/pieces-common';
|
||||
|
||||
export const BASE_URL = 'https://api.lofty.com/v1.0';
|
||||
|
||||
export async function makeRequest(
|
||||
apiKey: string,
|
||||
method: HttpMethod,
|
||||
path: string,
|
||||
body?: any
|
||||
): Promise<any> {
|
||||
try {
|
||||
const headers: Record<string, string> = {
|
||||
Authorization: `token ${apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
const response = await httpClient.sendRequest({
|
||||
method,
|
||||
url: `${BASE_URL}${path}`,
|
||||
headers,
|
||||
body: body,
|
||||
});
|
||||
|
||||
return response.body;
|
||||
} catch (error: any) {
|
||||
throw new Error(
|
||||
`MagicalAPI request failed: ${JSON.stringify(
|
||||
error.response?.body || error.message || error
|
||||
)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
import { Property } from '@activepieces/pieces-framework';
|
||||
import { loftyAuth } from './auth';
|
||||
import { makeRequest } from './client';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
|
||||
export const leadIdDropdown = Property.Dropdown({
|
||||
auth: loftyAuth,
|
||||
displayName: 'Lead',
|
||||
description: 'Select a lead',
|
||||
required: true,
|
||||
refreshers: [],
|
||||
options: async ({ auth }) => {
|
||||
if (!auth) {
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
};
|
||||
}
|
||||
try {
|
||||
const response = await makeRequest(
|
||||
auth.secret_text,
|
||||
HttpMethod.GET,
|
||||
'/leads'
|
||||
);
|
||||
|
||||
return {
|
||||
disabled: false,
|
||||
options: response.leads.map((lead: any) => {
|
||||
return {
|
||||
label: `${lead.firstName} ${lead.lastName}`,
|
||||
value: lead.leadId,
|
||||
};
|
||||
}),
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export const transactionIdDropdown = Property.Dropdown({
|
||||
auth: loftyAuth,
|
||||
displayName: 'Transaction',
|
||||
description: 'Select a transaction',
|
||||
required: true,
|
||||
refreshers: ['leadId'],
|
||||
options: async ({ auth, leadId }) => {
|
||||
if (!auth) {
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
};
|
||||
}
|
||||
if (!leadId) {
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
placeholder: 'Select a lead first',
|
||||
};
|
||||
}
|
||||
try {
|
||||
const response = await makeRequest(
|
||||
auth.secret_text,
|
||||
HttpMethod.GET,
|
||||
`/leads/${leadId}/transactions`
|
||||
);
|
||||
|
||||
return {
|
||||
disabled: false,
|
||||
options: response.transactions.map((transaction: any) => {
|
||||
return {
|
||||
label: `${transaction.leadName} - ${transaction.transactionName}`,
|
||||
value: transaction.transactionId,
|
||||
};
|
||||
}),
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"extends": "../../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"importHelpers": true,
|
||||
"noImplicitOverride": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noPropertyAccessFromIndexSignature": true
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../../dist/out-tsc",
|
||||
"declaration": true,
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
Reference in New Issue
Block a user