Enhance Activepieces automation flows with restore UI and publishing
- Add "Restore Defaults" dropdown to Automations page with confirmation - Create flows in "Defaults" folder for organization - Pre-populate trigger sample data when creating/restoring flows - Auto-publish flows (lock and enable) after creation - Fix email template context variables to match template tags - Fix dark mode logo switching in Activepieces iframe - Add iframe refresh on flow restore - Auto-populate business context (name, email, phone, address) in emails 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,6 @@ import { listCustomersAction } from './lib/actions/list-customers';
|
||||
import { sendEmailAction } from './lib/actions/send-email';
|
||||
import { listEmailTemplatesAction } from './lib/actions/list-email-templates';
|
||||
import { eventCreatedTrigger, eventUpdatedTrigger, eventCancelledTrigger, eventStatusChangedTrigger, paymentReceivedTrigger, upcomingEventsTrigger } from './lib/triggers';
|
||||
import { API_URL } from './lib/common';
|
||||
|
||||
/**
|
||||
* SmoothSchedule Authentication
|
||||
|
||||
@@ -38,6 +38,39 @@ interface PaymentData {
|
||||
} | null;
|
||||
}
|
||||
|
||||
const SAMPLE_PAYMENT_DATA: PaymentData = {
|
||||
id: 12345,
|
||||
payment_intent_id: 'pi_3QDEr5GvIfP3a7s90bcd1234',
|
||||
amount: '50.00',
|
||||
currency: 'usd',
|
||||
type: 'deposit',
|
||||
status: 'SUCCEEDED',
|
||||
created_at: '2024-12-01T10:00:00Z',
|
||||
completed_at: '2024-12-01T10:00:05Z',
|
||||
event: {
|
||||
id: 100,
|
||||
title: 'Consultation with John Doe',
|
||||
start_time: '2024-12-15T14:00:00Z',
|
||||
end_time: '2024-12-15T15:00:00Z',
|
||||
status: 'SCHEDULED',
|
||||
deposit_amount: '50.00',
|
||||
final_price: '200.00',
|
||||
remaining_balance: '150.00',
|
||||
},
|
||||
service: {
|
||||
id: 1,
|
||||
name: 'Consultation',
|
||||
price: '200.00',
|
||||
},
|
||||
customer: {
|
||||
id: 50,
|
||||
first_name: 'John',
|
||||
last_name: 'Doe',
|
||||
email: 'john.doe@example.com',
|
||||
phone: '+1-555-0100',
|
||||
},
|
||||
};
|
||||
|
||||
export const paymentReceivedTrigger = createTrigger({
|
||||
auth: smoothScheduleAuth,
|
||||
name: 'payment_received',
|
||||
@@ -78,15 +111,26 @@ export const paymentReceivedTrigger = createTrigger({
|
||||
queryParams['type'] = paymentType;
|
||||
}
|
||||
|
||||
const payments = await makeRequest<PaymentData[]>(
|
||||
auth,
|
||||
HttpMethod.GET,
|
||||
'/payments/',
|
||||
undefined,
|
||||
queryParams
|
||||
);
|
||||
try {
|
||||
const payments = await makeRequest<PaymentData[]>(
|
||||
auth,
|
||||
HttpMethod.GET,
|
||||
'/payments/',
|
||||
undefined,
|
||||
queryParams
|
||||
);
|
||||
|
||||
return payments;
|
||||
// Return real data if available, otherwise return sample data
|
||||
if (payments && payments.length > 0) {
|
||||
return payments;
|
||||
}
|
||||
} catch (error) {
|
||||
// Fall through to sample data on error
|
||||
console.error('Error fetching payments for sample data:', error);
|
||||
}
|
||||
|
||||
// Return static sample data if no real payments exist
|
||||
return [SAMPLE_PAYMENT_DATA];
|
||||
},
|
||||
async run(context) {
|
||||
const auth = context.auth as SmoothScheduleAuth;
|
||||
@@ -121,36 +165,5 @@ export const paymentReceivedTrigger = createTrigger({
|
||||
|
||||
return payments;
|
||||
},
|
||||
sampleData: {
|
||||
id: 12345,
|
||||
payment_intent_id: 'pi_3QDEr5GvIfP3a7s90bcd1234',
|
||||
amount: '50.00',
|
||||
currency: 'usd',
|
||||
type: 'deposit',
|
||||
status: 'SUCCEEDED',
|
||||
created_at: '2024-12-01T10:00:00Z',
|
||||
completed_at: '2024-12-01T10:00:05Z',
|
||||
event: {
|
||||
id: 100,
|
||||
title: 'Consultation with John Doe',
|
||||
start_time: '2024-12-15T14:00:00Z',
|
||||
end_time: '2024-12-15T15:00:00Z',
|
||||
status: 'SCHEDULED',
|
||||
deposit_amount: '50.00',
|
||||
final_price: '200.00',
|
||||
remaining_balance: '150.00',
|
||||
},
|
||||
service: {
|
||||
id: 1,
|
||||
name: 'Consultation',
|
||||
price: '200.00',
|
||||
},
|
||||
customer: {
|
||||
id: 50,
|
||||
first_name: 'John',
|
||||
last_name: 'Doe',
|
||||
email: 'john.doe@example.com',
|
||||
phone: '+1-555-0100',
|
||||
},
|
||||
},
|
||||
sampleData: SAMPLE_PAYMENT_DATA,
|
||||
});
|
||||
|
||||
@@ -1,15 +1,64 @@
|
||||
import { t } from 'i18next';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { flagsHooks } from '@/hooks/flags-hooks';
|
||||
import { useTheme } from '@/components/theme-provider';
|
||||
|
||||
const FullLogo = () => {
|
||||
const branding = flagsHooks.useWebsiteBranding();
|
||||
const { theme } = useTheme();
|
||||
|
||||
// Track resolved theme from DOM (handles 'system' theme correctly)
|
||||
const [isDark, setIsDark] = useState(() =>
|
||||
document.documentElement.classList.contains('dark')
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// Update when theme changes - check the actual applied class
|
||||
const checkDark = () => {
|
||||
setIsDark(document.documentElement.classList.contains('dark'));
|
||||
};
|
||||
checkDark();
|
||||
|
||||
// Observe class changes on documentElement
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
for (const mutation of mutations) {
|
||||
if (mutation.attributeName === 'class') {
|
||||
checkDark();
|
||||
}
|
||||
}
|
||||
});
|
||||
observer.observe(document.documentElement, { attributes: true });
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, [theme]);
|
||||
|
||||
// Support dark mode by switching logo URLs
|
||||
// Light logo (dark text) for light mode, dark logo (light text) for dark mode
|
||||
const baseLogoUrl = branding.logos.fullLogoUrl;
|
||||
|
||||
// Compute the appropriate logo URL based on theme
|
||||
let logoUrl = baseLogoUrl;
|
||||
if (isDark) {
|
||||
// Need dark logo (light text for dark background)
|
||||
if (baseLogoUrl.includes('-light.svg')) {
|
||||
logoUrl = baseLogoUrl.replace('-light.svg', '-dark.svg');
|
||||
} else if (!baseLogoUrl.includes('-dark.svg')) {
|
||||
logoUrl = baseLogoUrl.replace(/\.svg$/, '-dark.svg');
|
||||
}
|
||||
} else {
|
||||
// Need light logo (dark text for light background)
|
||||
if (baseLogoUrl.includes('-dark.svg')) {
|
||||
logoUrl = baseLogoUrl.replace('-dark.svg', '-light.svg');
|
||||
}
|
||||
// Otherwise use base URL as-is (assumed to be light version)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="h-[60px]">
|
||||
<img
|
||||
className="h-full"
|
||||
src={branding.logos.fullLogoUrl}
|
||||
src={logoUrl}
|
||||
alt={t('logo')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -65,8 +65,8 @@ export function generateTheme({
|
||||
|
||||
export const defaultTheme = generateTheme({
|
||||
primaryColor: '#6e41e2',
|
||||
websiteName: 'Activepieces',
|
||||
fullLogoUrl: 'https://cdn.activepieces.com/brand/full-logo.png',
|
||||
websiteName: 'Automation Builder',
|
||||
fullLogoUrl: 'https://smoothschedule.nyc3.digitaloceanspaces.com/static/images/automation-builder-logo-light.svg',
|
||||
favIconUrl: 'https://cdn.activepieces.com/brand/favicon.ico',
|
||||
logoIconUrl: 'https://cdn.activepieces.com/brand/logo.svg',
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user