Skip to content

Commit

Permalink
Jetpack Backup: i4 backup states (#46543)
Browse files Browse the repository at this point in the history
Co-authored-by: Kevin Zoschke <kevin.zoschke@automattic.com>
  • Loading branch information
elliottprogrammer and monsieur-z authored Oct 21, 2020
1 parent 1fcdda8 commit 7bf8123
Show file tree
Hide file tree
Showing 10 changed files with 548 additions and 21 deletions.
115 changes: 115 additions & 0 deletions client/components/jetpack/backup-card/backup-failed.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/**
* External dependencies
*/
import React, { FunctionComponent } from 'react';
import { useSelector } from 'react-redux';
import classNames from 'classnames';

/**
* Internal dependencies
*/
import { applySiteOffset } from 'calypso/lib/site/timezone';
import { Button } from '@automattic/components';
import { Card } from '@automattic/components';
import { getSelectedSiteId } from 'calypso/state/ui/selectors';
import { useTranslate } from 'i18n-calypso';
import ActivityDescription from 'calypso/components/activity-card/activity-description';
import contactSupportUrl from 'calypso/lib/jetpack/contact-support-url';
import getSiteTimezoneValue from 'calypso/state/selectors/get-site-timezone-value';
import getSiteGmtOffset from 'calypso/state/selectors/get-site-gmt-offset';
import getSiteUrl from 'calypso/state/sites/selectors/get-site-url';
import JetpackLogo from 'calypso/components/jetpack-logo';

/**
* Style dependencies
*/
import './style.scss';
import cloudErrorIcon from 'calypso/components/jetpack/daily-backup-status/status-card/icons/cloud-error.svg';

/**
* Type dependencies
*/
import type { Activity } from 'calypso/state/activity-log/types';

type Props = { backup: Activity; isFeatured: boolean };

const BackupFailed: FunctionComponent< Props > = ( { backup, isFeatured } ) => {
const translate = useTranslate();
const siteId = useSelector( ( state ) => getSelectedSiteId( state ) ) || -1;
const siteUrl = useSelector( ( state ) => getSiteUrl( state, siteId ) );

const timezone = useSelector( ( state ) => getSiteTimezoneValue( state, siteId ) );
const gmtOffset = useSelector( ( state ) => getSiteGmtOffset( state, siteId ) );

const backupDate = applySiteOffset( backup.activityTs, { timezone, gmtOffset } );

const displayDate = backupDate.format( 'MMM Do' );
const displayTime = backupDate.format( 'H:mma' );

return (
<Card
className={ classNames( 'backup-card', {
'is-featured': isFeatured,
} ) }
>
<div className="backup-card__main">
<div className="backup-card__header">
<div className="backup-card__header-text">
<h2 className="backup-card__date">{ translate( 'Backup failed' ) }</h2>
<p className="backup-card__title backup-card__title--failed">
<img className="backup-card__icon" src={ cloudErrorIcon } alt="" />
{ translate( 'Backup attempted on %(displayDate)s, %(displayTime)s and failed', {
args: { displayDate, displayTime },
} ) }
</p>
</div>
</div>
<ul className="backup-card__actions">
<li>
<Button
className="backup-card__support-button"
href="https://jetpack.com/support/backup/"
target="_blank"
rel="noopener noreferrer"
primary
>
{ translate( 'Read the support guide' ) }
</Button>
</li>
<li>
<Button
className="backup-card__support-button"
href={ contactSupportUrl( siteUrl ) }
target="_blank"
rel="noopener noreferrer"
>
{ translate( 'Contact support' ) }
</Button>
</li>
</ul>
</div>
<div className="backup-card__about">
<h3 className="backup-card__about-heading">{ translate( 'About this backup' ) }</h3>
<div className="backup-card__about-content">
<ul className="backup-card__about-list">
<li>
<div className="backup-card__about-media backup-card__about-media--notice">
<JetpackLogo className="backup-card__jetpack-logo-danger" size={ 32 } />
</div>
<div className="backup-card__about-body">
{ translate(
'Jetpack failed to complete the backup. Jetpack returned the following error:'
) }
<div>
<ActivityDescription activity={ backup } />
</div>
</div>
</li>
</ul>
</div>
</div>
</Card>
);
};

export default BackupFailed;
131 changes: 131 additions & 0 deletions client/components/jetpack/backup-card/backup-scheduled.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/**
* External dependencies
*/
import classNames from 'classnames';
import React, { FunctionComponent } from 'react';
import { useSelector } from 'react-redux';

/**
* Internal dependencies
*/
import { Button } from '@automattic/components';
import { backupMainPath } from 'calypso/my-sites/backup/paths';
import { Card } from '@automattic/components';
import { useTranslate } from 'i18n-calypso';
import { INDEX_FORMAT } from 'calypso/lib/jetpack/backup-utils';
import { applySiteOffset } from 'calypso/lib/site/timezone';
import { useLocalizedMoment } from 'calypso/components/localized-moment';
import { getSelectedSiteId, getSelectedSiteSlug } from 'calypso/state/ui/selectors';
import getSiteTimezoneValue from 'calypso/state/selectors/get-site-timezone-value';
import getSiteGmtOffset from 'calypso/state/selectors/get-site-gmt-offset';
import JetpackLogo from 'calypso/components/jetpack-logo';
/**
* Style dependencies
*/
import './style.scss';
import cloudScheduleIcon from 'calypso/components/jetpack/daily-backup-status/status-card/icons/cloud-schedule.svg';

/**
* Type dependencies
*/
import type { Moment } from 'moment';

type Props = { lastBackupDate: Moment; isFeatured: boolean };

const BackupScheduled: FunctionComponent< Props > = ( { lastBackupDate, isFeatured } ) => {
const translate = useTranslate();
const siteId = useSelector( ( state ) => getSelectedSiteId( state ) ) || -1;
const siteSlug = useSelector( ( state ) => getSelectedSiteSlug( state ) ) || '';

const timezone = useSelector( ( state ) => getSiteTimezoneValue( state, siteId ) );
const gmtOffset = useSelector( ( state ) => getSiteGmtOffset( state, siteId ) );
const moment = useLocalizedMoment();

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

const yesterday = moment( today ).subtract( 1, 'days' );

const lastBackupDay = lastBackupDate.isSame( yesterday, 'day' )
? translate( 'Yesterday ' )
: lastBackupDate.format( 'll' );

const lastBackupTime = lastBackupDate.format( 'H:mma' );

// Calculates the remaining hours for the next backup + 3 hours of safety margin
const DAY_HOURS = 24;
const hoursForNextBackup = DAY_HOURS - today.diff( lastBackupDate, 'hours' ) + 3;

const nextBackupHoursText =
hoursForNextBackup <= 1
? translate( 'In the next hour' )
: translate( 'In the next %d hour', 'In the next %d hours', {
args: [ hoursForNextBackup ],
count: hoursForNextBackup,
} );

return (
<Card
className={ classNames( 'backup-card', {
'is-featured': isFeatured,
} ) }
>
<div className="backup-card__main">
<div className="backup-card__header">
<div className="backup-card__header-text">
<h2 className="backup-card__date">{ nextBackupHoursText }</h2>
<p className="backup-card__title backup-card__title--scheduled">
<img className="backup-card__icon" src={ cloudScheduleIcon } alt="" />
{ translate( 'Your next backup has been scheduled' ) }
</p>
</div>
</div>
<ul className="backup-card__actions">
<li>
<Button className="backup-card__restore-button" primary disabled>
{ translate( 'Restore to this point' ) }
</Button>
</li>
<li>
<Button className="backup-card__download-button" disabled>
{ translate( 'Download backup' ) }
</Button>
</li>
</ul>
</div>
<div className="backup-card__about">
<h3 className="backup-card__about-heading">{ translate( 'About this backup' ) }</h3>
<div className="backup-card__about-content">
<ul className="backup-card__about-list">
<li>
<div className="backup-card__about-media backup-card__about-media--notice">
<JetpackLogo className="backup-card__jetpack-logo-muted" size={ 32 } />
</div>
<div className="backup-card__about-body">
{ translate(
'Jetpack has scheduled your next backup. {{link}}Your last backup was %(lastBackupDay)s %(lastBackupTime)s{{/link}}',
{
args: { lastBackupDay, lastBackupTime },
components: {
link: (
<a
href={ backupMainPath( siteSlug, {
date: lastBackupDate.format( INDEX_FORMAT ),
} ) }
/>
),
},
}
) }
</div>
</li>
</ul>
</div>
</div>
</Card>
);
};

export default BackupScheduled;
111 changes: 111 additions & 0 deletions client/components/jetpack/backup-card/no-backups-on-selected-date.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/**
* External dependencies
*/
import React, { FunctionComponent } from 'react';
import { useSelector } from 'react-redux';
import classNames from 'classnames';

/**
* Internal dependencies
*/
import { Button } from '@automattic/components';
import { backupMainPath } from 'calypso/my-sites/backup/paths';
import { Card } from '@automattic/components';
import { useTranslate } from 'i18n-calypso';
import { INDEX_FORMAT } from 'calypso/lib/jetpack/backup-utils';
import { getSelectedSiteId, getSelectedSiteSlug } from 'calypso/state/ui/selectors';
import contactSupportUrl from 'calypso/lib/jetpack/contact-support-url';
import getSiteUrl from 'calypso/state/sites/selectors/get-site-url';
import JetpackLogo from 'calypso/components/jetpack-logo';
/**
* Style dependencies
*/
import './style.scss';
import cloudWarningIcon from 'calypso/components/jetpack/daily-backup-status/status-card/icons/cloud-warning-orange-30.svg';

/**
* Type dependencies
*/
import type { Moment } from 'moment';

type Props = { selectedDate: Moment; isFeatured: boolean };

const NoBackupsOnSelectedDate: FunctionComponent< Props > = ( { selectedDate, isFeatured } ) => {
const translate = useTranslate();
const siteId = useSelector( ( state ) => getSelectedSiteId( state ) ) || -1;
const siteSlug = useSelector( ( state ) => getSelectedSiteSlug( state ) ) || '';
const siteUrl = useSelector( ( state ) => getSiteUrl( state, siteId ) );

const displayDate = selectedDate.format( 'MMM Do' );
const nextDate = selectedDate.clone().add( 1, 'days' );
const displayNextDate = nextDate.format( 'll' );

return (
<Card
className={ classNames( 'backup-card', {
'is-featured': isFeatured,
} ) }
>
<div className="backup-card__main">
<div className="backup-card__header">
<div className="backup-card__header-text">
<h2 className="backup-card__date">
{ translate( 'No Backup for %(displayDate)s', {
args: { displayDate },
} ) }
</h2>
<p className="backup-card__title backup-card__title--delayed">
<img className="backup-card__icon" src={ cloudWarningIcon } alt="" />
{ translate( 'Backup was delayed' ) }
</p>
</div>
</div>
<ul className="backup-card__actions">
<li>
<Button
className="backup-card__support-button"
href={ contactSupportUrl( siteUrl ) }
target="_blank"
rel="noopener noreferrer"
primary
>
{ translate( 'Contact support' ) }
</Button>
</li>
</ul>
</div>
<div className="backup-card__about">
<h3 className="backup-card__about-heading">{ translate( 'About this backup' ) }</h3>
<div className="backup-card__about-content">
<ul className="backup-card__about-list">
<li>
<div className="backup-card__about-media backup-card__about-media--notice">
<JetpackLogo className="backup-card__jetpack-logo-warning" size={ 32 } />
</div>
<div className="backup-card__about-body">
{ translate(
'Don’t worry, the backup was most likely completed in the early hours of the following morning. ' +
'Check the following day ({{link}}%(displayNextDate)s{{/link}}) or contact support if you need help.',
{
args: { displayNextDate },
components: {
link: (
<a
href={ backupMainPath( siteSlug, {
date: nextDate.format( INDEX_FORMAT ),
} ) }
/>
),
},
}
) }
</div>
</li>
</ul>
</div>
</div>
</Card>
);
};

export default NoBackupsOnSelectedDate;
Loading

0 comments on commit 7bf8123

Please sign in to comment.