Просмотр исходного кода

Merge branch 'master' into user-scripts

Nashwan Azhari 5 лет назад
Родитель
Сommit
c769a05670

+ 3 - 0
src/@types/Task.ts

@@ -13,8 +13,11 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 export type ProgressUpdate = {
+  index: number,
   message: string,
   created_at: Date,
+  total_steps: number | null
+  current_step: number | null
 }
 
 export type Task = {

+ 25 - 13
src/components/atoms/ProgressBar/ProgressBar.tsx

@@ -19,29 +19,41 @@ import styled from 'styled-components'
 import Palette from '../../styleUtils/Palette'
 import StyleProps from '../../styleUtils/StyleProps'
 
-type Props = {
-  progress: number,
-  width?: number,
-  style?: React.CSSProperties
-}
-
-const Wrapper = styled.div<any>`
+const Wrapper = styled.div`
+  display: flex;
+  align-items: center;
+`
+const ProgressLabel = styled.div`
+  text-align: right;
+  min-width: 36px;
+  font-size: 12px;
+`
+const ProgressBarWrapper = styled.div`
   background: white;
+  flex-grow: 1;
 `
-const Progress = styled.div<any>`
+const Progress = styled.div<{width: number}>`
   height: 2px;
   background: ${Palette.primary};
   transition: all ${StyleProps.animations.swift};
-  width: ${(props: Props) => props.width}%;
+  width: ${props => props.width}%;
 `
-
+type Props = {
+  progress: number,
+  style?: React.CSSProperties
+  useLabel?: boolean
+}
 @observer
 class ProgressBar extends React.Component<Props> {
   render() {
     return (
-      // eslint-disable-next-line react/jsx-props-no-spreading
-      <Wrapper {...this.props}>
-        <Progress data-test-id="progressBar-progress" width={this.props.progress} />
+      <Wrapper>
+        <ProgressBarWrapper style={this.props.style}>
+          <Progress width={this.props.progress} />
+        </ProgressBarWrapper>
+        {this.props.useLabel ? (
+          <ProgressLabel>{this.props.progress} %</ProgressLabel>
+        ) : null}
       </Wrapper>
     )
   }

+ 29 - 9
src/components/molecules/TaskItem/TaskItem.tsx

@@ -17,7 +17,7 @@ import { observer } from 'mobx-react'
 import styled, { css, createGlobalStyle } from 'styled-components'
 import { Collapse } from 'react-collapse'
 
-import type { Task } from '../../../@types/Task'
+import type { ProgressUpdate, Task } from '../../../@types/Task'
 import StatusIcon from '../../atoms/StatusIcon'
 import Arrow from '../../atoms/Arrow'
 import StatusPill from '../../atoms/StatusPill'
@@ -136,7 +136,7 @@ const ExceptionText = styled.div<any>`
 const ProgressUpdates = styled.div<any>`
   color: ${Palette.black};
 `
-const ProgressUpdate = styled.div<any>`
+const ProgressUpdateDiv = styled.div<any>`
   display: flex;
   color: ${props => (props.secondary ? Palette.grayscale[5] : 'inherit')};
 `
@@ -172,9 +172,23 @@ class TaskItem extends React.Component<Props> {
     return message
   }
 
-  getMessageProgress(message: string) {
-    const match = message.match(/.*progress.*?(100|\d{1,2})%/)
-    return match && match[1]
+  getProgressPercentage(progressUpdate: ProgressUpdate): {useLabel: boolean, value: number} | null {
+    if (progressUpdate.total_steps && progressUpdate.current_step) {
+      const currentStep = Math.min(progressUpdate.total_steps, progressUpdate.current_step)
+      return {
+        value: (currentStep * 100) / progressUpdate.total_steps,
+        useLabel: true,
+      }
+    }
+
+    const stringPercentage = progressUpdate.message.match(/.*progress.*?(100|\d{1,2})%/)?.[1]
+    if (!stringPercentage) {
+      return null
+    }
+    return {
+      value: Number(stringPercentage),
+      useLabel: false,
+    }
   }
 
   handleExceptionTextClick(exceptionText: string) {
@@ -250,19 +264,25 @@ class TaskItem extends React.Component<Props> {
           if (!update) {
             return <Value>N/A</Value>
           }
-          const messageProgress = this.getMessageProgress(update.message)
+          const progressPercentage = this.getProgressPercentage(update)
 
           return (
             // eslint-disable-next-line react/no-array-index-key
-            <ProgressUpdate key={i} secondary={i < this.props.item.progress_updates.length - 1 || this.props.item.status !== 'RUNNING'}>
+            <ProgressUpdateDiv key={i} secondary={i < this.props.item.progress_updates.length - 1 || this.props.item.status !== 'RUNNING'}>
               <ProgressUpdateDate width={this.props.columnWidths[0]}>
                 <span>{DateUtils.getLocalTime(update.created_at).format('YYYY-MM-DD HH:mm:ss')}</span>
               </ProgressUpdateDate>
               <ProgressUpdateValue data-test-id={`taskItem-progressUpdateMessage-${i}`}>
                 {update.message}
-                {messageProgress && <ProgressBar style={{ margin: '8px 0' }} progress={Number(messageProgress)} data-test-id={`taskItem-progressBar-${i}`} />}
+                {progressPercentage && (
+                  <ProgressBar
+                    style={{ margin: '8px 0' }}
+                    progress={progressPercentage.value}
+                    useLabel={progressPercentage.useLabel}
+                  />
+                )}
               </ProgressUpdateValue>
-            </ProgressUpdate>
+            </ProgressUpdateDiv>
           )
         })}
       </ProgressUpdates>

+ 12 - 0
src/components/molecules/TaskItem/story.tsx

@@ -20,6 +20,18 @@ const item: any = {
   progress_updates: [
     { message: 'the task has a progress of 50%', created_at: new Date() },
     { message: 'the task is almost done', created_at: new Date() },
+    {
+      message: 'Transferring changed areas of Cinder volume \'ca816553-a616-413e-a078-9a19e1da5326\'',
+      created_at: new Date(),
+      total_steps: 5,
+      current_step: 2,
+    },
+    {
+      message: 'Transferring changed areas of Cinder volume \'ca816553-a616-413e-a078-9a19e1da5326\'',
+      created_at: new Date(),
+      total_steps: 5,
+      current_step: 7,
+    },
   ],
   exception_details: 'Exception details',
   status: 'RUNNING',

+ 2 - 1
src/components/organisms/MigrationDetailsContent/MigrationDetailsContent.tsx

@@ -55,6 +55,7 @@ const NavigationItems = [
 
 type Props = {
   item: MigrationItemDetails | null,
+  itemId: string
   minionPools: MinionPool[]
   detailsLoading: boolean,
   instancesDetails: Instance[],
@@ -124,7 +125,7 @@ class MigrationDetailsContent extends React.Component<Props> {
         <DetailsNavigation
           items={NavigationItems}
           selectedValue={this.props.page}
-          itemId={this.props.item ? this.props.item.id : ''}
+          itemId={this.props.itemId}
           itemType="migration"
         />
         <DetailsBody>

+ 1 - 0
src/components/organisms/ReplicaDetailsContent/ReplicaDetailsContent.tsx

@@ -72,6 +72,7 @@ const NavigationItems = [
 type TimezoneValue = 'utc' | 'local'
 type Props = {
   item?: ReplicaItemDetails | null,
+  itemId: string
   endpoints: Endpoint[],
   sourceSchema: Field[],
   sourceSchemaLoading: boolean,

+ 1 - 0
src/components/pages/MigrationDetailsPage/MigrationDetailsPage.tsx

@@ -376,6 +376,7 @@ class MigrationDetailsPage extends React.Component<Props, State> {
           contentComponent={(
             <MigrationDetailsContent
               item={migrationStore.migrationDetails}
+              itemId={this.props.match.params.id}
               instancesDetails={instanceStore.instancesDetails}
               instancesDetailsLoading={instanceStore.loadingInstancesDetails}
               sourceSchema={providerStore.sourceSchema}

+ 2 - 0
src/components/pages/ReplicaDetailsPage/ReplicaDetailsPage.tsx

@@ -148,6 +148,7 @@ class ReplicaDetailsPage extends React.Component<Props, State> {
   }
 
   componentWillUnmount() {
+    replicaStore.clearDetails()
     scheduleStore.clearUnsavedSchedules()
     this.stopPolling = true
   }
@@ -581,6 +582,7 @@ class ReplicaDetailsPage extends React.Component<Props, State> {
           contentComponent={(
             <ReplicaDetailsContent
               item={replica}
+              itemId={this.replicaId}
               instancesDetails={instanceStore.instancesDetails}
               instancesDetailsLoading={instanceStore.loadingInstancesDetails}
               endpoints={endpointStore.endpoints}

+ 5 - 4
src/sources/MigrationSource.ts

@@ -25,23 +25,24 @@ import type { NetworkMap } from '../@types/Network'
 import type { Endpoint, StorageMap } from '../@types/Endpoint'
 
 import configLoader from '../utils/Config'
-import { Task } from '../@types/Task'
+import { ProgressUpdate, Task } from '../@types/Task'
 import {
   MigrationItem, MigrationItemOptions, MigrationItemDetails, UserScriptData,
 } from '../@types/MainItem'
+
 import { INSTANCE_OSMORPHING_MINION_POOL_MAPPINGS } from '../components/organisms/WizardOptions/WizardOptions'
 
 class MigrationSourceUtils {
-  static sortTaskUpdates(updates: any[]) {
+  static sortTaskUpdates(updates: ProgressUpdate[]) {
     if (!updates) {
       return
     }
-    updates.sort((a: any, b: any) => {
+    updates.sort((a, b) => {
       const sortNull = !a && b ? 1 : a && !b ? -1 : !a && !b ? 0 : false
       if (sortNull !== false) {
         return sortNull
       }
-      return moment(a.created_at).toDate().getTime() - moment(b.created_at).toDate().getTime()
+      return a.index - b.index
     })
   }
 

+ 1 - 5
src/sources/ReplicaSource.ts

@@ -12,8 +12,6 @@ You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-import moment from 'moment'
-
 import Api from '../utils/ApiCaller'
 import { OptionsSchemaPlugin } from '../plugins/endpoint'
 import DefaultOptionsSchemaPlugin from '../plugins/endpoint/default/OptionsSchemaPlugin'
@@ -93,9 +91,7 @@ export class ReplicaSourceUtils {
     if (!updates) {
       return
     }
-    updates
-      .sort((a, b) => moment(a.created_at)
-        .toDate().getTime() - moment(b.created_at).toDate().getTime())
+    updates.sort((a, b) => a.index - b.index)
   }
 }
 

+ 4 - 0
src/stores/ReplicaStore.ts

@@ -107,6 +107,10 @@ class ReplicaStore {
     }
   }
 
+  @action clearDetails() {
+    this.replicaDetails = null
+  }
+
   @action getReplicasSuccess(replicas: ReplicaItem[]) {
     this.replicasLoaded = true
     this.replicas = replicas