diff --git a/services/web/app/views/subscriptions/new.jade b/services/web/app/views/subscriptions/new.jade index 852a2f5ab3..d26a760e69 100644 --- a/services/web/app/views/subscriptions/new.jade +++ b/services/web/app/views/subscriptions/new.jade @@ -175,11 +175,15 @@ block content strong {{price.currency.symbol}}{{price.next.total}} .col-md-12(sixpack-when="simple") - form(ng-if="planName",novalidate) + form( + ng-if="planName" + name="simpleCCForm" + novalidate + ) div.payment-method-toggle a.payment-method-toggle-switch( href - ng-click="paymentMethod = 'credit_card'" + ng-click="setPaymentMethod('credit_card');" ng-class="paymentMethod === 'credit_card' ? 'payment-method-toggle-switch-selected' : ''" ) i.fa.fa-cc-mastercard.fa-2x @@ -189,7 +193,7 @@ block content i.fa.fa-cc-amex.fa-2x a.payment-method-toggle-switch( href - ng-click="paymentMethod = 'paypal'" + ng-click="setPaymentMethod('paypal');" ng-class="paymentMethod === 'paypal' ? 'payment-method-toggle-switch-selected' : ''" ) i.fa.fa-cc-paypal.fa-2x @@ -200,40 +204,68 @@ block content div(ng-if="paymentMethod === 'credit_card'") .row .col-xs-6 - .form-group(ng-class="validation.errorFields.first_name ? 'has-error' : ''") + .form-group(ng-class="validation.errorFields.first_name || inputHasError(simpleCCForm.firstName) ? 'has-error' : ''") label(for="first-name") #{translate('first_name')} - input#first-name.form-control(type='text', value='', maxlength='255', , onkeyup='', data-recurly="first_name", ng-model="data.first_name", required) + input#first-name.form-control( + type="text" + maxlength='255' + data-recurly="first_name" + name="firstName" + ng-model="data.first_name" + required + ) + span.input-feedback-message {{ simpleCCForm.firstName.$error.required ? 'This field is required' : '' }} .col-xs-6 - .form-group(for="last-name",ng-class="validation.errorFields.last_name ? 'has-error' : ''") + .form-group(for="last-name",ng-class="validation.errorFields.last_name || inputHasError(simpleCCForm.lastName)? 'has-error' : ''") label(for="last-name") #{translate('last_name')} - input#last-name.form-control(type='text', value='', maxlength='255', onkeyup='', data-recurly="last_name", ng-model="data.last_name", required) + input#last-name.form-control( + type="text" + maxlength='255' + data-recurly="last_name" + name="lastName" + ng-model="data.last_name" + required + ) + span.input-feedback-message {{ simpleCCForm.lastName.$error.required ? 'This field is required' : '' }} - .form-group(ng-class="validation.correctCardNumber == false || validation.errorFields.number ? 'has-error' : ''") + .form-group(ng-class="validation.correctCardNumber == false || validation.errorFields.number || inputHasError(simpleCCForm.ccNumber) ? 'has-error' : ''") label(for="card-no") #{translate("credit_card_number")} input#card-no.form-control( type="text" ng-model="data.number" - ng-blur="validateCardNumber()" - cc-format-card-number) + name="ccNumber" + required + ng-change="validateCardNumber()" + cc-format-card-number + ) + span.input-feedback-message {{ simpleCCForm.ccNumber.$error.required ? 'This field is required' : 'Please re-check the card number' }} + .row .col-xs-6 - .form-group.has-feedback(ng-class="validation.correctExpiry == false || validation.errorFields.expiry ? 'has-error' : ''") + .form-group.has-feedback(ng-class="validation.correctExpiry == false || validation.errorFields.expiry || inputHasError(simpleCCForm.expiry) ? 'has-error' : ''") label #{translate("expiry")} input.form-control( type="text" ng-model="data.mmYY" + name="expiry" + required placeholder="MM / YY" - ng-blur="validateExpiry()" + ng-change="updateExpiry(); validateExpiry()" cc-format-expiry ) + span.input-feedback-message {{ simpleCCForm.expiry.$error.required ? 'This field is required' : 'Please re-check the expiry date' }} + .col-xs-6 - .form-group.has-feedback(ng-class="validation.correctCvv == false || validation.errorFields.cvv ? 'has-error' : ''") + .form-group.has-feedback(ng-class="validation.correctCvv == false || validation.errorFields.cvv || inputHasError(simpleCCForm.cvv) ? 'has-error' : ''") label #{translate("security_code")} input.form-control( type="text" ng-model="data.cvv" - ng-blur="validateCvv()" - cc-format-sec-code) + ng-change="validateCvv()" + name="cvv" + required + cc-format-sec-code + ) .form-control-feedback a.form-helper( href @@ -242,22 +274,26 @@ block content tooltip-trigger="mouseenter" tooltip-append-to-body="true" ) ? - .form-group(ng-class="validation.errorFields.country ? 'has-error' : ''") + span.input-feedback-message {{ simpleCCForm.cvv.$error.required ? 'This field is required' : 'Please re-check the security code' }} + + .form-group(ng-class="validation.errorFields.country || inputHasError(simpleCCForm.country) ? 'has-error' : ''") label(for="country") #{translate('country')} select#country.form-control( data-recurly="country" ng-model="data.country" + name="country" ng-change="updateCountry()" required ) mixin countries_options() + span.input-feedback-message {{ simpleCCForm.country.$error.required ? 'This field is required' : '' }} div if (showVatField) .form-group label(for="vat-no") #{translate('vat_number')} input#vat-no.form-control( - type='text' + type="text" ng-blur="applyVatNumber()" ng-model="data.vat_number" ) @@ -265,7 +301,7 @@ block content .form-group label(for="coupon-code") #{translate('coupon_code')} input#coupon-code.form-control( - type='text' + type="text" ng-blur="applyCoupon()" ng-model="data.coupon" ) @@ -283,7 +319,7 @@ block content div.payment-submit button.btn.btn-success.btn-block( ng-click="submit()" - ng-disabled="processing" + ng-disabled="processing || !isFormValid(simpleCCForm);" sixpack-convert="payment-left-menu-bottom" ) {{ paymentMethod === 'credit_card' ? '#{translate("upgrade_cc_btn")}' : '#{translate("upgrade_paypal_btn")}' }} diff --git a/services/web/public/coffee/main/new-subscription.coffee b/services/web/public/coffee/main/new-subscription.coffee index 96575eb25a..f1c81458ef 100644 --- a/services/web/public/coffee/main/new-subscription.coffee +++ b/services/web/public/coffee/main/new-subscription.coffee @@ -32,12 +32,6 @@ define [ coupon: window.couponCode mmYY: "" - $scope.$watch 'data.mmYY', (newVal) -> - parsedDateObj = ccUtils.parseExpiry newVal - if parsedDateObj? - $scope.data.month = parsedDateObj.month - $scope.data.year = parsedDateObj.year - $scope.validation = correctCardNumber : true correctExpiry: true @@ -81,11 +75,16 @@ define [ $scope.applyVatNumber = -> pricing.tax({tax_code: 'digital', vat_number: $scope.data.vat_number}).done() - $scope.changeCurrency = (newCurrency)-> $scope.currencyCode = newCurrency pricing.currency(newCurrency).done() + $scope.updateExpiry = () -> + parsedDateObj = ccUtils.parseExpiry $scope.data.mmYY + if parsedDateObj? + $scope.data.month = parsedDateObj.month + $scope.data.year = parsedDateObj.year + $scope.validateCardNumber = validateCardNumber = -> if $scope.data.number?.length != 0 $scope.validation.correctCardNumber = recurly.validate.cardNumber($scope.data.number) @@ -98,14 +97,26 @@ define [ if $scope.data.cvv?.length != 0 $scope.validation.correctCvv = recurly.validate.cvv($scope.data.cvv) + $scope.inputHasError = inputHasError = (formItem) -> + if !formItem? + return false + + return (formItem.$touched && formItem.$invalid) + + $scope.isFormValid = isFormValid = (form) -> + return $scope.paymentMethod == 'paypal' or + (form.$valid and + $scope.validation.correctCardNumber and + $scope.validation.correctExpiry and + $scope.validation.correctCvv) + $scope.updateCountry = -> pricing.address({country:$scope.data.country}).done() - $scope.changePaymentMethod = (paymentMethod)-> - if paymentMethod == "paypal" - $scope.usePaypal = true - else - $scope.usePaypal = false + $scope.setPaymentMethod = setPaymentMethod = (method) -> + $scope.paymentMethod = method; + $scope.validation.errorFields = {} + $scope.genericError = "" completeSubscription = (err, recurly_token_id) -> $scope.validation.errorFields = {} diff --git a/services/web/public/stylesheets/app/subscription.less b/services/web/public/stylesheets/app/subscription.less index 5e7656adc7..b99fe5d774 100644 --- a/services/web/public/stylesheets/app/subscription.less +++ b/services/web/public/stylesheets/app/subscription.less @@ -21,6 +21,15 @@ margin-bottom: -10px; } +.input-feedback-message { + display: none; + font-size: 0.8em; + + .has-error & { + display: inline-block; + } +} + .payment-submit { padding-top: (@line-height-computed / 2); }