Skip to content
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
3e32b71
refactor: remove MobX observer() wrappers from simple components
jvorcak Mar 2, 2026
86ec8d7
refactor: remove MobX from admin, consumer, transforms, and broker pages
jvorcak Mar 2, 2026
34c9df7
Remove more MobX annotations
jvorcak Mar 2, 2026
1e42828
remove MobX observer() wrappers from simple components
jvorcak Mar 2, 2026
109c32e
remove MobX observer() wrappers from more complex components
jvorcak Mar 2, 2026
e269190
fix: resolve TypeScript type check errors and E2E test typo
jvorcak Mar 2, 2026
192a2b1
refactor: remove MobX from rp-connect, connect forms, and misc pages …
jvorcak Mar 3, 2026
83bbc53
refactor: complete MobX removal from src/components/ (100%)
jvorcak Mar 3, 2026
c61fb07
refactor: remove MobX from utility files (modal-container, tsx-utils,…
jvorcak Mar 3, 2026
35c7c85
refactor: remove MobX observer() from root app entry points
jvorcak Mar 3, 2026
0070c50
fix: resolve TypeScript errors after MobX removal
jvorcak Mar 3, 2026
7f3a114
fix: restore MobX→React reactivity for pages reading api.* observables
jvorcak Mar 3, 2026
49acb99
fix: touch api.* observables in PageComponent render() delegates
jvorcak Mar 4, 2026
a384e49
fix: replace MobX array mutations in KeyValuePairEditor with immutabl…
jvorcak Mar 4, 2026
4da203a
fix: migrate backend-api.ts from MobX to Zustand and fix E2E tests
jvorcak Mar 4, 2026
f4f5cc2
Fixes sidebar in an embedded mode'
jvorcak Mar 5, 2026
6f28d1b
Merge branch 'master' into refactor/remove-mobx-observer-wrappers
jvorcak Mar 5, 2026
16a411a
Merge branch 'master' into refactor/remove-mobx-observer-wrappers
jvorcak Mar 6, 2026
08f1232
Remove autorun import
jvorcak Mar 6, 2026
3b38179
Fixes on querychanged
jvorcak Mar 8, 2026
36696d8
Fixes kafka connect forms in non-mobx version
jvorcak Mar 9, 2026
66b1681
Address PR review comments: restore MAX_PAGE_SIZE, fix tags const, in…
jvorcak Mar 9, 2026
5143948
Remove stale MobX observable touches from TopicDetails render
jvorcak Mar 9, 2026
58a7baf
Fix delete connector toast race and improve e2e test reliability
jvorcak Mar 9, 2026
8bbfba8
Try to fix delete connector test
jvorcak Mar 9, 2026
e24a15f
Try to run kafka-connect tests in serial for now
jvorcak Mar 9, 2026
f65520b
Merge branch 'master' into refactor/remove-mobx-observer-wrappers
jvorcak Mar 9, 2026
3dd44b6
Merge branch 'master' into refactor/remove-mobx-observer-wrappers
jvorcak Mar 9, 2026
b9a8990
Fixes infinite loading in topic detail
jvorcak Mar 10, 2026
229e240
Fix schema creation
jvorcak Mar 10, 2026
1846671
Use useStore from zustand
jvorcak Mar 10, 2026
c785024
Improve debug bundle status check
jvorcak Mar 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions frontend/bun.lock
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also regenerate yarn.lock

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yarn is not installed in this environment — can you regenerate yarn.lock locally with yarn install after merging bun.lock?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can do "bun install --yarn"

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,6 @@
"jwt-decode": "^4.0.0",
"lottie-react": "^2.4.1",
"lucide-react": "^0.563.0",
"mobx": "^6.10.0",
"mobx-react": "^7.6.0",
Comment on lines -98 to -99
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

big moment if we can finally get there. thank you

"moment": "^2.30.1",
"monaco-editor": "^0.54.0",
"monaco-editor-webpack-plugin": "^7.1.1",
Expand Down
3 changes: 1 addition & 2 deletions frontend/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ import { builderCustomComponents } from 'components/builder-io/builder-custom-co
import { BUILDER_API_KEY } from 'components/constants';
import { CustomFeatureFlagProvider } from 'custom-feature-flag-provider';
import useDeveloperView from 'hooks/use-developer-view';
import { observer } from 'mobx-react';
import { protobufRegistry } from 'protobuf-registry';
import queryClient from 'query-client';
import { getBasePath } from 'utils/env';
Expand Down Expand Up @@ -111,4 +110,4 @@ const App = () => {
);
};

