@@ -17,6 +17,12 @@ import { handleServerError } from '../../../utils/errorHandlingUtils';
1717import InfoIcon from '../../../redesign/assets/info-message.svg' ;
1818import { YBInput , YBTooltip } from '../../../redesign/components' ;
1919import { formatDatetime , YBTimeFormats } from '../../../redesign/helpers/DateUtils' ;
20+ import {
21+ getHaBackupFileTimestamp ,
22+ getIsHaBackupOld ,
23+ HA_BACKUP_OLD_THRESHOLD_HOURS
24+ } from '../utils' ;
25+ import { ReactComponent as WarningIcon } from '@app/redesign/assets/alert.svg' ;
2026
2127import './PromoteInstanceModal.scss' ;
2228
@@ -41,12 +47,16 @@ const validationSchema = Yup.object().shape({
4147 backupFile : Yup . object ( ) . nullable ( ) . required ( 'Backup file is required' )
4248} ) ;
4349
44- const adaptHaBackupToFormFieldOption = ( value : string , currentUserTimezone ?: string ) : PromoteInstanceFormValues [ 'backupFile' ] => {
45- // backup_21-02-20-00-40.tgz --> 21-02-20-00-40
46- const timestamp = value . replace ( 'backup_' , '' ) . replace ( '.tgz' , '' ) ;
47- // we always get the backup list time in UTC
48- const formattedTimestamp = moment . utc ( timestamp , 'YY-MM-DD-HH:mm' ) . toDate ( ) ;
49- const label = formatDatetime ( formattedTimestamp , YBTimeFormats . YB_DEFAULT_TIMESTAMP , currentUserTimezone ) ;
50+ const adaptHaBackupToFormFieldOption = (
51+ value : string ,
52+ currentUserTimezone ?: string
53+ ) : PromoteInstanceFormValues [ 'backupFile' ] => {
54+ const formattedTimestamp = moment . utc ( getHaBackupFileTimestamp ( value ) , 'YY-MM-DD-HH:mm' ) . toDate ( ) ;
55+ const label = formatDatetime (
56+ formattedTimestamp ,
57+ YBTimeFormats . YB_DEFAULT_TIMESTAMP ,
58+ currentUserTimezone
59+ ) ;
5060
5161 return { value, label } ;
5262} ;
@@ -60,6 +70,17 @@ const useStyles = makeStyles((theme) => ({
6070 } ,
6171 fieldLabel : {
6272 marginBottom : theme . spacing ( 1 )
73+ } ,
74+ warningBanner : {
75+ display : 'flex' ,
76+ alignItems : 'center' ,
77+ gap : theme . spacing ( 1 ) ,
78+
79+ marginTop : theme . spacing ( 2 ) ,
80+ padding : theme . spacing ( 1 ) ,
81+
82+ backgroundColor : theme . palette . warning [ 100 ] ,
83+ borderRadius : theme . shape . borderRadius
6384 }
6485} ) ) ;
6586
@@ -70,11 +91,17 @@ export const PromoteInstanceModal: FC<PromoteInstanceModalProps> = ({
7091 instanceId
7192} ) => {
7293 const [ confirmationText , setConfirmationText ] = useState < string > ( '' ) ;
94+ const [
95+ isRestoreOldBackupConfirmationModalOpen ,
96+ setIsRestoreOldBackupConfirmationModalOpen
97+ ] = useState ( false ) ;
7398 const formik = useRef ( { } as FormikProps < PromoteInstanceFormValues > ) ;
7499 const theme = useTheme ( ) ;
75100 const classes = useStyles ( ) ;
76101 const queryClient = useQueryClient ( ) ;
77- const currentUserTimezone = useSelector ( ( state : any ) => state ?. customer ?. currentUser ?. data ?. timezone ) ;
102+ const currentUserTimezone = useSelector (
103+ ( state : any ) => state ?. customer ?. currentUser ?. data ?. timezone
104+ ) ;
78105
79106 const { isLoading, data } = useQuery (
80107 [ QUERY_KEY . getHABackups , configId ] ,
@@ -84,7 +111,10 @@ export const PromoteInstanceModal: FC<PromoteInstanceModalProps> = ({
84111 onSuccess : ( data ) => {
85112 // pre-select first backup file from the list
86113 if ( Array . isArray ( data ) && data . length ) {
87- formik . current . setFieldValue ( 'backupFile' , adaptHaBackupToFormFieldOption ( data [ 0 ] , currentUserTimezone ) ) ;
114+ formik . current . setFieldValue (
115+ 'backupFile' ,
116+ adaptHaBackupToFormFieldOption ( data [ 0 ] , currentUserTimezone )
117+ ) ;
88118 }
89119 }
90120 }
@@ -106,7 +136,9 @@ export const PromoteInstanceModal: FC<PromoteInstanceModalProps> = ({
106136 }
107137 ) ;
108138
109- const backupsList = ( data ?? [ ] ) . map ( backup => adaptHaBackupToFormFieldOption ( backup , currentUserTimezone ) ) ;
139+ const backupsList = ( data ?? [ ] ) . map ( ( backup ) =>
140+ adaptHaBackupToFormFieldOption ( backup , currentUserTimezone )
141+ ) ;
110142
111143 const closeModal = ( ) => {
112144 if ( ! formik . current . isSubmitting ) {
@@ -115,7 +147,7 @@ export const PromoteInstanceModal: FC<PromoteInstanceModalProps> = ({
115147 }
116148 } ;
117149
118- const submitForm = async (
150+ const handleSubmit = async (
119151 formValues : PromoteInstanceFormValues ,
120152 actions : FormikActions < PromoteInstanceFormValues >
121153 ) => {
@@ -137,7 +169,7 @@ export const PromoteInstanceModal: FC<PromoteInstanceModalProps> = ({
137169 showCancelButton
138170 title = "Make Active"
139171 onHide = { closeModal }
140- onFormSubmit = { submitForm }
172+ onFormSubmit = { handleSubmit }
141173 isSubmitDisabled = { isSubmitDisabled }
142174 footerAccessory = {
143175 < Box display = "flex" gridGap = { theme . spacing ( 1 ) } >
@@ -164,38 +196,80 @@ export const PromoteInstanceModal: FC<PromoteInstanceModalProps> = ({
164196 render = { ( formikProps : FormikProps < PromoteInstanceFormValues > ) => {
165197 // workaround for outdated version of Formik to access form methods outside of <Formik>
166198 formik . current = formikProps ;
167-
199+ const isHaBackupOld =
200+ ! ! formik . current . values . backupFile ?. value &&
201+ getIsHaBackupOld ( formik . current . values . backupFile . value ) ;
168202 return (
169- < div data-testid = "ha-make-active-modal" >
170- { isLoading ? (
171- < YBLoading />
172- ) : (
173- < div className = "ha-promote-instance-modal" >
174- < Alert bsStyle = "warning" >
175- Note: promotion will replace all existing data on this platform instance with
176- the data from the selected backup. After promotion succeeds you will need to
177- re-sign in with the credentials of the previously active platform instance.
178- </ Alert >
179- < Typography variant = "body2" className = { classes . fieldLabel } >
180- < Field
181- name = "backupFile"
182- component = { YBFormSelect }
183- options = { backupsList }
184- label = "Select the backup to restore from"
185- isSearchable
203+ < >
204+ < div data-testid = "ha-make-active-modal" >
205+ { isLoading ? (
206+ < YBLoading />
207+ ) : (
208+ < div className = "ha-promote-instance-modal" >
209+ < Box
210+ display = "flex"
211+ flexDirection = "column"
212+ gridGap = { theme . spacing ( 1 ) }
213+ marginBottom = { 2 }
214+ >
215+ < div className = { classes . warningBanner } >
216+ < Box width = { 24 } height = { 24 } >
217+ < WarningIcon width = { 24 } height = { 24 } color = { theme . palette . warning [ 700 ] } />
218+ </ Box >
219+ < Typography variant = "body2" >
220+ < b > Note! </ b > After promotion, all existing data from this platform
221+ instance will be replaced with the data from teh selected backup. After
222+ promotion succeeds, you will need to log-in again with the credentials of
223+ the previous active platform instance. Contact Yugabyte Support for
224+ assistance.
225+ </ Typography >
226+ </ div >
227+ { isHaBackupOld && (
228+ < div className = { classes . warningBanner } >
229+ < Box width = { 24 } height = { 24 } >
230+ < WarningIcon
231+ width = { 24 }
232+ height = { 24 }
233+ color = { theme . palette . warning [ 700 ] }
234+ />
235+ </ Box >
236+ < Typography variant = "body2" >
237+ < p >
238+ < b >
239+ { `Note! The selected backup is more than ${ HA_BACKUP_OLD_THRESHOLD_HOURS } hours old. Restoring from this backup could cause serious issues, including
240+ data loss.` }
241+ </ b > { ' ' }
242+ </ p >
243+ < p > Contact Yugabyte Support for assistance.</ p >
244+ </ Typography >
245+ </ div >
246+ ) }
247+ </ Box >
248+ < Typography variant = "body2" className = { classes . fieldLabel } >
249+ < Field
250+ name = "backupFile"
251+ component = { YBFormSelect }
252+ options = { backupsList }
253+ label = "Select the backup to restore from"
254+ isSearchable
255+ menuPortalTarget = { document . body }
256+ styles = { {
257+ menuPortal : ( base : any ) => ( { ...base , zIndex : 4000 } )
258+ } }
259+ />
260+ Please type PROMOTE to confirm.
261+ </ Typography >
262+ < YBInput
263+ className = { classes . confirmTextInputBox }
264+ inputProps = { { 'data-testid' : 'PromoteInstanceModal-ConfirmTextInputField' } }
265+ placeholder = "PROMOTE"
266+ value = { confirmationText }
267+ onChange = { ( event ) => setConfirmationText ( event . target . value ) }
186268 />
187- Please type PROMOTE to confirm.
188- </ Typography >
189- < YBInput
190- className = { classes . confirmTextInputBox }
191- inputProps = { { 'data-testid' : 'PromoteInstanceModal-ConfirmTextInputField' } }
192- placeholder = "PROMOTE"
193- value = { confirmationText }
194- onChange = { ( event ) => setConfirmationText ( event . target . value ) }
195- />
196- </ div >
197- ) }
198- </ div >
269+ </ div >
270+ ) }
271+ </ div >
272+ </ >
199273 ) ;
200274 } }
201275 />
0 commit comments