0
0
mirror of https://github.com/PostHog/posthog.git synced 2024-11-24 00:47:50 +01:00

new site apps

This commit is contained in:
Marius Andra 2024-11-22 16:19:00 +01:00
parent da084ed77e
commit 42579b88ad
16 changed files with 162 additions and 62 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 209 KiB

After

Width:  |  Height:  |  Size: 184 KiB

View File

@ -9,7 +9,7 @@ import { ActivityScope, PipelineTab } from '~/types'
import { AppsManagement } from './AppsManagement'
import { FrontendApps } from './FrontendApps'
import { DESTINATION_TYPES } from './hog-functions-list/constants'
import { DESTINATION_TYPES, SITE_APP_TYPES } from './hog-functions-list/constants'
import { HogFunctionsList } from './hog-functions-list/HogFunctionsList'
import { ImportApps } from './ImportApps'
import { importAppsLogic } from './importAppsLogic'
@ -29,7 +29,8 @@ export function Pipeline(): JSX.Element {
{ key: PipelineTab.Sources, content: <Sources /> },
{ key: PipelineTab.Transformations, content: <Transformations /> },
{ key: PipelineTab.Destinations, content: <HogFunctionsList types={DESTINATION_TYPES} /> },
{ key: PipelineTab.SiteApps, content: <FrontendApps /> },
{ key: PipelineTab.SiteApps, content: <HogFunctionsList types={SITE_APP_TYPES} /> },
{ key: PipelineTab.SiteAppsOld, content: <FrontendApps /> },
]
// Import apps are deprecated, we only show the tab if there are some still enabled

View File