export default observer(App);
export default App;
9 changes: 4 additions & 5 deletions frontend/src/components/layout/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import {
useSidebar,
} from 'components/redpanda-ui/components/sidebar';
import { ChevronsLeft, ChevronsRight, ChevronUp, LogOut, Settings } from 'lucide-react';
import { observer } from 'mobx-react';
import type React from 'react';
import { useEffect, useState } from 'react';
import { createGroupedSidebarItems, type SidebarGroupedItems } from 'utils/route-utils';
Expand Down Expand Up @@ -81,7 +80,7 @@ function SidebarCollapseToggle() {
);
}

const UserProfile = observer(() => {
const UserProfile = () => {
const [preferencesOpen, setPreferencesOpen] = useState(false);
const { state, isMobile, setOpenMobile } = useSidebar();

Expand Down Expand Up @@ -178,7 +177,7 @@ const UserProfile = observer(() => {
<UserPreferencesDialog isOpen={preferencesOpen} onClose={() => setPreferencesOpen(false)} />
</>
);
});
};

type NavItemProps = {
item: SidebarGroupedItems['items'][number];
Expand Down Expand Up @@ -220,7 +219,7 @@ function SidebarNavItem({ item, isActive, onNavClick }: NavItemProps) {
);
}

const SidebarNavigation = observer(() => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's double check if that still works when in embedded mode with cloud UI, sidebar items need to refresh

const SidebarNavigation = () => {
const location = useLocation();
const { isMobile, setOpenMobile } = useSidebar();
const groupedItems = createGroupedSidebarItems();
Expand Down Expand Up @@ -250,7 +249,7 @@ const SidebarNavigation = observer(() => {
))}
</nav>
);
});
};

