Add event status trigger, improve test coverage, and UI enhancements

- Add event-status-changed trigger for SmoothSchedule Activepieces piece
- Add comprehensive test coverage for payments, tickets, messaging, mobile
- Add test coverage for core services, signals, consumers, and views
- Improve Activepieces UI: templates, billing hooks, project hooks
- Update marketing automation showcase and workflow visual components
- Add public API endpoints for availability

🤖 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-20 00:19:12 -05:00
parent f3e1b8f8bf
commit 2417bb8313
51 changed files with 13823 additions and 340 deletions

View File

@@ -123,7 +123,15 @@ export const billingQueries = {
usePlatformSubscription: (platformId: string) => {
return useQuery({
queryKey: billingKeys.platformSubscription(platformId),
queryFn: platformBillingApi.getSubscriptionInfo,
queryFn: async () => {
try {
return await platformBillingApi.getSubscriptionInfo();
} catch {
// Return null if endpoint doesn't exist (community edition)
return null;
}
},
retry: false, // Don't retry on failure
});
},
};

View File

@@ -12,20 +12,26 @@ export const projectMembersHooks = {
const query = useQuery<ProjectMemberWithUser[]>({
queryKey: ['project-members', authenticationSession.getProjectId()],
queryFn: async () => {
const projectId = authenticationSession.getProjectId();
assertNotNullOrUndefined(projectId, 'Project ID is null');
const res = await projectMembersApi.list({
projectId: projectId,
projectRoleId: undefined,
cursor: undefined,
limit: 100,
});
return res.data;
try {
const projectId = authenticationSession.getProjectId();
assertNotNullOrUndefined(projectId, 'Project ID is null');
const res = await projectMembersApi.list({
projectId: projectId,
projectRoleId: undefined,
cursor: undefined,
limit: 100,
});
return res.data;
} catch {
// Return empty array if endpoint doesn't exist (community edition)
return [];
}
},
staleTime: Infinity,
retry: false, // Don't retry on failure
});
return {
projectMembers: query.data,
projectMembers: query.data ?? [],
isLoading: query.isLoading,
refetch: query.refetch,
};

View File

@@ -79,10 +79,14 @@ export const TemplateCard = ({
className="rounded-lg border border-solid border-dividers overflow-hidden"
>
<div className="flex items-center gap-2 p-4">
<PieceIconList
trigger={template.flows![0].trigger}
maxNumberOfIconsToShow={2}
/>
{template.flows && template.flows.length > 0 && template.flows[0].trigger ? (
<PieceIconList
trigger={template.flows[0].trigger}
maxNumberOfIconsToShow={2}
/>
) : (
<div className="h-8 w-8 rounded bg-muted" />
)}
</div>
<div className="text-sm font-medium px-4 min-h-16">{template.name}</div>
<div className="py-2 px-4 gap-1 flex items-center">

View File

@@ -13,11 +13,15 @@ export const TemplateDetailsView = ({ template }: TemplateDetailsViewProps) => {
return (
<div className="px-2">
<div className="mb-4 p-8 flex items-center justify-center gap-2 width-full bg-green-300 rounded-lg">
<PieceIconList
size="xxl"
trigger={template.flows![0].trigger}
maxNumberOfIconsToShow={3}
/>
{template.flows && template.flows.length > 0 && template.flows[0].trigger ? (
<PieceIconList
size="xxl"
trigger={template.flows[0].trigger}
maxNumberOfIconsToShow={3}
/>
) : (
<div className="h-16 w-16 rounded bg-muted" />
)}
</div>
<ScrollArea className="px-2 min-h-[156px] h-[calc(70vh-144px)] max-h-[536px]">
<div className="mb-4 text-lg font-medium font-black">

View File

@@ -1,7 +1,11 @@
import { useQuery } from '@tanstack/react-query';
import { useState } from 'react';
import { ListTemplatesRequestQuery, Template } from '@activepieces/shared';
import {
ListTemplatesRequestQuery,
Template,
TemplateType,
} from '@activepieces/shared';
import { templatesApi } from '../lib/templates-api';
@@ -9,7 +13,7 @@ export const useTemplates = (request: ListTemplatesRequestQuery) => {
const [search, setSearch] = useState<string>('');
const { data: templates, isLoading } = useQuery<Template[], Error>({
queryKey: ['templates'],
queryKey: ['templates', request.type],
queryFn: async () => {
const templates = await templatesApi.list(request);
return templates.data;
@@ -34,3 +38,86 @@ export const useTemplates = (request: ListTemplatesRequestQuery) => {
setSearch,
};
};
/**
* Hook to fetch both custom (platform) and official templates
*/
export const useAllTemplates = () => {
const [search, setSearch] = useState<string>('');
// Fetch custom templates (platform-specific)
const {
data: customTemplates,
isLoading: isLoadingCustom,
} = useQuery<Template[], Error>({
queryKey: ['templates', TemplateType.CUSTOM],
queryFn: async () => {
try {
const templates = await templatesApi.list({
type: TemplateType.CUSTOM,
});
return templates.data;
} catch {
// If custom templates fail (e.g., feature not enabled), return empty array
return [];
}
},
staleTime: 0,
});
// Fetch official templates from Activepieces cloud
const {
data: officialTemplates,
isLoading: isLoadingOfficial,
} = useQuery<Template[], Error>({
queryKey: ['templates', TemplateType.OFFICIAL],
queryFn: async () => {
try {
const templates = await templatesApi.list({
type: TemplateType.OFFICIAL,
});
return templates.data;
} catch {
return [];
}
},
staleTime: 0,
});
const isLoading = isLoadingCustom || isLoadingOfficial;
// Combine all templates
const allTemplates = [
...(customTemplates || []),
...(officialTemplates || []),
];
const filteredTemplates = allTemplates.filter((template) => {
const templateName = template.name.toLowerCase();
const templateDescription = template.description.toLowerCase();
return (
templateName.includes(search.toLowerCase()) ||
templateDescription.includes(search.toLowerCase())
);
});
// Separate filtered results by type
const filteredCustomTemplates = filteredTemplates.filter(
(t) => t.type === TemplateType.CUSTOM,
);
const filteredOfficialTemplates = filteredTemplates.filter(
(t) => t.type === TemplateType.OFFICIAL,
);
return {
customTemplates: customTemplates || [],
officialTemplates: officialTemplates || [],
allTemplates,
filteredTemplates,
filteredCustomTemplates,
filteredOfficialTemplates,
isLoading,
search,
setSearch,
};
};