@ -14,7 +14,7 @@ import { urls } from 'scenes/urls'
import { AvailableFeature, PipelineStage, PluginType } from '~/types'
import { frontendAppsLogic } from './frontendAppsLogic'
import { DESTINATION_TYPES } from './hog-functions-list/constants'
import { DESTINATION_TYPES, SITE_APP_TYPES } from './hog-functions-list/constants'
import { NewFunctionsList } from './hog-functions-list/NewHogFunction'
import { HogFunctionConfiguration } from './hogfunctions/HogFunctionConfiguration'
import { PipelineBatchExportConfiguration } from './PipelineBatchExportConfiguration'
@ -106,6 +106,8 @@ export function PipelineNodeNew(params: { stage?: string; id?: string } = {}): J
} else if (stage === PipelineStage.Destination) {
return <NewFunctionsList types={DESTINATION_TYPES} />
} else if (stage === PipelineStage.SiteApp) {
return <NewFunctionsList types={SITE_APP_TYPES} />
} else if (stage === PipelineStage.SiteAppOld) {
return <SiteAppOptionsTable />
} else if (stage === PipelineStage.Source) {
return <NewSourceWizardScene />

View File

@ -33,24 +33,44 @@ export function HogFunctionsList({ types }: HogFunctionsListProps): JSX.Element
return (
<>
<PageHeader
caption="Send your data in real time or in batches to destinations outside of PostHog."
buttons={<NewButton stage={PipelineStage.Destination} />}
/>
<PayGateMini feature={AvailableFeature.DATA_PIPELINES} className="mb-2">
<ProductIntroduction
productName="Pipeline destinations"
thingName="destination"
productKey={ProductKey.PIPELINE_DESTINATIONS}
description="Pipeline destinations allow you to export data outside of PostHog, such as webhooks to Slack."
docsURL="https://posthog.com/docs/cdp"
actionElementOverride={<NewButton stage={PipelineStage.Destination} />}
isEmpty={destinations.length === 0 && !loading}
{types.includes('destination') ? (
<>
<PageHeader
caption="Send your data in real time or in batches to destinations outside of PostHog."
buttons={<NewButton stage={PipelineStage.Destination} />}
/>
<PayGateMini feature={AvailableFeature.DATA_PIPELINES} className="mb-2">
<ProductIntroduction
productName="Pipeline destinations"
thingName="destination"
productKey={ProductKey.PIPELINE_DESTINATIONS}
description="Pipeline destinations allow you to export data outside of PostHog, such as webhooks to Slack."
docsURL="https://posthog.com/docs/cdp"
actionElementOverride={<NewButton stage={PipelineStage.Destination} />}
isEmpty={destinations.length === 0 && !loading}
/>
</PayGateMini>
</>
) : types.includes('site_app') ? (
<PageHeader
caption="Run custom scripts on your website."
buttons={<NewButton stage={PipelineStage.SiteApp} />}
/>
</PayGateMini>
) : (
<PageHeader
caption="Run custom scripts on your website or send your data in real time or in batches to destinations outside of PostHog."
buttons={<NewButton stage={PipelineStage.SiteApp} />}
/>
)}
<HogFunctionsListTable types={types} />
<div className="mt-4" />
<h2>New destinations</h2>
<h2>
{types.includes('destination')
? 'New destinations'
: types.includes('site_app')
? 'New site app'
: 'New Hog function'}
</h2>
<NewFunctionsListTable types={types} />
</>
)
@ -64,9 +84,11 @@ export function HogFunctionsListTable({ types }: HogFunctionsListProps): JSX.Ele
const { toggleNode, deleteNode } = useActions(hogFunctionsListLogic({ types }))
const { resetFilters } = useActions(hogFunctionsListFiltersLogic({ types }))
const isDestination = types.includes('destination')
return (
<div className="space-y-2">
<HogFunctionsListFilters types={types} />
<HogFunctionsListFilters types={types} hideKind={types.includes('site_app')} />
<LemonTable
dataSource={filteredDestinations}
@ -115,33 +137,41 @@ export function HogFunctionsListTable({ types }: HogFunctionsListProps): JSX.Ele
)
},
},
{
title: 'Frequency',
key: 'interval',
render: function RenderFrequency(_, destination) {
return destination.interval
},
},
{
title: 'Last 7 days',
render: function RenderSuccessRate(_, destination) {
return (
<Link
to={urls.pipelineNode(
PipelineStage.Destination,
destination.id,
PipelineNodeTab.Metrics
)}
>
{destination.backend === PipelineBackend.HogFunction ? (
<AppMetricSparkLineV2 id={destination.hog_function.id} />
) : (
<AppMetricSparkLine pipelineNode={destination} />
)}
</Link>
)
},
},
...(isDestination
? [
{
title: 'Frequency',
key: 'interval',
render: function RenderFrequency(_, destination) {
return destination.interval
},
} as LemonTableColumn<Destination, any>,
]
: []),
...(isDestination
? [
{
title: 'Last 7 days',
render: function RenderSuccessRate(_, destination) {
return (
<Link
to={urls.pipelineNode(
PipelineStage.Destination,
destination.id,
PipelineNodeTab.Metrics
)}
>
{destination.backend === PipelineBackend.HogFunction ? (
<AppMetricSparkLineV2 id={destination.hog_function.id} />
) : (
<AppMetricSparkLine pipelineNode={destination} />
)}
</Link>
)
},
} as LemonTableColumn<Destination, any>,
]
: []),
updatedAtColumn() as LemonTableColumn<Destination, any>,
{
title: 'Status',

View File

@ -20,7 +20,7 @@ export type NewFunctionsListProps = {
export function NewFunctionsList({ types }: NewFunctionsListProps): JSX.Element {
return (
<div className="space-y-2">
<PayGateMini feature={AvailableFeature.DATA_PIPELINES} />
{types.includes('destination') ? <PayGateMini feature={AvailableFeature.DATA_PIPELINES} /> : null}
<HogFunctionsListFilters types={types} hideShowPaused />
<NewFunctionsListTable types={types} />
</div>

View File

@ -68,7 +68,7 @@ export const hogFunctionsListLogic = kea<hogFunctionsListLogicType>([
updatePluginConfig: (pluginConfig: PluginConfigTypeNew) => ({ pluginConfig }),
updateBatchExportConfig: (batchExportConfig: BatchExportConfiguration) => ({ batchExportConfig }),
}),
loaders(({ values, actions }) => ({
loaders(({ values, actions, props }) => ({
plugins: [
{} as Record<number, PluginType>,
{
@ -173,7 +173,7 @@ export const hogFunctionsListLogic = kea<hogFunctionsListLogicType>([
{
loadHogFunctions: async () => {
// TODO: Support pagination?
return (await api.hogFunctions.list(undefined, ['destination', 'site_destination'])).results
return (await api.hogFunctions.list(undefined, props.types)).results
},
deleteNodeHogFunction: async ({ destination }) => {

View File

@ -239,7 +239,7 @@ export function HogFunctionConfiguration({ templateId, id }: HogFunctionConfigur
<LemonTextArea disabled={loading} />
</LemonField>
{hogFunction?.template ? (
{hogFunction?.template && !hogFunction.template.id.startsWith('template-blank-') ? (
<LemonDropdown
showArrow
overlay={

View File

@ -313,7 +313,7 @@ export function HogFunctionInputWithSchema({ schema }: HogFunctionInputWithSchem
const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id: schema.key })
const { showSource, configuration } = useValues(hogFunctionConfigurationLogic)
const { setConfigurationValue } = useActions(hogFunctionConfigurationLogic)
const [editing, setEditing] = useState(showSource)
const [editing, setEditing] = useState(false)
const value = configuration.inputs?.[schema.key]

View File

@ -189,9 +189,9 @@ export const hogFunctionConfigurationLogic = kea<hogFunctionConfigurationLogicTy
persistForUnload: true,
setSampleGlobalsError: (error) => ({ error }),
}),
reducers({
reducers(({ props }) => ({
showSource: [
false,
(props.templateId?.startsWith('template-blank-') ? true : false) && !props.id,
{
setShowSource: (_, { showSource }) => showSource,
},
@ -226,7 +226,7 @@ export const hogFunctionConfigurationLogic = kea<hogFunctionConfigurationLogicTy
setSampleGlobalsError: (_, { error }) => error,
},
],
}),
})),
loaders(({ actions, props, values }) => ({
template: [
null as HogFunctionTemplateType | null,

View File

@ -11,7 +11,11 @@ import { ActivityScope, Breadcrumb, PipelineTab } from '~/types'
import type { pipelineLogicType } from './pipelineLogicType'
export const humanFriendlyTabName = (tab: PipelineTab): string => {
return capitalizeFirstLetter(tab).replace(/[-_]/g, ' ')
const label = capitalizeFirstLetter(tab).replace(/[-_]/g, ' ')
if (label.endsWith(' old')) {
return label.slice(0, -4) + ' (old)'
}
return label
}
export const pipelineLogic = kea<pipelineLogicType>([

View File

@ -17,7 +17,7 @@ import {
getPluginConfigFormData,
} from './configUtils'
import { frontendAppsLogic } from './frontendAppsLogic'
import { DESTINATION_TYPES } from './hog-functions-list/constants'
import { DESTINATION_TYPES, SITE_APP_TYPES } from './hog-functions-list/constants'
import { hogFunctionsListLogic } from './hog-functions-list/hogFunctionsListLogic'
import { importAppsLogic } from './importAppsLogic'
import { pipelineAccessLogic } from './pipelineAccessLogic'
@ -174,6 +174,8 @@ export const pipelinePluginConfigurationLogic = kea<pipelinePluginConfigurationL
.findMounted({ types: DESTINATION_TYPES })
?.actions.updatePluginConfig(pluginConfig)
} else if (props.stage === PipelineStage.SiteApp) {
hogFunctionsListLogic.findMounted({ types: SITE_APP_TYPES })?.actions.updatePluginConfig(pluginConfig)
} else if (props.stage === PipelineStage.SiteAppOld) {
frontendAppsLogic.findMounted()?.actions.updatePluginConfig(pluginConfig)
} else if (props.stage === PipelineStage.ImportApp) {
importAppsLogic.findMounted()?.actions.updatePluginConfig(pluginConfig)

View File

@ -707,6 +707,7 @@ export enum PipelineTab {
Transformations = 'transformations',
Destinations = 'destinations',
SiteApps = 'site-apps',
SiteAppsOld = 'site-apps-old',
Sources = 'sources',
ImportApps = 'legacy-sources',
AppsManagement = 'apps-management',
@ -718,6 +719,7 @@ export enum PipelineStage {
Destination = 'destination',
Source = 'source',
SiteApp = 'site-app',
SiteAppOld = 'site-app-old',
ImportApp = 'legacy-source',
}

View File

@ -40,11 +40,14 @@ from .google_cloud_storage.template_google_cloud_storage import (
from .airtable.template_airtable import template as airtable
from .brevo.template_brevo import template as brevo
from .pineapple.template_pineapple_analytics import template as pineapple_analytics
from .pineapple.template_pineapple_mode import template as pineapple_mode
from .pineapple.template_pineapple_rain import template as pineapple_rain
from ._internal.template_broadcast import template_new_broadcast as _broadcast
from ._internal.template_blank import blank_site_destination, blank_site_app
HOG_FUNCTION_TEMPLATES = [
_broadcast,
blank_site_app,
blank_site_destination,
slack,
webhook,
activecampaign,
@ -78,7 +81,7 @@ HOG_FUNCTION_TEMPLATES = [
meta_ads,
microsoft_teams,
pineapple_analytics,
pineapple_mode,
pineapple_rain,
posthog,
rudderstack,
salesforce_create,

View File

@ -0,0 +1,56 @@
from posthog.cdp.templates.hog_function_template import HogFunctionTemplate
blank_site_app: HogFunctionTemplate = HogFunctionTemplate(
status="free",
type="site_app",
id="template-blank-site-app",
name="Blank Site App",
description="Run custom code on your website",
icon_url="/static/hedgehog/builder-hog-03.png",
category=["Custom", "Analytics"],
hog="""
export function onLoad({ inputs, posthog }) {
console.log(`Hello ${inputs.name} from your new Site App!`)
}
""".strip(),
inputs_schema=[
{
"key": "name",
"type": "string",
"label": "Name",
"description": "What's your name?",
"default": "Max",
},
],
)
blank_site_destination: HogFunctionTemplate = HogFunctionTemplate(
status="free",
type="site_destination",
id="template-blank-site-destination",
name="Blank Site Destination",
description="Run code on your site when an event is sent to PostHog",
icon_url="/static/hedgehog/builder-hog-01.png",
category=["Custom", "Analytics"],
hog="""
export async function onLoad({ inputs, posthog }) {
console.log('🦔 Loading (takes 1 sec)', { inputs })
// onEvent will not be called until this function resolves
await new Promise((resolve) => window.setTimeout(resolve, 1000))
console.log("🦔 Script loaded")
}
export function onEvent({ posthog, ...globals }) {
console.log(`🦔 Sending event: ${globals.event.event}`, globals)
}
""".strip(),
inputs_schema=[
{
"key": "name",
"type": "string",
"label": "Name",
"description": "What's your name?",
"default": "Max",
},
],
)

View File

@ -3,10 +3,10 @@ from posthog.cdp.templates.hog_function_template import HogFunctionTemplate
template: HogFunctionTemplate = HogFunctionTemplate(
status="free",
type="site_app",
id="template-pineapple-mode",
name="Pineapple Mode",
description="Make any website better by adding pineapples",
icon_url="/static/services/pineapple.png",
id="template-pineapple-rain",
name="Pineapple Rain",
description="Make any website better by adding raining pineapples",
icon_url="/static/services/pineapple-rain.png",
category=["Custom", "Analytics"],
hog="""
const style = `