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

Plans: Add beginning of PlanOverview, PlanFeatures, and PlanStatus #1395

Merged
merged 14 commits into from
Dec 10, 2015
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions assets/stylesheets/_components.scss
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@
@import 'my-sites/people/people-profile/style';
@import 'my-sites/people/people-notices/style';
@import 'my-sites/plans/style';
@import 'my-sites/plans/plan-overview/plan-status/style';
@import 'my-sites/post/post-image/style';
@import 'my-sites/post-selector/style';
@import 'my-sites/post-trends/style';
Expand Down
27 changes: 27 additions & 0 deletions client/lib/site-specific-plans-details-list/assembler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Internal dependencies
*/
import moment from 'moment';

function createSiteSpecificPlanObject( plan ) {
return {
currentPlan: Boolean( plan.current_plan ),
expiry: plan.expiry,
expiryMoment: moment( plan.expiry ),
formattedDiscount: plan.formatted_discount,
formattedPrice: plan.formatted_price,
freeTrial: Boolean( plan.free_trial ),
id: Number( plan.id ),
productName: plan.product_name,
productSlug: plan.product_slug,
rawDiscount: plan.raw_discount,
rawPrice: plan.raw_price,
subscribedDate: plan.subscribed_date,
subscribedMoment: moment( plan.subscribed_date ),
userFacingExpiry: plan.user_facing_expiry,
// we add a day to the user facing expiry so that it isn't expired on the last day
userFacingExpiryMoment: moment( plan.user_facing_expiry ).add( { day: 1 } )
}
}

export default { createSiteSpecificPlanObject };
5 changes: 5 additions & 0 deletions client/lib/site-specific-plans-details-list/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* External dependencies
*/
var debug = require( 'debug' )( 'calypso:site-specific-plans-details-list' ),
find = require( 'lodash/collection/find' ),
store = require( 'store' );

/**
Expand Down Expand Up @@ -57,6 +58,10 @@ SiteSpecificPlansDetailsList.prototype.hasJpphpBundle = function( siteDomain ) {
return this.get( siteDomain, 'host-bundle' ).current_plan;
};

SiteSpecificPlansDetailsList.prototype.getCurrentPlan = function( siteDomain ) {
return find( this.data[ siteDomain ], { current_plan: true } );
};

/**
* Fetch the site specific plan data from WordPress.com via the REST API.
*
Expand Down
45 changes: 28 additions & 17 deletions client/my-sites/plans/main.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ var React = require( 'react/addons' );
* Internal dependencies
*/
var analytics = require( 'analytics' ),
config = require( 'config' ),
observe = require( 'lib/mixins/data-observe' ),
PlanList = require( 'components/plans/plan-list' ),
PlanOverview = require( './plan-overview' ),
siteSpecificPlansDetailsMixin = require( 'components/plans/site-specific-plan-details-mixin' ),
SidebarNavigation = require( 'my-sites/sidebar-navigation' ),
UpgradesNavigation = require( 'my-sites/upgrades/navigation' ),
Gridicon = require( 'components/gridicon' );
Gridicon = require( 'components/gridicon' ),
createSiteSpecificPlanObject = require( 'lib/site-specific-plans-details-list/assembler' ).createSiteSpecificPlanObject;