export function AppSidebar() {
return (
Expand Down
2 changes: 0 additions & 2 deletions frontend/src/components/misc/broker-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

import { Tooltip } from '@redpanda-data/ui';
import { ChevronRightIcon } from 'components/icons';
import { observer } from 'mobx-react';
import React, { Component } from 'react';

import { api, brokerMap } from '../../state/backend-api';
Expand All @@ -21,7 +20,6 @@ type BrokerListProps =
| { brokerIds: number[]; addedIds?: number[]; removedIds?: number[]; leaderId?: number }
| { partition: Partition };

@observer
export class BrokerList extends Component<BrokerListProps> {
constructor(p: BrokerListProps) {
super(p);
Expand Down
123 changes: 53 additions & 70 deletions frontend/src/components/misc/buttons/data-refresh/component.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
import { Box, Flex, IconButton, Popover, Spinner, Text } from '@redpanda-data/ui';
import { PauseIcon, PlayIcon, RefreshIcon } from 'components/icons';
import { autorun, observable } from 'mobx';
import { observer } from 'mobx-react';

/**
* Copyright 2022 Redpanda Data, Inc.
*
Expand All @@ -14,83 +9,73 @@ import { observer } from 'mobx-react';
* by the Apache License, Version 2.0
*/

import { Box, Flex, IconButton, Popover, Spinner, Text } from '@redpanda-data/ui';
import { PauseIcon, PlayIcon, RefreshIcon } from 'components/icons';
import { useEffect, useRef, useState } from 'react';

import { appGlobal } from '../../../../state/app-global';
import { api, REST_CACHE_DURATION_SEC } from '../../../../state/backend-api';
import { uiSettings } from '../../../../state/ui';
import { prettyMilliseconds } from '../../../../utils/utils';

const autoRefresh = observable(
{
active: false,
timerId: undefined as ReturnType<typeof setInterval> | undefined,
maxRequestCount: 0,
export const DataRefreshButton = () => {
const [isActive, setIsActive] = useState(false);
const [remainingSeconds, setRemainingSeconds] = useState(0);
const [activeRequests, setActiveRequests] = useState(0);
const [maxRequestCount, setMaxRequestCount] = useState(0);

const stateRef = useRef({
isActive: false,
nextRefresh: Number.POSITIVE_INFINITY,
remainingSeconds: 0,
maxRequestCount: 0,
});

get currentTime() {
return Date.now();
},
useEffect(() => {
const interval = setInterval(() => {
const currentRequests = api.activeRequests.length;

toggleAutorefresh() {
this.active = !this.active;
if (this.active) {
// Start
this.scheduleNextRefresh();
this.timerId = setInterval(this.updateAutorefresh, 150);
} else {
// Stop
clearInterval(this.timerId);
appGlobal.onRefresh();
// Track max request count
if (currentRequests === 0) {
stateRef.current.maxRequestCount = 0;
} else if (currentRequests > stateRef.current.maxRequestCount) {
stateRef.current.maxRequestCount = currentRequests;
}
},
setActiveRequests(currentRequests);
setMaxRequestCount(stateRef.current.maxRequestCount);

updateAutorefresh() {
const timeUntilRefresh = this.nextRefresh - this.currentTime;
if (!stateRef.current.isActive) return;

if (api.activeRequests.length > 0) {
// There are active requests, delay the next refresh / reset the timer
this.scheduleNextRefresh();
if (currentRequests > 0) {
// Active requestsdelay the next refresh
stateRef.current.nextRefresh = Date.now() + uiSettings.autoRefreshIntervalSecs * 1000;
return;
}

const timeUntilRefresh = stateRef.current.nextRefresh - Date.now();
if (timeUntilRefresh > 0) {
// Still some time left, only update visual timer
this.remainingSeconds = Math.ceil(timeUntilRefresh / 1000);
return;
setRemainingSeconds(Math.ceil(timeUntilRefresh / 1000));
} else {
stateRef.current.nextRefresh = Date.now() + uiSettings.autoRefreshIntervalSecs * 1000;
appGlobal.onRefresh();
}
}, 150);

// The timer has expired
// Refresh now and schedule the next refresh...
this.scheduleNextRefresh();
appGlobal.onRefresh();
},

scheduleNextRefresh() {
this.nextRefresh = this.currentTime + uiSettings.autoRefreshIntervalSecs * 1000;
},
},
undefined,
{ autoBind: true }
);
return () => clearInterval(interval);
}, []);

autorun(() => {
const currentRequests = api.activeRequests.length;
if (currentRequests === 0) {
autoRefresh.maxRequestCount = 0;
}
if (currentRequests > autoRefresh.maxRequestCount) {
autoRefresh.maxRequestCount = currentRequests;
}
});
const toggleAutorefresh = () => {
const newActive = !stateRef.current.isActive;
stateRef.current.isActive = newActive;
if (newActive) {
stateRef.current.nextRefresh = Date.now() + uiSettings.autoRefreshIntervalSecs * 1000;
} else {
appGlobal.onRefresh();
}
setIsActive(newActive);
};

export const DataRefreshButton = observer(() => {
// Track how many requests we've sent in total
const countStr =
autoRefresh.maxRequestCount > 1
? `${autoRefresh.maxRequestCount - api.activeRequests.length} / ${autoRefresh.maxRequestCount}`
: '';
const countStr = maxRequestCount > 1 ? `${maxRequestCount - activeRequests} / ${maxRequestCount}` : '';

// maybe we need to use the same 'no vertical expansion' trick:
return (
<div className="flex items-center gap-1">
<Box>
Expand All @@ -108,16 +93,16 @@ export const DataRefreshButton = observer(() => {
>
<IconButton
aria-label="Auth Refresh"
icon={autoRefresh.active ? <PauseIcon size={18} /> : <PlayIcon size={18} />}
onClick={autoRefresh.toggleAutorefresh}
icon={isActive ? <PauseIcon size={18} /> : <PlayIcon size={18} />}
onClick={toggleAutorefresh}
p={0}
size="xs"
variant="ghost"
/>
</Popover>
</Box>
<Flex alignItems="center" flexDirection="column">
{autoRefresh.active || api.activeRequests.length > 0 ? (
{isActive || activeRequests > 0 ? (
<Spinner color="red.500" ml={2} size="sm" speed="0.3s" />
) : (
<Popover
Expand Down Expand Up @@ -145,13 +130,11 @@ export const DataRefreshButton = observer(() => {
)}
</Flex>
<Text fontSize="sm" ml={4} userSelect="none">
{autoRefresh.active && api.activeRequests.length === 0 && (
<>Refreshing in {autoRefresh.remainingSeconds} secs</>
)}
{api.activeRequests.length > 0 && <>Fetching data... {countStr}</>}
{isActive && activeRequests === 0 && <>Refreshing in {remainingSeconds} secs</>}
{activeRequests > 0 && <>Fetching data... {countStr}</>}
</Text>
</div>
);
});
};

export default DataRefreshButton;
5 changes: 2 additions & 3 deletions frontend/src/components/misc/common.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

import { Button, Modal, ModalBody, ModalContent, ModalFooter, ModalHeader, ModalOverlay } from '@redpanda-data/ui';
import { AlertIcon } from 'components/icons';
import { observer } from 'mobx-react';
import React, { type PropsWithChildren, useState } from 'react';

import type { TopicLogDirSummary } from '../../state/rest-interfaces';
Expand Down Expand Up @@ -65,7 +64,7 @@ export function range(start: number, end: number): number[] {
* Reloading the page does not ensure we'll get the update!
* If there are multiple backend instances, we might get connected to an old instance again when we trigger a reload.
*/
export const UpdatePopup = observer(() => {
export const UpdatePopup = () => {
const [isUpdateDialogOpen, setUpdateDialogOpen] = useState(true);
if (IsDev) {
return null;
Expand Down Expand Up @@ -121,7 +120,7 @@ export const UpdatePopup = observer(() => {
</ModalContent>
</Modal>
);
});
};

export function renderLogDirSummary(summary: TopicLogDirSummary): JSX.Element {
if (!summary.hint) {
Expand Down
5 changes: 2 additions & 3 deletions frontend/src/components/misc/error-display.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

import { Box, Button, List, ListIcon, ListItem, Result, Section } from '@redpanda-data/ui';
import { WarningIcon } from 'components/icons';
import { observer } from 'mobx-react';
import type { FC, ReactElement } from 'react';

import ErrorResult from './error-result';
Expand All @@ -22,7 +21,7 @@ function isWrappedApiError(error: unknown): error is WrappedApiError {
return error !== null && typeof error === 'object' && 'statusCode' in error;
}

export const ErrorDisplay: FC<{ children: ReactElement }> = observer(({ children }) => {
export const ErrorDisplay: FC<{ children: ReactElement }> = ({ children }) => {
if (api.errors.length === 0) {
return children;
}
Expand Down Expand Up @@ -60,7 +59,7 @@ export const ErrorDisplay: FC<{ children: ReactElement }> = observer(({ children
</Section>
</>
);
});
};

function formatError(err: unknown): string {
if (err instanceof Error && err.message) {
Expand Down
Loading
Loading