Skip to content

Commit 9406650

Browse files
committed
Improve performance adding integrations from editor sidebar
1 parent 51aeec2 commit 9406650

File tree

80 files changed

+1422
-819
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

80 files changed

+1422
-819
lines changed

apps/gateway/src/presenters/documentPresenter.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,14 @@ export function documentPresenterWithProviderAndMetadata({
3838
const parameters =
3939
rawParams.length > 0
4040
? rawParams.reduce(
41-
(acc, rawParam) => {
42-
if (acc[rawParam]) return acc
43-
acc[rawParam] = { type: ParameterType.Text }
41+
(acc, rawParam) => {
42+
if (acc[rawParam]) return acc
43+
acc[rawParam] = { type: ParameterType.Text }
4444

45-
return acc
46-
},
47-
{ ...configParams },
48-
)
45+
return acc
46+
},
47+
{ ...configParams },
48+
)
4949
: configParams
5050

5151
return {
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
'use server'
2+
3+
import { z } from 'zod'
4+
import { withAdmin } from '../../procedures'
5+
import { invalidateAppCache } from '@latitude-data/core/services/integrations/pipedream/cache/invalidate'
6+
7+
export const invalidateAppCacheAction = withAdmin
8+
.inputSchema(
9+
z.object({
10+
nameSlug: z.string().min(1, { message: 'App slug is required' }),
11+
}),
12+
)
13+
.action(async ({ parsedInput }) => {
14+
const result = await invalidateAppCache(parsedInput.nameSlug)
15+
return result.unwrap()
16+
})
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
'use server'
2+
3+
import { withAdmin } from '../../procedures'
4+
import { invalidateFirstPageCache } from '@latitude-data/core/services/integrations/pipedream/cache/invalidate'
5+
6+
export const invalidateFirstPageCacheAction = withAdmin.action(async () => {
7+
const result = await invalidateFirstPageCache()
8+
return result.unwrap()
9+
})
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
'use server'
2+
3+
import { z } from 'zod'
4+
import { withAdmin } from '../../procedures'
5+
import { searchAppCacheKeys } from '@latitude-data/core/services/integrations/pipedream/cache/invalidate'
6+
7+
export const searchAppCacheKeysAction = withAdmin
8+
.inputSchema(
9+
z.object({
10+
searchTerm: z.string(),
11+
}),
12+
)
13+
.action(async ({ parsedInput }) => {
14+
const result = await searchAppCacheKeys(parsedInput.searchTerm)
15+
return result.unwrap()
16+
})

apps/web/src/app/(admin)/backoffice/_components/BackofficeTabs/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ export function BackofficeTabs({ children }: { children: ReactNode }) {
5050
label: 'Promocodes',
5151
value: BackofficeRoutes.promocodes,
5252
},
53+
{
54+
label: 'Integrations',
55+
value: BackofficeRoutes.integrations,
56+
},
5357
]}
5458
selected={selected}
5559
onSelect={(value) => {
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
'use client'
2+
3+
import React, { useState } from 'react'
4+
import { Button } from '@latitude-data/web-ui/atoms/Button'
5+
import { Text } from '@latitude-data/web-ui/atoms/Text'
6+
import { Input } from '@latitude-data/web-ui/atoms/Input'
7+
import useLatitudeAction from '$/hooks/useLatitudeAction'
8+
import { invalidateAppCacheAction } from '$/actions/admin/integrations/invalidateAppCache'
9+
import { searchAppCacheKeysAction } from '$/actions/admin/integrations/searchAppCacheKeys'
10+
import { toast } from '@latitude-data/web-ui/atoms/Toast'
11+
import {
12+
Table,
13+
TableBody,
14+
TableCell,
15+
TableHead,
16+
TableHeader,
17+
TableRow,
18+
} from '@latitude-data/web-ui/atoms/Table'
19+
20+
export function InvalidateAppCache() {
21+
const [searchTerm, setSearchTerm] = useState('')
22+
const [searchResults, setSearchResults] = useState<string[]>([])
23+
const [invalidatedApps, setInvalidatedApps] = useState<
24+
{ nameSlug: string; timestamp: Date }[]
25+
>([])
26+
27+
const { execute: searchApps, isPending: isSearching } = useLatitudeAction(
28+
searchAppCacheKeysAction,
29+
{
30+
onSuccess: ({ data }) => {
31+
setSearchResults(data)
32+
if (data.length === 0) {
33+
toast({
34+
title: 'No Results',
35+
description: 'No cached apps found matching your search',
36+
})
37+
}
38+
},
39+
onError: (error) => {
40+
toast({
41+
title: 'Search Error',
42+
description: error.message || 'Failed to search cache keys',
43+
variant: 'destructive',
44+
})
45+
},
46+
},
47+
)
48+
49+
const { execute: invalidateApp, isPending: isInvalidating } =
50+
useLatitudeAction(invalidateAppCacheAction, {
51+
onError: (error) => {
52+
toast({
53+
title: 'Error',
54+
description: error.message || 'Failed to invalidate app cache',
55+
variant: 'destructive',
56+
})
57+
},
58+
})
59+
60+
const handleSearch = (e: React.FormEvent) => {
61+
e.preventDefault()
62+
if (searchTerm.trim()) {
63+
searchApps({ searchTerm: searchTerm.trim() })
64+
}
65+
}
66+
67+
const handleInvalidate = async (nameSlug: string) => {
68+
const [result, _error] = await invalidateApp({ nameSlug })
69+
if (result) {
70+
setInvalidatedApps((prev) => [
71+
{ nameSlug, timestamp: new Date() },
72+
...prev.slice(0, 9),
73+
])
74+
setSearchResults((prev) => prev.filter((slug) => slug !== nameSlug))
75+
toast({
76+
title: 'Cache Invalidated',
77+
description: `Successfully cleared cache for "${nameSlug}". Deleted ${result.deletedCount} key(s).`,
78+
})
79+
}
80+
}
81+
82+
return (
83+
<div className='space-y-4'>
84+
<div className='flex flex-col gap-2'>
85+
<Text.H4B>Invalidate Specific App Cache</Text.H4B>
86+
<Text.H5 color='foregroundMuted'>
87+
Search for a specific Pipedream app by nameSlug or display name, then
88+
clear its cache. This will clear both the full config and slim
89+
versions of the cached app data.
90+
</Text.H5>
91+
</div>
92+
93+
<form onSubmit={handleSearch} className='flex gap-2'>
94+
<div className='flex-1'>
95+
<Input
96+
name='searchTerm'
97+
placeholder='Search by app nameSlug or display name (e.g., slack, github)'
98+
value={searchTerm}
99+
onChange={(e) => setSearchTerm(e.target.value)}
100+
/>
101+
</div>
102+
<Button
103+
type='submit'
104+
disabled={isSearching || !searchTerm.trim()}
105+
iconProps={{ name: 'search' }}
106+
>
107+
{isSearching ? 'Searching...' : 'Search'}
108+
</Button>
109+
</form>
110+
111+
{searchResults.length > 0 && (
112+
<div className='space-y-2'>
113+
<Text.H5B>Search Results ({searchResults.length})</Text.H5B>
114+
<Table>
115+
<TableHeader>
116+
<TableRow>
117+
<TableHead>App Slug</TableHead>
118+
<TableHead>Actions</TableHead>
119+
</TableRow>
120+
</TableHeader>
121+
<TableBody>
122+
{searchResults.map((nameSlug) => (
123+
<TableRow key={nameSlug}>
124+
<TableCell>{nameSlug}</TableCell>
125+
<TableCell>
126+
<Button
127+
variant='ghost'
128+
size='small'
129+
onClick={() => handleInvalidate(nameSlug)}
130+
disabled={isInvalidating}
131+
iconProps={{ name: 'trash' }}
132+
>
133+
Clear Cache
134+
</Button>
135+
</TableCell>
136+
</TableRow>
137+
))}
138+
</TableBody>
139+
</Table>
140+
</div>
141+
)}
142+
143+
{invalidatedApps.length > 0 && (
144+
<div className='space-y-2'>
145+
<Text.H5B>Recently Invalidated</Text.H5B>
146+
<Table>
147+
<TableHeader>
148+
<TableRow>
149+
<TableHead>App Slug</TableHead>
150+
<TableHead>Cleared At</TableHead>
151+
</TableRow>
152+
</TableHeader>
153+
<TableBody>
154+
{invalidatedApps.map(({ nameSlug, timestamp }) => (
155+
<TableRow key={`${nameSlug}-${timestamp.getTime()}`}>
156+
<TableCell>{nameSlug}</TableCell>
157+
<TableCell>{timestamp.toLocaleString()}</TableCell>
158+
</TableRow>
159+
))}
160+
</TableBody>
161+
</Table>
162+
</div>
163+
)}
164+
</div>
165+
)
166+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
'use client'
2+
3+
import { useState } from 'react'
4+
import { Button } from '@latitude-data/web-ui/atoms/Button'
5+
import { Text } from '@latitude-data/web-ui/atoms/Text'
6+
import useLatitudeAction from '$/hooks/useLatitudeAction'
7+
import { invalidateFirstPageCacheAction } from '$/actions/admin/integrations/invalidateFirstPageCache'
8+
import { toast } from '@latitude-data/web-ui/atoms/Toast'
9+
10+
export function InvalidateFirstPageCache() {
11+
const [lastInvalidated, setLastInvalidated] = useState<Date | null>(null)
12+
13+
const { execute: invalidateCache, isPending } = useLatitudeAction(
14+
invalidateFirstPageCacheAction,
15+
{
16+
onSuccess: ({ data }) => {
17+
setLastInvalidated(new Date())
18+
toast({
19+
title: 'Cache Invalidated',
20+
description: `Successfully cleared first page cache. Deleted ${data.deletedCount} key(s).`,
21+
})
22+
},
23+
onError: (error) => {
24+
toast({
25+
title: 'Error',
26+
description: error.message || 'Failed to invalidate cache',
27+
variant: 'destructive',
28+
})
29+
},
30+
},
31+
)
32+
33+
return (
34+
<div className='space-y-4'>
35+
<div className='flex flex-col gap-2'>
36+
<Text.H4B>First Page Cache</Text.H4B>
37+
<Text.H5 color='foregroundMuted'>
38+
Clear the cached first page of Pipedream apps. This cache is used when
39+
users first load the integrations list without any search query.
40+
</Text.H5>
41+
</div>
42+
43+
<div className='flex items-center gap-4'>
44+
<Button
45+
fancy
46+
onClick={() => invalidateCache(undefined)}
47+
disabled={isPending}
48+
iconProps={{ name: 'trash' }}
49+
>
50+
{isPending ? 'Clearing...' : 'Clear First Page Cache'}
51+
</Button>
52+
53+
{lastInvalidated && (
54+
<Text.H5 color='foregroundMuted'>
55+
Last cleared: {lastInvalidated.toLocaleTimeString()}
56+
</Text.H5>
57+
)}
58+
</div>
59+
</div>
60+
)
61+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { InvalidateFirstPageCache } from './InvalidateFirstPageCache'
2+
export { InvalidateAppCache } from './InvalidateAppCache'
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { InvalidateFirstPageCache, InvalidateAppCache } from './_components'
2+
import { Text } from '@latitude-data/web-ui/atoms/Text'
3+
4+
export default function AdminIntegrationsCache() {
5+
return (
6+
<div className='container flex flex-col gap-y-8'>
7+
<section className='flex flex-col gap-y-4'>
8+
<Text.H1>Integrations Cache Management</Text.H1>
9+
<Text.H4 color='foregroundMuted'>
10+
Manage cached Pipedream integration data. Clear the first page cache
11+
or invalidate specific app caches by searching for them.
12+
</Text.H4>
13+
</section>
14+
15+
<section className='flex flex-col gap-y-4'>
16+
<InvalidateFirstPageCache />
17+
</section>
18+
19+
<section className='flex flex-col gap-y-4'>
20+
<InvalidateAppCache />
21+
</section>
22+
</div>
23+
)
24+
}

apps/web/src/app/(onboarding)/onboarding-agents/start/_components/OnboardingClient/index.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,12 @@ export function OnboardingClient({
6767
)}
6868
{(currentStep === OnboardingStepKey.TriggerAgent ||
6969
currentStep === OnboardingStepKey.RunAgent) && (
70-
<PlaygroundSteps
71-
moveNextOnboardingStep={moveNextOnboardingStep}
72-
currentStep={currentStep}
73-
executeCompleteOnboarding={executeCompleteOnboarding}
74-
/>
75-
)}
70+
<PlaygroundSteps
71+
moveNextOnboardingStep={moveNextOnboardingStep}
72+
currentStep={currentStep}
73+
executeCompleteOnboarding={executeCompleteOnboarding}
74+
/>
75+
)}
7676
</div>
7777
</div>
7878
</PlaygroundProvider>

0 commit comments

Comments
 (0)