Skip to content

Commit 2d34fba

Browse files
authored
Exposes ConnectTiles via module federation (redpanda-data#1957)
* feat: exposed connect tiles via federated module * feat: ran bun install --yarn * feat: exposed types * feat: removed module-federation/typescript package * feat: fixed types that were not exported * feat: various cleanup
1 parent 048b5ce commit 2d34fba

19 files changed

+447
-329
lines changed

frontend/CLAUDE.md

Lines changed: 119 additions & 56 deletions
Large diffs are not rendered by default.

frontend/rsbuild.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { pluginModuleFederation } from '@module-federation/rsbuild-plugin';
2-
import { defineConfig, loadEnv, rspack } from '@rsbuild/core';
2+
import { defineConfig, loadEnv } from '@rsbuild/core';
33
import { pluginReact } from '@rsbuild/plugin-react';
44
import { pluginSass } from '@rsbuild/plugin-sass';
55
import { pluginSvgr } from '@rsbuild/plugin-svgr';

frontend/src/components/pages/connect/Overview.tsx

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,15 @@ class KafkaConnectOverview extends PageComponent<{ defaultView: string }> {
117117
),
118118
content: (
119119
<Box>
120-
<Text mb={4}>
121-
Redpanda Connect is an alternative to Kafka Connect. Choose from a growing ecosystem of readily available
122-
connectors.{' '}
123-
<Link href="https://docs.redpanda.com/redpanda-cloud/develop/connect/about/" target="_blank">
124-
Learn more.
125-
</Link>
126-
</Text>
120+
{!isFeatureFlagEnabled('enableRpcnTiles') && (
121+
<Text mb={4}>
122+
Redpanda Connect is an alternative to Kafka Connect. Choose from a growing ecosystem of readily
123+
available connectors.{' '}
124+
<Link href="https://docs.redpanda.com/redpanda-cloud/develop/connect/about/" target="_blank">
125+
Learn more.
126+
</Link>
127+
</Text>
128+
)}
127129
{Features.pipelinesApi ? <RpConnectPipelinesList matchedPath="/rp-connect" /> : <RedpandaConnectIntro />}
128130
</Box>
129131
),

frontend/src/components/pages/rp-connect/Pipelines.Create.tsx

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ import { CreatePipelineSidebar } from './onboarding/create-pipeline-sidebar';
5050
import { SecretsQuickAdd } from './secrets/Secrets.QuickAdd';
5151
import { cpuToTasks, MAX_TASKS, MIN_TASKS, tasksToCPU } from './tasks';
5252
import type { ConnectComponentType } from './types/schema';
53-
import type { AddUserFormData, ConnectTilesFormData } from './types/wizard';
53+
import type { AddUserFormData, WizardFormData } from './types/wizard';
5454
import { getConnectTemplate } from './utils/yaml';
5555

5656
const exampleContent = `
@@ -361,22 +361,32 @@ export const PipelineEditor = observer(
361361
const [editorInstance, setEditorInstance] = useState<null | editor.IStandaloneCodeEditor>(null);
362362
const [secretAutocomplete, setSecretAutocomplete] = useState<IDisposable | undefined>(undefined);
363363
const [monaco, setMonaco] = useState<Monaco | undefined>(undefined);
364-
const [persistedFormData, _] = useSessionStorage<Partial<ConnectTilesFormData>>(CONNECT_WIZARD_CONNECTOR_KEY, {});
364+
const [persistedFormData, _] = useSessionStorage<Partial<WizardFormData>>(CONNECT_WIZARD_CONNECTOR_KEY, {});
365365
const enableRpcnTiles = isFeatureFlagEnabled('enableRpcnTiles');
366366

367367
// Track actual editor content to keep sidebar in sync with editor's real state
368368
const [actualEditorContent, setActualEditorContent] = useState<string>('');
369369

370370
const persistedConnectComponentTemplate = useMemo(() => {
371-
if (!persistedFormData?.connectionName || !persistedFormData?.connectionType) {
371+
const persistedInput = persistedFormData?.input;
372+
const persistedOutput = persistedFormData?.output;
373+
if (!persistedInput?.connectionName || !persistedInput?.connectionType) {
372374
return undefined;
373375
}
374-
const template = getConnectTemplate({
375-
connectionName: persistedFormData?.connectionName,
376-
connectionType: persistedFormData?.connectionType,
376+
const inputTemplate = getConnectTemplate({
377+
connectionName: persistedInput.connectionName,
378+
connectionType: persistedInput.connectionType,
377379
showOptionalFields: false,
378380
});
379-
return template;
381+
if (persistedOutput?.connectionName && persistedOutput?.connectionType) {
382+
const outputTemplate = getConnectTemplate({
383+
connectionName: persistedOutput.connectionName,
384+
connectionType: persistedOutput.connectionType,
385+
showOptionalFields: false,
386+
});
387+
return `${inputTemplate}\n${outputTemplate}`;
388+
}
389+
return inputTemplate;
380390
}, [persistedFormData]);
381391

382392
const yaml = useMemo(() => {

frontend/src/components/pages/rp-connect/Pipelines.List.tsx

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import { PageComponent, type PageInitHelper } from '../Page';
3636
import { openDeleteModal } from './modals';
3737
import { ConnectTiles } from './onboarding/connect-tiles';
3838
import type { ConnectComponentType } from './types/schema';
39-
import type { AddTopicFormData, AddUserFormData, ConnectTilesFormData } from './types/wizard';
39+
import type { AddTopicFormData, AddUserFormData, ConnectTilesFormData, WizardFormData } from './types/wizard';
4040

4141
const { ToastContainer, toast } = createStandaloneToast();
4242

@@ -96,7 +96,7 @@ const LegacyEmptyState = () => {
9696
* Shows ConnectTiles and navigates to wizard with connector pre-selected
9797
*/
9898
const WizardEmptyState = () => {
99-
const [, setPersistedConnectionName] = useSessionStorage<Partial<ConnectTilesFormData>>(
99+
const [persistedConnectionName, setPersistedConnectionName] = useSessionStorage<Partial<WizardFormData>>(
100100
CONNECT_WIZARD_CONNECTOR_KEY,
101101
{},
102102
);
@@ -107,10 +107,14 @@ const WizardEmptyState = () => {
107107
const handleConnectionChange = useCallback(
108108
(connectionName: string, connectionType: ConnectComponentType) => {
109109
try {
110-
setPersistedConnectionName({ connectionName, connectionType });
110+
const existingOutput = persistedConnectionName.output;
111+
const newConfig = existingOutput
112+
? { input: { connectionName, connectionType }, output: existingOutput }
113+
: { input: { connectionName, connectionType } };
114+
setPersistedConnectionName(newConfig);
111115
setPersistedTopic({});
112116
setPersistedUser({});
113-
navigate('/rp-connect/wizard?step=add-topic');
117+
navigate('/rp-connect/wizard?step=add-output');
114118
} catch (error) {
115119
toast({
116120
status: 'error',
@@ -121,10 +125,19 @@ const WizardEmptyState = () => {
121125
});
122126
}
123127
},
124-
[setPersistedConnectionName, setPersistedTopic, setPersistedUser, navigate],
128+
[setPersistedConnectionName, setPersistedTopic, setPersistedUser, navigate, persistedConnectionName],
125129
);
126130

127-
return <ConnectTiles onChange={handleConnectionChange} componentTypeFilter={['input', 'output']} hideHeader />;
131+
return (
132+
<ConnectTiles
133+
title="Send data to your pipeline"
134+
onChange={handleConnectionChange}
135+
componentTypeFilter={['input']}
136+
defaultConnectionName={persistedConnectionName.input?.connectionName}
137+
defaultConnectionType={persistedConnectionName.input?.connectionType}
138+
className="mt-4"
139+
/>
140+
);
128141
};
129142

130143
const EmptyPlaceholder = () => {

frontend/src/components/pages/rp-connect/onboarding/add-connector-dialog.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import {
22
Dialog,
33
DialogBody,
44
DialogContent,
5+
DialogDescription,
56
DialogHeader,
6-
DialogOverlay,
7+
DialogTitle,
78
} from 'components/redpanda-ui/components/dialog';
8-
import { Heading } from 'components/redpanda-ui/components/typography';
99
import type { ConnectComponentType } from '../types/schema';
1010
import { ConnectTiles } from './connect-tiles';
1111

@@ -22,10 +22,10 @@ export const AddConnectorDialog = ({
2222
}) => {
2323
return (
2424
<Dialog open={isOpen} onOpenChange={onCloseAddConnector}>
25-
<DialogOverlay />
2625
<DialogContent size="xl">
2726
<DialogHeader>
28-
<Heading level={2}>Add a Connector</Heading>
27+
<DialogTitle>Add a Connector</DialogTitle>
28+
<DialogDescription>Add a connector to your pipeline.</DialogDescription>
2929
</DialogHeader>
3030
<DialogBody>
3131
<ConnectTiles

frontend/src/components/pages/rp-connect/onboarding/add-secrets-dialog.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import {
33
Dialog,
44
DialogBody,
55
DialogContent,
6+
DialogDescription,
67
DialogHeader,
7-
DialogOverlay,
8+
DialogTitle,
89
} from 'components/redpanda-ui/components/dialog';
9-
import { Heading } from 'components/redpanda-ui/components/typography';
1010
import { QuickAddSecrets } from 'components/ui/secret/quick-add-secrets';
1111
import { AlertTriangle } from 'lucide-react';
1212
import { Scope } from 'protogen/redpanda/api/dataplane/v1/secret_pb';
@@ -40,10 +40,10 @@ export const AddSecretsDialog = ({
4040

4141
return (
4242
<Dialog open={isOpen} onOpenChange={onClose}>
43-
<DialogOverlay />
4443
<DialogContent size="xl">
4544
<DialogHeader>
46-
<Heading level={2}>Add Secrets</Heading>
45+
<DialogTitle>Add Secrets</DialogTitle>
46+
<DialogDescription>Add secrets to your pipeline.</DialogDescription>
4747
</DialogHeader>
4848
<DialogBody>
4949
{errorMessages.length > 0 && (

frontend/src/components/pages/rp-connect/onboarding/add-topic-step.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ export const AddTopicStep = forwardRef<BaseStepRef, AddTopicStepProps>(({ topicL
5454
const createTopicMutation = useCreateTopicMutation();
5555
const updateTopicConfigMutation = useUpdateTopicConfigMutation();
5656

57+
const isLoading = createTopicMutation.isPending || updateTopicConfigMutation.isPending;
58+
5759
// Sync topicOptions when topicList updates
5860
useEffect(() => {
5961
setTopicOptions(initialTopicOptions);
@@ -228,7 +230,7 @@ export const AddTopicStep = forwardRef<BaseStepRef, AddTopicStepProps>(({ topicL
228230
error: 'Form validation failed',
229231
};
230232
},
231-
isLoading: createTopicMutation.isPending || updateTopicConfigMutation.isPending,
233+
isLoading,
232234
}));
233235

234236
return (
@@ -261,6 +263,7 @@ export const AddTopicStep = forwardRef<BaseStepRef, AddTopicStepProps>(({ topicL
261263
onCreateOption={handleCreateTopicOption}
262264
placeholder="Select or create a topic..."
263265
className="max-w-[300px]"
266+
disabled={isLoading}
264267
/>
265268
</FormControl>
266269
<FormMessage />
@@ -270,13 +273,17 @@ export const AddTopicStep = forwardRef<BaseStepRef, AddTopicStepProps>(({ topicL
270273

271274
<Collapsible open={showAdvancedSettings} onOpenChange={setShowAdvancedSettings}>
272275
<CollapsibleTrigger asChild>
273-
<Button variant="ghost" size="sm" className="w-fit p-0">
276+
<Button variant="ghost" size="sm" className="w-fit p-0" disabled={isLoading}>
274277
<ChevronDown className="h-4 w-4" />
275278
Show Advanced Settings
276279
</Button>
277280
</CollapsibleTrigger>
278281
<CollapsibleContent className="space-y-6 mt-4">
279-
<AdvancedTopicSettings form={form} isExistingTopic={Boolean(existingTopicBeingEdited)} />
282+
<AdvancedTopicSettings
283+
form={form}
284+
isExistingTopic={Boolean(existingTopicBeingEdited)}
285+
disabled={isLoading}
286+
/>
280287
</CollapsibleContent>
281288
</Collapsible>
282289
</div>

frontend/src/components/pages/rp-connect/onboarding/add-user-step.tsx

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
SelectTrigger,
1919
SelectValue,
2020
} from 'components/redpanda-ui/components/select';
21-
import { Heading } from 'components/redpanda-ui/components/typography';
21+
import { Heading, Link, Text } from 'components/redpanda-ui/components/typography';
2222
import { useSessionStorage } from 'hooks/use-session-storage';
2323
import { CircleAlert, RefreshCcw } from 'lucide-react';
2424
import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useState } from 'react';
@@ -56,6 +56,8 @@ export const AddUserStep = forwardRef<BaseStepRef, AddUserStepProps>(({ usersLis
5656
const createUserMutation = useCreateUserMutation();
5757
const createACLMutation = useLegacyCreateACLMutation();
5858

59+
const isLoading = createUserMutation.isPending || createACLMutation.isPending;
60+
5961
// Initialize password based on persisted settings or defaults
6062
const initialSpecialChars = useMemo(
6163
() => persistedUserData?.specialCharactersEnabled ?? false,
@@ -194,7 +196,7 @@ export const AddUserStep = forwardRef<BaseStepRef, AddUserStepProps>(({ usersLis
194196
error: 'Form validation failed',
195197
};
196198
},
197-
isLoading: createUserMutation.isPending || createACLMutation.isPending,
199+
isLoading,
198200
}));
199201

200202
return (
@@ -215,6 +217,7 @@ export const AddUserStep = forwardRef<BaseStepRef, AddUserStepProps>(({ usersLis
215217
<FormField
216218
control={form.control}
217219
name="username"
220+
disabled={isLoading}
218221
render={({ field }) => (
219222
<FormItem>
220223
<FormLabel>Username</FormLabel>
@@ -232,12 +235,16 @@ export const AddUserStep = forwardRef<BaseStepRef, AddUserStepProps>(({ usersLis
232235
)}
233236
/>
234237

235-
{existingUserBeingEdited && (
238+
{existingUserBeingEdited && !isLoading && (
236239
<Alert>
237240
<CircleAlert className="h-4 w-4" />
238241
<AlertTitle>Existing User Selected</AlertTitle>
239242
<AlertDescription>
240-
This user already exists. To change permissions or password, please create a new user.
243+
<Text>
244+
This user already exists. To enable topic-specific permissions automatically, please create a new
245+
user. You can see if this existing user already has permissions{' '}
246+
<Link href={`/security/users/${existingUserBeingEdited.name}/details`}>here</Link>.
247+
</Text>
241248
</AlertDescription>
242249
</Alert>
243250
)}
@@ -247,6 +254,7 @@ export const AddUserStep = forwardRef<BaseStepRef, AddUserStepProps>(({ usersLis
247254
<FormField
248255
control={form.control}
249256
name="password"
257+
disabled={isLoading}
250258
render={({ field }) => (
251259
<FormItem>
252260
<FormLabel>Password</FormLabel>
@@ -261,6 +269,7 @@ export const AddUserStep = forwardRef<BaseStepRef, AddUserStepProps>(({ usersLis
261269
</FormControl>
262270
<FormMessage />
263271
<FormField
272+
{...field}
264273
control={form.control}
265274
name="specialCharactersEnabled"
266275
render={({ field: specialCharsField }) => (
@@ -279,6 +288,7 @@ export const AddUserStep = forwardRef<BaseStepRef, AddUserStepProps>(({ usersLis
279288
<FormField
280289
control={form.control}
281290
name="saslMechanism"
291+
disabled={isLoading}
282292
render={({ field }) => (
283293
<FormItem>
284294
<FormLabel>SASL Mechanism</FormLabel>
@@ -305,12 +315,17 @@ export const AddUserStep = forwardRef<BaseStepRef, AddUserStepProps>(({ usersLis
305315
<FormField
306316
control={form.control}
307317
name="superuser"
318+
disabled={isLoading}
308319
render={({ field }) => (
309320
<FormItem>
310321
<div className="flex flex-col gap-2">
311322
<div className="flex items-center space-x-3">
312323
<FormControl>
313-
<Checkbox checked={field.value} onCheckedChange={field.onChange} />
324+
<Checkbox
325+
disabled={field.disabled}
326+
checked={field.value}
327+
onCheckedChange={field.onChange}
328+
/>
314329
</FormControl>
315330
<FormLabel className="text-sm font-medium">
316331
Enable topic-specific permissions for this user for "{topicData.topicName}"
@@ -326,15 +341,12 @@ export const AddUserStep = forwardRef<BaseStepRef, AddUserStepProps>(({ usersLis
326341
Want custom User Permissions?
327342
</AlertTitle>
328343
<AlertDescription>
329-
You can configure custom ACLs to connect your data to Redpanda{' '}
330-
<a
331-
href="/security/acls"
332-
className="text-blue-600 hover:text-blue-800 underline"
333-
target="_blank"
334-
rel="noopener noreferrer"
335-
>
336-
here
337-
</a>
344+
<Text>
345+
You can configure custom ACLs to connect your data to Redpanda{' '}
346+
<Link href="/security/acls" target="_blank" rel="noopener noreferrer">
347+
here
348+
</Link>
349+
</Text>
338350
</AlertDescription>
339351
</Alert>
340352
)}

0 commit comments

Comments
 (0)