module.exports = React.createClass( {
displayName: 'Plans',
Expand Down Expand Up @@ -51,20 +54,37 @@ module.exports = React.createClass( {
);
},

sidebarNavigation: function() {
return <SidebarNavigation />;
},

render: function() {
var classNames = 'main main-column ',
selectedSiteDomain = this.props.sites.getSelectedSite().domain,
hasJpphpBundle = this.props.siteSpecificPlansDetailsList &&
this.props.siteSpecificPlansDetailsList.hasJpphpBundle( this.props.sites.getSelectedSite().domain );
this.props.siteSpecificPlansDetailsList.hasJpphpBundle( selectedSiteDomain ),
currentPlan;

if ( this.props.siteSpecificPlansDetailsList.hasLoadedFromServer( selectedSiteDomain ) ) {
currentPlan = createSiteSpecificPlanObject( this.props.siteSpecificPlansDetailsList.getCurrentPlan( selectedSiteDomain ) );

if ( config.isEnabled( 'upgrades/free-trials' ) && currentPlan.freeTrial ) {
return (
<PlanOverview
path={ this.props.context.path }
cart={ this.props.cart }
plan={ currentPlan }
selectedSite={ this.props.sites.getSelectedSite() } />
);
}
}

return (
<div className={ classNames } role="main">
{ this.sidebarNavigation() }
<SidebarNavigation />

<div id="plans" className="plans has-sidebar">
{ this.sectionNavigation() }
<UpgradesNavigation
path={ this.props.context.path }
cart={ this.props.cart }
selectedSite={ this.props.sites.getSelectedSite() } />

<PlanList
sites={ this.props.sites }
plans={ this.props.plans.get() }
Expand All @@ -76,14 +96,5 @@ module.exports = React.createClass( {
</div>
</div>
);
},

sectionNavigation: function() {
return (
<UpgradesNavigation
path={ this.props.context.path }
cart={ this.props.cart }
selectedSite={ this.props.sites.getSelectedSite() } />
);
}
} );
44 changes: 44 additions & 0 deletions client/my-sites/plans/plan-overview/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* External dependencies
*/
import React from 'react';

/**
* Internal dependencies
*/
import Main from 'components/main';
import PlanFeatures from 'my-sites/plans/plan-overview/plan-features';
import PlanStatus from 'my-sites/plans/plan-overview/plan-status';
import SidebarNavigation from 'my-sites/sidebar-navigation';
import UpgradesNavigation from 'my-sites/upgrades/navigation';

const PlanOverview = React.createClass( {
propTypes: {
cart: React.PropTypes.object.isRequired,
plan: React.PropTypes.object.isRequired,
path: React.PropTypes.string.isRequired,
selectedSite: React.PropTypes.oneOfType( [
React.PropTypes.object,
React.PropTypes.bool
] ).isRequired
},

render() {
return (
<Main className="plan-overview">
<SidebarNavigation />

<UpgradesNavigation
cart={ this.props.cart }
path={ this.props.path }
selectedSite={ this.props.selectedSite } />

<PlanStatus plan={ this.props.plan } />

<PlanFeatures />
</Main>
);
}
} );

export default PlanOverview;
25 changes: 25 additions & 0 deletions client/my-sites/plans/plan-overview/plan-features/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* External dependencies
*/
import React from 'react';

/**
* Internal dependencies
*/
import Card from 'components/card';
import SectionHeader from 'components/section-header';

const PlanFeatures = React.createClass( {
render() {
return (
<div>
<SectionHeader label={ this.translate( "Your Site's Features" ) } />
<Card>
...
</Card>
</div>
);
}
} );

export default PlanFeatures;
88 changes: 88 additions & 0 deletions client/my-sites/plans/plan-overview/plan-status/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/**
* External dependencies
*/
import React from 'react';
import classNames from 'classnames';

/**
* Internal dependencies
*/
import CompactCard from 'components/card/compact';
import ProgressBar from 'components/progress-bar';
import { isPremium, isBusiness } from 'lib/products-values';

const PlanStatus = React.createClass( {
propTypes: {
plan: React.PropTypes.object.isRequired
},

getDaysUntilExpiry() {
const { userFacingExpiryMoment } = this.props.plan;

return userFacingExpiryMoment.diff( this.moment(), 'days' );
},

renderProgressBar() {
const { subscribedMoment, userFacingExpiryMoment } = this.props.plan,
// we strip the hour/minute/second/millisecond data here from `subscribed_date` to match `expiry`
trialPeriodInDays = userFacingExpiryMoment.diff( subscribedMoment, 'days' ),
timeUntilExpiryInDays = this.getDaysUntilExpiry(),
progress = Math.max( 0.5, trialPeriodInDays - timeUntilExpiryInDays );

return (
<ProgressBar
value={ progress }
total={ trialPeriodInDays } />
);
},

renderDaysRemaining() {
return this.translate(
'%(daysUntilExpiry)s day remaining',
'%(daysUntilExpiry)s days remaining',
{
args: { daysUntilExpiry: this.getDaysUntilExpiry() },
count: this.getDaysUntilExpiry(),
context: 'The amount of time until the trial plan expires, e.g. "5 days remaining"'
}
);
},

render() {
const { plan } = this.props,
iconClasses = classNames( 'plan-status__icon', {
'is-premium': isPremium( plan ),
'is-business': isBusiness( plan )
} );

return (
<div className="plan-status">
<CompactCard className="plan-status__info">
<div className={ iconClasses } />
<div className="plan-status__header">
<span className="plan-status__text">
{ this.translate( 'Your Current Plan:' ) }
</span>
<h1 className="plan-status__plan">
{
this.translate( '%(planName)s Free Trial', {
args: { planName: this.props.plan.productName }
} )
}
</h1>
</div>
</CompactCard>
<CompactCard>
<div className="plan-status__time-until-expiry">
{ this.renderDaysRemaining() }
</div>
<div className="plan-status__progress">
{ this.renderProgressBar() }
</div>
</CompactCard>
</div>
);
}
} );

export default PlanStatus;
56 changes: 56 additions & 0 deletions client/my-sites/plans/plan-overview/plan-status/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
.plan-status {
margin-bottom: 30px;
}

.plan-status__info {
align-items: center;
display: flex;
}

.plan-status__header {
float: left;
margin-left: 20px;
width: 80%;
}

.plan-status__icon {
background-position: center;
background-repeat: no-repeat;
float: left;
height: 50px;
width: 50px;

&.is-premium {
background-image: url( '/calypso/images/plans/plan-trial-premium.svg' );
}

&.is-business {
background-image: url( '/calypso/images/plans/plan-trial-business.svg' );
}
}

.plan-status__text {
color: $gray;
font-size: 12px;
text-transform: uppercase;
}

.plan-status__plan {
line-height: 26px;
font-family: Merriweather, Georgia, "Times New Roman", Times, serif;
font-size: 24px;
font-weight: 700;
}

.plan-status__time-until-expiry {
box-sizing: border-box;
float: left;
font-weight: 700;
padding-right: 20px;
text-align: center;
width: 180px;
}

.plan-status__progress {
margin-left: 180px;
}
Loading