Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update backup status views: no backup, failed backup, scheduled backup #40932

Merged
merged 4 commits into from
Apr 10, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
163 changes: 136 additions & 27 deletions client/landing/jetpack-cloud/components/daily-backup-status/index.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
import React, { Component, Fragment } from 'react';
import React, { Component } from 'react';
import { localize, useTranslate } from 'i18n-calypso';
import page from 'page';
import { get } from 'lodash';
Expand Down Expand Up @@ -38,7 +38,7 @@ class DailyBackupStatus extends Component {
//page.redirect( backupDetailPath( this.props.siteSlug, this.props.backup.rewindId ) );
}

getDisplayDate = date => {
getDisplayDate = ( date, withLatest = true ) => {
const { translate, moment, timezone, gmtOffset } = this.props;

//Apply the time offset
Expand All @@ -50,11 +50,18 @@ class DailyBackupStatus extends Component {
const yearDate = backupDate.format( 'YYYY' );

const dateFormat = yearToday === yearDate ? 'MMM D' : 'MMM D, YYYY';
const displayBackupTime = backupDate.format( 'LT' );

let displayableDate;

if ( isToday ) {
displayableDate = translate( 'Latest: Today ' ) + backupDate.format( 'LT' );
if ( isToday && withLatest ) {
displayableDate = translate( 'Latest: Today %s', { args: [ displayBackupTime ] } );
cleacos marked this conversation as resolved.
Show resolved Hide resolved
} else if ( isToday ) {
displayableDate = translate( 'Today %s', { args: [ displayBackupTime ] } );
} else if ( withLatest ) {
displayableDate = translate( 'Latest: %s', {
args: [ backupDate.format( dateFormat + ', LT' ) ],
} );
} else {
displayableDate = backupDate.format( dateFormat + ', LT' );
}
Expand All @@ -70,7 +77,7 @@ class DailyBackupStatus extends Component {
const meta = get( backup, 'activityDescription[2].children[0]', '' );

return (
<Card className="daily-backup-status__success">
<>
<div className="daily-backup-status__icon-section">
<Gridicon className="daily-backup-status__status-icon" icon="cloud-upload" />
<div className="daily-backup-status__title">{ translate( 'Latest backup' ) }</div>
Expand All @@ -82,36 +89,37 @@ class DailyBackupStatus extends Component {
onRestoreClick={ this.triggerRestore }
disabledRestore={ ! allowRestore }
/>
</Card>
</>
);
}

renderFailedBackup( backup ) {
const { translate, timezone, gmtOffset } = this.props;

const backupTitleDate = this.getDisplayDate( backup.activityTs );
const backupTitleDate = this.getDisplayDate( backup.activityTs, false );
const backupDate = applySiteOffset( backup.activityTs, { timezone, gmtOffset } );

const displayDate = backupDate.format( 'L' );
const displayTime = backupDate.format( 'LT' );

return (
<Card className="daily-backup-status__failed">
<>
Copy link
Member

Choose a reason for hiding this comment

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

Neat. Had to look this up. Shorthand for Fragment. Love it.

<Gridicon icon="cloud-upload" className="daily-backup-status__gridicon-error-state" />
<div className="daily-backup-status__failed-message">
{ translate( 'Backup attempt failed' ) }
{ translate( 'Backup failed' ) }: { backupTitleDate }
cleacos marked this conversation as resolved.
Show resolved Hide resolved
</div>
<div className="daily-backup-status__date">{ backupTitleDate }</div>
<div className="daily-backup-status__label">
<p>
{ translate(
'A backup for your site was attempted on %(displayDate)s at %(displayTime)s and was not able to be completed.',
'A backup for your site was attempted on %(displayDate)s at %(displayTime)s and was not ' +
'able to be completed.',
{ args: { displayDate, displayTime } }
) }
</p>
<p>
{ translate(
'Check out the {{a}}backups help guide{{/a}} or contact our support team to resolve the issue. View to get the issue resolved',
'Check out the {{a}}backups help guide{{/a}} or contact our support team to resolve the ' +
'issue. View to get the issue resolved',
{
components: {
a: (
Expand All @@ -135,34 +143,131 @@ class DailyBackupStatus extends Component {
{ translate( 'Contact support' ) }
</Button>
</div>
</Card>
</>
);
}

renderNoBackups() {
const { translate } = this.props;
renderNoBackup() {
const { translate, selectedDate, onDateChange } = this.props;

// todo: remove the mock dates by the real dates
const lastBackupAt = 'Yesterday 16:02';
const nextBackupAt = 'Today 16:02';
const displayDate = selectedDate.format( 'll' );
const nextDate = selectedDate.clone().add( 1, 'days' );
const displayNextDate = nextDate.format( 'll' );

return (
<Fragment>
<Gridicon icon="sync" />
<>
<Gridicon icon="cloud-upload" className="daily-backup-status__gridicon-no-backup" />
<div className="daily-backup-status__title">{ translate( 'No backup' ) }</div>
Copy link

Choose a reason for hiding this comment

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

ℹ️ String reuse speeds up translation and improves consistency. The following string might make a good alternative and has already been translated 21 times:
translate( 'No backups' ) ES Score: 7


<div className="daily-backup-status__date">
{ translate( 'Backup Scheduled' ) }: { nextBackupAt }
<div className="daily-backup-status__label">
<p>
{ translate( 'The backup attempt for %(displayDate)s was delayed.', {
args: { displayDate },
} ) }
</p>
<p>
{ translate(
'But don’t worry, it was likely completed in the early hours the next morning. ' +
'Check the following day, {{link}}%(displayNextDate)s{{/link}} or contact support if you still need help.',
{
args: { displayNextDate },
components: {
//todo: href need implementation and add the correct query
link: (
<a
href="?date="
onClick={ event => {
event.preventDefault();
onDateChange( nextDate );
} }
/>
),
},
}
) }
</p>
</div>
<div>{ translate( 'Last daily backup' ) + ` ${ lastBackupAt }` }</div>

<Button
className="daily-backup-status__support-button"
href="https://jetpack.com/contact-support/"
target="_blank"
rel="noopener noreferrer"
isPrimary={ false }
>
{ translate( 'Contact support' ) }
</Button>
</>
);
}

renderNoBackupToday( lastBackupDate ) {
const { translate, timezone, gmtOffset, moment, onDateChange } = this.props;

const today = applySiteOffset( moment(), {
timezone: timezone,
gmtOffset: gmtOffset,
} );
const yesterday = today.subtract( 1, 'days' );

const lastBackupDay = lastBackupDate.isSame( yesterday, 'day' )
? translate( 'Yesterday ' )
Copy link

Choose a reason for hiding this comment

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

ℹ️ String reuse speeds up translation and improves consistency. The following string might make a good alternative and has already been translated 54 times:
translate( 'yesterday' ) ES Score: 15
See 1 additional suggestions in the PR translation status page

: lastBackupDate.format( 'll' );

const lastBackupTime = lastBackupDate.format( 'LT' );

// Calculates the remaining hours for the next backup + 3 hours of safety margin
const hoursForNextBackup =
parseInt( lastBackupDate.format( 'H' ) ) - parseInt( today.format( 'H' ) ) + 3;

return (
<>
<Gridicon className="daily-backup-status__gridicon-backup-scheduled" icon="cloud-upload" />
<div className="daily-backup-status__static-title">
{ translate( 'Backup Scheduled:' ) }
Copy link

Choose a reason for hiding this comment

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

ℹ️ String reuse speeds up translation and improves consistency. The following string might make a good alternative and has already been translated 9 times:
translate( 'Backup Scheduled' ) ES Score: 11

<div>
{ translate( 'In the next %d hour', 'In the next %d hours', {
args: [ hoursForNextBackup ],
count: hoursForNextBackup,
} ) }
</div>
</div>
<div className="daily-backup-status__no-backup-last-backup">
{ translate( 'Last daily backup: {{link}}%(lastBackupDay)s %(lastBackupTime)s{{/link}}', {
args: { lastBackupDay, lastBackupTime },
components: {
//todo: href need implementation and add the correct query
link: (
<a
href="?date="
onClick={ event => {
event.preventDefault();
onDateChange( lastBackupDate );
} }
/>
),
},
} ) }
</div>
<ActionButtons disabledDownload={ true } disabledRestore={ true } />
</Fragment>
</>
);
}

renderBackupStatus( backup ) {
if ( ! backup ) {
return this.renderNoBackups();
const { selectedDate, lastDateAvailable, moment, timezone, gmtOffset } = this.props;

const today = applySiteOffset( moment(), {
timezone: timezone,
gmtOffset: gmtOffset,
} );

const isToday = today.isSame( selectedDate, 'day' );

if ( ! backup && isToday ) {
return this.renderNoBackupToday( lastDateAvailable );
} else if ( ! backup ) {
return this.renderNoBackup();
} else if ( isSuccessfulBackup( backup ) ) {
return this.renderGoodBackup( backup );
}
Expand All @@ -173,7 +278,11 @@ class DailyBackupStatus extends Component {
render() {
const backup = this.props.backup;

return <div className="daily-backup-status">{ this.renderBackupStatus( backup ) }</div>;
return (
<div className="daily-backup-status">
<Card className="daily-backup-status__success">{ this.renderBackupStatus( backup ) }</Card>
</div>
);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,25 @@
fill: rgb( 214, 54, 56 );
}

.daily-backup-status__gridicon-no-backup {
width: 3rem;
height: 3rem;
color: var( --studio-yellow-20 );
}

.daily-backup-status__gridicon-backup-scheduled {
width: 3rem;
height: 3rem;
color: var( --studio-gray-40 );
}

.daily-backup-status__label {
color: var( --studio-gray-80 );
text-align: left;
}

.daily-backup-status__date {
.daily-backup-status__date,
.daily-backup-status__static-title {
font-size: 1.4rem;
font-weight: 500;
margin-bottom: 1rem;
Expand All @@ -35,6 +48,11 @@
margin-top: -0.5rem;
}

.daily-backup-status__no-backup-last-backup {
margin-top: 1rem;
font-style: italic;
}

.form-button.daily-backup-status__restore-button,
.form-button.daily-backup-status__download-button,
.form-button.daily-backup-status__support-button {
Expand Down Expand Up @@ -108,4 +126,4 @@
padding-left: 2rem;
padding-right: 2rem;
}
}
}
21 changes: 19 additions & 2 deletions client/landing/jetpack-cloud/sections/backups/main.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import momentDate from 'moment';
import DocumentHead from 'components/data/document-head';
import { updateFilter } from 'state/activity-log/actions';
import {
isActivityBackup,
getBackupAttemptsForDate,
getDailyBackupDeltas,
getEventsInDailyBackup,
Expand Down Expand Up @@ -153,6 +154,7 @@ class BackupsPage extends Component {
siteSlug,
isLoadingBackups,
oldestDateAvailable,
lastDateAvailable,
timezone,
gmtOffset,
} = this.props;
Expand Down Expand Up @@ -194,8 +196,11 @@ class BackupsPage extends Component {
allowRestore={ allowRestore }
siteSlug={ siteSlug }
backup={ backupsOnSelectedDate.lastBackup }
lastDateAvailable={ lastDateAvailable }
selectedDate={ this.getSelectedDate() }
timezone={ timezone }
gmtOffset={ gmtOffset }
onDateChange={ this.onDateChange }
/>
{ doesRewindNeedCredentials && (
<MissingCredentialsWarning settingsLink={ `/settings/${ siteSlug }` } />
Expand Down Expand Up @@ -310,6 +315,7 @@ const createIndexedLog = ( logs, timezone, gmtOffset ) => {
timezone,
gmtOffset,
} );
let lastDateAvailable = null;

if ( 'success' === logs.state ) {
logs.data.forEach( log => {
Expand All @@ -325,11 +331,16 @@ const createIndexedLog = ( logs, timezone, gmtOffset ) => {
if ( ! ( index in indexedLog ) ) {
//The first time we create the index for this date
indexedLog[ index ] = [];
}

//Check if the backup date is the oldest
// Check for the oldest and the last backup dates
if ( isActivityBackup( log ) ) {
if ( backupDate < oldestDateAvailable ) {
oldestDateAvailable = backupDate;
}
if ( backupDate > lastDateAvailable ) {
lastDateAvailable = backupDate;
}
}

indexedLog[ index ].push( log );
Expand All @@ -339,6 +350,7 @@ const createIndexedLog = ( logs, timezone, gmtOffset ) => {
return {
indexedLog,
oldestDateAvailable,
lastDateAvailable,
};
};

Expand All @@ -356,7 +368,11 @@ const mapStateToProps = state => {
const allowRestore =
'active' === rewind.state && ! ( 'queued' === restoreStatus || 'running' === restoreStatus );

const { indexedLog, oldestDateAvailable } = createIndexedLog( logs, timezone, gmtOffset );
const { indexedLog, oldestDateAvailable, lastDateAvailable } = createIndexedLog(
logs,
timezone,
gmtOffset
);

const isLoadingBackups = ! ( logs.state === 'success' );

Expand All @@ -373,6 +389,7 @@ const mapStateToProps = state => {
gmtOffset,
indexedLog,
oldestDateAvailable,
lastDateAvailable,
isLoadingBackups,
};
};
Expand Down