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,117 @@
import { Property, createAction } from '@activepieces/pieces-framework';
import { makeClient } from '../common';
import { moxieCRMAuth } from '../..';
export const moxieCreateClientAction = createAction({
auth: moxieCRMAuth,
name: 'moxie_create_client',
displayName: 'Create a Client',
description: 'Create a new client record in moxie CRM.',
props: {
name: Property.ShortText({
displayName: 'Name',
required: true,
}),
clientType: Property.StaticDropdown({
displayName: 'Client Type',
required: true,
defaultValue: 'Client',
options: {
disabled: false,
options: [
{
label: 'Client',
value: 'Client',
},
{
label: 'Prospect',
value: 'Prospect',
},
],
},
}),
initials: Property.ShortText({
displayName: 'Initials',
required: false,
}),
address1: Property.ShortText({
displayName: 'Address 1',
required: false,
}),
address2: Property.ShortText({
displayName: 'Address 2',
required: false,
}),
city: Property.ShortText({
displayName: 'City',
required: false,
}),
locality: Property.ShortText({
displayName: 'Locality',
required: false,
}),
postal: Property.ShortText({
displayName: 'Postal',
required: false,
}),
country: Property.ShortText({
displayName: 'Country',
required: false,
description: 'ISO 3166-1 alpha-2 country code',
}),
website: Property.ShortText({
displayName: 'Website',
required: false,
}),
phone: Property.ShortText({
displayName: 'Phone',
required: false,
}),
color: Property.ShortText({
displayName: 'Color',
required: false,
}),
taxId: Property.ShortText({
displayName: 'Tax ID',
required: false,
}),
leadSource: Property.ShortText({
displayName: 'Lead Source',
required: false,
}),
archive: Property.Checkbox({
displayName: 'Archive ?',
required: true,
defaultValue: false,
}),
payInstructions: Property.LongText({
displayName: 'Pay Instructions',
required: false,
}),
hourlyAmount: Property.Number({
displayName: 'Hourly Amount',
required: false,
}),
roundingIncrement: Property.Number({
displayName: 'Rounding Increment',
required: false,
}),
currency: Property.ShortText({
displayName: 'Currency',
required: false,
description: 'Valid 3-Letter ISO 4217 currency code.',
}),
stripeClientId: Property.ShortText({
displayName: 'Stripe Client ID',
required: false,
}),
notes: Property.LongText({
displayName: 'Notes',
required: false,
}),
},
async run({ auth, propsValue }) {
const client = await makeClient(auth);
return await client.createClient(propsValue);
},
});

View File

@@ -0,0 +1,166 @@
import {
Property,
createAction,
PiecePropValueSchema,
} from '@activepieces/pieces-framework';
import { makeClient, reformatDate } from '../common';
import { moxieCRMAuth } from '../..';
export const moxieCreateProjectAction = createAction({
auth: moxieCRMAuth,
name: 'moxie_create_project',
description: 'Creates a new project in moxie CRM.',
displayName: 'Create a Project',
props: {
name: Property.ShortText({
displayName: 'Project Name',
required: true,
}),
clientName: Property.Dropdown({
auth: moxieCRMAuth,
displayName: 'Client',
required: true,
refreshers: [],
options: async ({ auth }) => {
if (!auth) {
return {
disabled: true,
placeholder: 'Connect your account first',
options: [],
};
}
const client = await makeClient(
auth
);
const clients = await client.listClients();
return {
disabled: false,
options: clients.map((client) => {
return {
label: client.name,
value: client.name,
};
}),
};
},
}),
startDate: Property.DateTime({
displayName: 'Start Date',
description: 'Please enter date in YYYY-MM-DD format.',
required: false,
}),
dueDate: Property.DateTime({
displayName: 'Due Date',
description: 'Please enter date in YYYY-MM-DD format.',
required: false,
}),
portalAccess: Property.StaticDropdown({
displayName: 'Client Portal Access',
description: 'One of: None, Overview, Full access, or Read only.',
required: true,
defaultValue: 'Read Only',
options: {
options: [
{
label: 'Not Visible',
value: 'None',
},
{
label: 'Overview only',
value: 'Overview',
},
{
label: 'Read only project collaboration',
value: 'Read only',
},
{
label: 'Full project collaboration',
value: 'Full access',
},
],
},
}),
showTimeWorkedInPortal: Property.Checkbox({
displayName: 'Show time worked in portal ?',
required: false,
defaultValue: true,
}),
feeType: Property.StaticDropdown({
displayName: 'Fee Type',
description: 'One of: Hourly, Fixed Price, Retainer, Per Item.',
required: true,
options: {
options: [
{
label: 'Hourly',
value: 'Hourly',
},
{
label: 'Fixed Price',
value: 'Fixed Price',
},
{
label: 'Retainer',
value: 'Retainer',
},
{
label: 'Per Item',
value: 'Per Item',
},
],
},
}),
amount: Property.Number({
displayName: 'Amount',
required: false,
defaultValue: 0,
}),
estimateMax: Property.Number({
displayName: 'Estimate maximum Amount',
required: false,
defaultValue: 0,
}),
estimateMin: Property.Number({
displayName: 'Estimate minimum Amount',
required: false,
defaultValue: 0,
}),
taxable: Property.Checkbox({
displayName: 'Is amount taxable ?',
required: false,
defaultValue: false,
}),
},
async run({ auth, propsValue }) {
const {
name,
clientName,
portalAccess,
showTimeWorkedInPortal,
feeType,
amount,
estimateMax,
estimateMin,
taxable,
} = propsValue;
const dueDate = reformatDate(propsValue.dueDate) as string;
const startDate = reformatDate(propsValue.startDate) as string;
const client = await makeClient(auth);
return await client.createProject({
name,
clientName,
startDate,
dueDate,
portalAccess,
showTimeWorkedInPortal,
feeSchedule: {
feeType,
amount,
estimateMax,
estimateMin,
taxable,
},
});
},
});

