This is an automated email from the ASF dual-hosted git repository.

jackie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pinot.git


The following commit(s) were added to refs/heads/master by this push:
     new 8d061eea14f rebalance ui flow fixes (#16545)
8d061eea14f is described below

commit 8d061eea14f7d717e75a831864b2cb8467348ee8
Author: Saptarshi Sengupta <[email protected]>
AuthorDate: Fri Aug 8 01:07:46 2025 +0530

    rebalance ui flow fixes (#16545)
---
 .../RebalanceServer/RebalanceResponse.tsx          |  24 ++-
 .../Operations/RebalanceServerStatusOp.tsx         |  57 +++++--
 .../Homepage/Operations/RebalanceServerTableOp.tsx | 169 ++++++++++++++-------
 3 files changed, 181 insertions(+), 69 deletions(-)

diff --git 
a/pinot-controller/src/main/resources/app/components/Homepage/Operations/RebalanceServer/RebalanceResponse.tsx
 
b/pinot-controller/src/main/resources/app/components/Homepage/Operations/RebalanceServer/RebalanceResponse.tsx
index d8d09a60d6f..67658974100 100644
--- 
a/pinot-controller/src/main/resources/app/components/Homepage/Operations/RebalanceServer/RebalanceResponse.tsx
+++ 
b/pinot-controller/src/main/resources/app/components/Homepage/Operations/RebalanceServer/RebalanceResponse.tsx
@@ -54,7 +54,7 @@ const CopyJobIdToClipboardButton = ({ jobId }: { jobId: 
string }) => {
         </>
     );
 };
-export const RebalanceResponse = ({ response }) => {
+export const RebalanceResponse = ({ response, onJobIdClick }) => {
     const responseSectionsToShow = [
         {
             name: 'Segment Assignment',
@@ -81,9 +81,25 @@ export const RebalanceResponse = ({ response }) => {
                             <Grid item xs={6}>
                                 <Typography color='textSecondary' 
variant='caption'>Job Id</Typography>
                                 <Box flexDirection="row" display="flex" 
alignItems="center" marginTop={-1.25}>
-                                    <Typography style={{ fontWeight: 600 }} 
variant='body2'>
-                                        {response.jobId}
-                                    </Typography>
+                                    {response.status === 'IN_PROGRESS' && 
onJobIdClick ? (
+                                        <Typography 
+                                            style={{ 
+                                                fontWeight: 600, 
+                                                cursor: 'pointer', 
+                                                color: '#1976d2',
+                                                textDecoration: 'underline'
+                                            }} 
+                                            variant='body2'
+                                            onClick={() => 
onJobIdClick(response.jobId)}
+                                            title="Click to view job details"
+                                        >
+                                            {response.jobId}
+                                        </Typography>
+                                    ) : (
+                                        <Typography style={{ fontWeight: 600 
}} variant='body2'>
+                                            {response.jobId}
+                                        </Typography>
+                                    )}
                                     <CopyJobIdToClipboardButton 
jobId={response.jobId} />
                                 </Box>
                             </Grid>
diff --git 
a/pinot-controller/src/main/resources/app/components/Homepage/Operations/RebalanceServerStatusOp.tsx
 
b/pinot-controller/src/main/resources/app/components/Homepage/Operations/RebalanceServerStatusOp.tsx
index 9d61cf6f5ae..4a07b4a0ba9 100644
--- 
a/pinot-controller/src/main/resources/app/components/Homepage/Operations/RebalanceServerStatusOp.tsx
+++ 
b/pinot-controller/src/main/resources/app/components/Homepage/Operations/RebalanceServerStatusOp.tsx
@@ -35,10 +35,11 @@ import {RebalanceTableSegmentJob} from "Models";
 type RebalanceServerStatusOpProps = {
     tableName: string;
     hideModal: () => void;
+    initialJobId?: string;
 };
 
 export const RebalanceServerStatusOp = (
-    { tableName, hideModal } : RebalanceServerStatusOpProps
+    { tableName, hideModal, initialJobId } : RebalanceServerStatusOpProps
 ) => {
     const { currentTimezone } = useTimezone();
     const [rebalanceServerJobs, setRebalanceServerJobs] = 
React.useState<RebalanceTableSegmentJob[]>([])
@@ -47,7 +48,7 @@ export const RebalanceServerStatusOp = (
     const [rebalanceProgressStats, setRebalanceProgressStats] = 
useState<{}>({});
     const [loading, setLoading] = useState(false);
 
-    useEffect(() => {
+    const fetchRebalanceJobs = () => {
         setLoading(true);
         PinotMethodUtils
             .fetchRebalanceTableJobs(tableName)
@@ -55,8 +56,35 @@ export const RebalanceServerStatusOp = (
                 setRebalanceServerJobs(jobs)
             })
             .finally(() => setLoading(false));
+    };
+
+    useEffect(() => {
+        fetchRebalanceJobs();
     }, []);
 
+    // Set initial job selection when jobs are loaded and initialJobId is 
provided
+    useEffect(() => {
+        if (initialJobId && rebalanceServerJobs.length > 0 && !jobSelected) {
+            const jobExists = rebalanceServerJobs.find(job => job.jobId === 
initialJobId);
+            if (jobExists) {
+                setJobSelected(initialJobId);
+            }
+        }
+    }, [initialJobId, rebalanceServerJobs, jobSelected]);
+
+    const RefreshAction = () => {
+        return (
+            <Button
+                variant='outlined'
+                color='primary'
+                onClick={fetchRebalanceJobs}
+                disabled={loading}
+            >
+                Refresh
+            </Button>
+        );
+    };
+
     const BackAction = () => {
         return (
             <Button
@@ -67,7 +95,8 @@ export const RebalanceServerStatusOp = (
                 Back
             </Button>
         );
-    }
+    };
+
 
     useEffect(() => {
         try {
@@ -107,10 +136,15 @@ export const RebalanceServerStatusOp = (
             <Dialog
                 open={true}
                 handleClose={hideModal}
-                title="Rebalance Table Status"
+                title="Rebalance Servers Status"
                 showOkBtn={false}
                 size='lg'
-                moreActions={jobSelected ? <BackAction /> : null}
+                moreActions={
+                    <Box display="flex">
+                        <RefreshAction />
+                        {jobSelected && !initialJobId && <Box 
ml={1}><BackAction /></Box>}
+                    </Box>
+                }
             >
                 <DialogContent>
                     <Box alignItems='center' display='flex' 
justifyContent='center'>
@@ -125,10 +159,15 @@ export const RebalanceServerStatusOp = (
         <Dialog
             open={true}
             handleClose={hideModal}
-            title="Rebalance Table Status"
+            title="Rebalance Servers Status"
             showOkBtn={false}
             size='lg'
-            moreActions={jobSelected ? <BackAction /> : null}
+            moreActions={
+                <Box display="flex">
+                    <RefreshAction />
+                    {jobSelected && !initialJobId && <Box ml={1}><BackAction 
/></Box>}
+                </Box>
+            }
         >
             <DialogContent>
                 {
@@ -147,10 +186,10 @@ export const RebalanceServerStatusOp = (
                                         rebalanceServerJob.jobId,
                                         rebalanceServerJob.tableName,
                                         progressStats.status,
-                                        
formatTimeInTimezone(+rebalanceServerJob.submissionTimeMs)
+                                        
formatTimeInTimezone(+rebalanceServerJob.submissionTimeMs + 
(JSON.parse(rebalanceServerJob?.REBALANCE_PROGRESS_STATS || 
'{}').timeToFinishInSeconds * 1000))
                                     ];
                                 }),
-                                columns: ['Job id', 'Table name', 'Status', 
'Started at']
+                                columns: ['Job id', 'Table name', 'Status', 
'Started at', 'Finished at']
                             }}
                             showSearchBox
                         /> :
diff --git 
a/pinot-controller/src/main/resources/app/components/Homepage/Operations/RebalanceServerTableOp.tsx
 
b/pinot-controller/src/main/resources/app/components/Homepage/Operations/RebalanceServerTableOp.tsx
index dd752bfa1e3..4a9773394c3 100644
--- 
a/pinot-controller/src/main/resources/app/components/Homepage/Operations/RebalanceServerTableOp.tsx
+++ 
b/pinot-controller/src/main/resources/app/components/Homepage/Operations/RebalanceServerTableOp.tsx
@@ -32,6 +32,7 @@ import InfoOutlinedIcon from 
"@material-ui/icons/InfoOutlined";
 import {rebalanceServerOptions} from 
"./RebalanceServer/RebalanceServerOptions";
 import {RebalanceServerConfigurationOption} from 
"./RebalanceServer/RebalanceServerConfigurationOption";
 import {RebalanceResponse} from "./RebalanceServer/RebalanceResponse";
+import {RebalanceServerStatusOp} from "./RebalanceServerStatusOp";
 
 type Props = {
   tableType: string,
@@ -41,12 +42,20 @@ type Props = {
 
 const DryRunAction = ({ handleOnRun, disabled }: { handleOnRun: () => void, 
disabled?: boolean }) => {
   return (
-      <Button disabled={disabled} onClick={handleOnRun} variant="outlined" 
style={{ textTransform: 'none' }} color="primary">
+      <Button disabled={disabled} onClick={handleOnRun} variant="contained" 
style={{ textTransform: 'none' }} color="primary">
         Dry Run
       </Button>
   );
 }
 
+const RebalanceAction = ({ handleOnRun, disabled }: { handleOnRun: () => void, 
disabled?: boolean }) => {
+  return (
+      <Button disabled={disabled} onClick={handleOnRun} variant="contained" 
style={{ textTransform: 'none' }} color="primary">
+        Rebalance
+      </Button>
+  );
+}
+
 const BackAction = ({ onClick }: { onClick: () => void }) => {
   return (
       <Button onClick={onClick} variant="outlined" color="primary">
@@ -55,6 +64,8 @@ const BackAction = ({ onClick }: { onClick: () => void }) => {
   );
 }
 
+
+
 export default function RebalanceServerTableOp({
   hideModal,
   tableName,
@@ -65,6 +76,12 @@ export default function RebalanceServerTableOp({
   const [rebalanceConfig, setRebalanceConfig] = React.useState(
       rebalanceServerOptions.reduce((config, option) => ({ ...config, 
[option.name]: option.defaultValue }), {})
   );
+  const [isDryRun, setIsDryRun] = React.useState(false);
+  const [dryRunCompleted, setDryRunCompleted] = React.useState(false);
+  const [dryRunResponse, setDryRunResponse] = React.useState(null);
+  const [showingDryRunResults, setShowingDryRunResults] = 
React.useState(false);
+  const [showJobStatusDialog, setShowJobStatusDialog] = React.useState(false);
+  const [selectedJobId, setSelectedJobId] = React.useState<string | 
null>(null);
 
   const getData = () => {
     return {
@@ -87,10 +104,14 @@ export default function RebalanceServerTableOp({
     } else {
       setRebalanceResponse(response);
     }
+
+    setShowingDryRunResults(false);
+    setIsDryRun(false);
     setPending(false);
   };
 
-  const handleDryRun = async () => {
+    const handleDryRun = async () => {
+    setIsDryRun(true);
     const data = getData();
     setPending(true);
     const response = await 
PinotMethodUtils.rebalanceServersForTableOp(tableName, {
@@ -106,7 +127,10 @@ export default function RebalanceServerTableOp({
       })
     } else {
       setRebalanceResponse(response);
+      setDryRunResponse(response);
+      setDryRunCompleted(true);
     }
+    setShowingDryRunResults(true);
     setPending(false);
   };
 
@@ -137,62 +161,95 @@ export default function RebalanceServerTableOp({
 
   const handleBackBtnOnClick = () => {
     setRebalanceResponse(null);
+    setShowingDryRunResults(false);
+    setIsDryRun(false);
   }
 
-  return (
-    <Dialog
-      showTitleDivider
-      showFooterDivider
-      size='md'
-      okBtnDisabled={pending}
-      open={true}
-      handleClose={hideModal}
-      title={<RebalanceServerDialogHeader />}
-      handleSave={handleSave}
-      btnOkText='Rebalance'
-      showOkBtn={!rebalanceResponse}
-      moreActions={
-        !rebalanceResponse ?
-            <DryRunAction disabled={pending} handleOnRun={handleDryRun} /> :
-            <BackAction onClick={handleBackBtnOnClick} />
-      }
-    >
-        {!rebalanceResponse ?
-          <Box flexDirection="column">
-            <RebalanceServerSection sectionTitle='Before you begin'>
-              <Alert color='info' icon={<InfoOutlinedIcon fontSize='small' />}>
-                <Typography variant='body2'>
-                  It is strongly recommended to run once via "Dry Run" with 
the options enabled prior to running the actual "Rebalance" operation.
-                  This is needed to verify that rebalance will do what's 
expected.
-                </Typography>
-              </Alert>
-            </RebalanceServerSection>
-            <Divider style={{ marginBottom: 20 }} />
-            <RebalanceServerSection sectionTitle='Basic Options'>
-              <Grid container spacing={2}>
-                {rebalanceServerOptions.filter(option => 
!option.isAdvancedConfig && !option.isStatsGatheringConfig).map((option) => (
-                    <Grid item xs={12} key={`basic-options-${option.name}`}>
-                      <RebalanceServerConfigurationOption 
rebalanceConfig={rebalanceConfig} option={option} 
handleConfigChange={handleConfigChange} />
-                    </Grid>
-                ))}
-              </Grid>
-            </RebalanceServerSection>
-            <Divider style={{ marginBottom: 20 }}/>
-            <RebalanceServerSection sectionTitle='Advanced Options' 
canHideSection showSectionByDefault={false}>
-              <Grid container spacing={2}>
-                {rebalanceServerOptions.filter(option => 
option.isAdvancedConfig).map((option) => (
-                    <Grid item xs={12} key={`advanced-options-${option.name}`}>
-                      <RebalanceServerConfigurationOption 
rebalanceConfig={rebalanceConfig} option={option} 
handleConfigChange={handleConfigChange} />
-                    </Grid>
-                ))}
-              </Grid>
-            </RebalanceServerSection>
+  const handleJobIdClick = (jobId: string) => {
+    setSelectedJobId(jobId);
+    setShowJobStatusDialog(true);
+  }
+
+  const handleCloseJobStatusDialog = () => {
+    setShowJobStatusDialog(false);
+    setSelectedJobId(null);
+  }
+
+  const getDialogActions = () => {
+    if (!showingDryRunResults && !rebalanceResponse) {
+      return <DryRunAction disabled={pending} handleOnRun={handleDryRun} />;
+    }
+    
+    if (showingDryRunResults) {
+      return (
+        <Box display="flex">
+          <BackAction onClick={handleBackBtnOnClick} />
+          <Box ml={1}>
+            <RebalanceAction disabled={pending} handleOnRun={handleSave} />
           </Box>
-        :
-          <React.Fragment>
-            <RebalanceResponse response={rebalanceResponse} />
-          </React.Fragment>
-        }
-    </Dialog>
+        </Box>
+      );
+    }
+    
+    return <BackAction onClick={handleBackBtnOnClick} />;
+  };
+
+  return (
+    <React.Fragment>
+      <Dialog
+        showTitleDivider
+        showFooterDivider
+        size='md'
+        okBtnDisabled={pending}
+        open={true}
+        handleClose={hideModal}
+        title={<RebalanceServerDialogHeader />}
+        showOkBtn={false}
+        moreActions={getDialogActions()}
+      >
+          {!showingDryRunResults && !rebalanceResponse && (
+            <Box flexDirection="column">
+              <RebalanceServerSection sectionTitle='Basic Options'>
+                <Grid container spacing={2}>
+                  {rebalanceServerOptions.filter(option => 
!option.isAdvancedConfig && !option.isStatsGatheringConfig).map((option) => (
+                      <Grid item xs={12} key={`basic-options-${option.name}`}>
+                        <RebalanceServerConfigurationOption 
rebalanceConfig={rebalanceConfig} option={option} 
handleConfigChange={handleConfigChange} />
+                      </Grid>
+                  ))}
+                </Grid>
+              </RebalanceServerSection>
+              <Divider style={{ marginBottom: 20 }}/>
+              <RebalanceServerSection sectionTitle='Advanced Options' 
canHideSection showSectionByDefault={false}>
+                <Grid container spacing={2}>
+                  {rebalanceServerOptions.filter(option => 
option.isAdvancedConfig).map((option) => (
+                      <Grid item xs={12} 
key={`advanced-options-${option.name}`}>
+                        <RebalanceServerConfigurationOption 
rebalanceConfig={rebalanceConfig} option={option} 
handleConfigChange={handleConfigChange} />
+                      </Grid>
+                  ))}
+                </Grid>
+              </RebalanceServerSection>
+            </Box>
+          )}
+
+          {showingDryRunResults && dryRunResponse && (
+            <React.Fragment>
+              <RebalanceResponse response={dryRunResponse} 
onJobIdClick={handleJobIdClick} />
+            </React.Fragment>
+          )}
+
+          {rebalanceResponse && !showingDryRunResults && (
+            <React.Fragment>
+              <RebalanceResponse response={rebalanceResponse} 
onJobIdClick={handleJobIdClick} />
+            </React.Fragment>
+          )}
+      </Dialog>
+      {showJobStatusDialog && (
+        <RebalanceServerStatusOp
+          tableName={tableName}
+          hideModal={handleCloseJobStatusDialog}
+          initialJobId={selectedJobId}
+        />
+      )}
+    </React.Fragment>
   );
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to