View File

@@ -0,0 +1,165 @@
import {
Property,
createAction,
PiecePropValueSchema,
} from '@activepieces/pieces-framework';
import { makeClient, reformatDate } from '../common';
import { moxieCRMAuth } from '../..';
export const moxieCreateTaskAction = createAction({
auth: moxieCRMAuth,
name: 'moxie_create_task',
displayName: 'Create a Task',
description: 'Create a task in project.',
props: {
name: Property.ShortText({
displayName: 'Name',
required: true,
}),
clientName: Property.Dropdown({
auth: moxieCRMAuth,
displayName: 'Client Name',
description: 'Exact match of a client name in your CRM',
required: true,
refreshers: [],
options: async ({ auth }) => {
if (!auth) {
return {
disabled: true,
placeholder: 'Connect your account first',
options: [],
};
}
const client = await makeClient(
auth
);
const clients = await client.listClients();
return {
disabled: false,
options: clients.map((client) => {
return {
label: client.name,
value: client.name,
};
}),
};
},
}),
projectName: Property.Dropdown({
auth: moxieCRMAuth,
displayName: 'Project Name',
description: 'Exact match of a project that is owned by the client.',
required: true,
refreshers: ['clientName'],
options: async ({ auth, clientName }) => {
if (!auth || !clientName) {
return {
disabled: true,
placeholder: 'Connect your account first and select client',
options: [],
};
}
const client = await makeClient(
auth
);
const projects = await client.searchProjects(clientName as string);
return {
disabled: false,
options: projects.map((project) => {
return {
label: project.name,
value: project.name,
};
}),
};
},
}),
status: Property.Dropdown({
auth: moxieCRMAuth,
displayName: 'Status',
required: true,
defaultValue: 'Not Started',
refreshers: [],
options: async ({ auth }) => {
if (!auth) {
return {
disabled: true,
placeholder: 'Connect your account first',
options: [],
};
}
const client = await makeClient(
auth
);
const stages = await client.listProjectTaskStages();
return {
disabled: false,
options: stages.map((stage) => {
return {
label: stage.label,
value: stage.label,
};
}),
};
},
}),
description: Property.LongText({
displayName: 'Description',
required: false,
}),
startDate: Property.DateTime({
displayName: 'Start Date',
required: false,
description: 'ISO 8601 format date i.e. 2023-07-20',
}),
dueDate: Property.DateTime({
displayName: 'Due Date',
required: false,
description: 'ISO 8601 format date i.e. 2023-07-20',
}),
priority: Property.Number({
displayName: 'Priority',
required: false,
description: 'Numeric priority for sorting in kanban.',
}),
tasks: Property.Array({
displayName: 'Subtasks',
required: false,
}),
assignedTo: Property.Array({
displayName: 'Assigned To',
required: false,
description: 'email addresses of users in the workspace.',
}),
customValues: Property.Object({
displayName: 'Custom Values',
required: false,
}),
},
async run({ auth, propsValue }) {
const { name, clientName, projectName, status, description, priority } =
propsValue;
const dueDate = reformatDate(propsValue.dueDate) as string;
const startDate = reformatDate(propsValue.startDate) as string;
const tasks = (propsValue.tasks as string[]) || [];
const assignedTo = (propsValue.assignedTo as string[]) || [];
const customValues =
(propsValue.customValues as Record<string, string>) || {};
const client = await makeClient(auth);
return await client.createTask({
name,
clientName,
projectName,
status,
description,
dueDate,
startDate,
priority,
tasks,
assignedTo,
customValues,
});
},
});