Adding images to registration process

This commit is contained in:
Rob Harrington
2014-09-19 12:34:48 +10:00
parent e9dd863462
commit d3e72b5a2a
23 changed files with 283 additions and 46 deletions

View File

@@ -42,6 +42,7 @@ gem 'gmaps4rails'
gem 'spinjs-rails'
gem 'rack-ssl', :require => 'rack/ssl'
gem 'custom_error_message', :github => 'jeremydurham/custom-err-msg'
gem 'angularjs-file-upload-rails', '~> 1.1.0'
gem 'foreigner'
gem 'immigrant'

View File

@@ -153,6 +153,7 @@ GEM
railties (>= 3.1)
sprockets
tilt
angularjs-file-upload-rails (1.1.0)
angularjs-rails (1.2.13)
ansi (1.4.2)
arel (3.0.3)
@@ -505,6 +506,7 @@ DEPENDENCIES
active_model_serializers
andand
angular-rails-templates
angularjs-file-upload-rails (~> 1.1.0)
angularjs-rails
awesome_print
aws-sdk

View File

@@ -15,6 +15,8 @@
#= require ../shared/bindonce.min.js
#= require ../shared/ng-infinite-scroll.min.js
#= require ../shared/angular-local-storage.js
#= require angularjs-file-upload
#= require angular-rails-templates
#= require_tree ../templates

View File

@@ -0,0 +1,13 @@
angular.module('Darkswarm').controller "EnterpriseImageCtrl", ($scope, EnterpriseImageService) ->
$scope.imageStep = 'logo'
$scope.imageSteps = ['logo', 'promo']
$scope.imageUploader = EnterpriseImageService.imageUploader
$scope.imageSelect = (image_step) ->
EnterpriseImageService.imageSrc = null
$scope.imageStep = image_step
$scope.imageSrc = ->
EnterpriseImageService.imageSrc

View File

@@ -12,4 +12,4 @@ Darkswarm.controller "RegistrationFormCtrl", ($scope, RegistrationService, Enter
EnterpriseRegistrationService.update(nextStep) if $scope.valid(form)
$scope.selectIfValid = (nextStep, form) ->
RegistrationService.select(nextStep) if $scope.valid(form)
RegistrationService.select(nextStep) if $scope.valid(form)

View File

@@ -1,18 +1,19 @@
window.Darkswarm = angular.module("Darkswarm", ["ngResource",
'mm.foundation',
'angularLocalStorage',
'pasvaz.bindonce',
'infinite-scroll',
'angular-flash.service',
window.Darkswarm = angular.module("Darkswarm", ["ngResource",
'mm.foundation',
'angularLocalStorage',
'pasvaz.bindonce',
'infinite-scroll',
'angular-flash.service',
'templates',
'ngSanitize',
'ngAnimate',
'google-maps',
'duScroll',
'angularFileUpload',
]).config ($httpProvider, $tooltipProvider, $locationProvider, $anchorScrollProvider) ->
$httpProvider.defaults.headers.post['X-CSRF-Token'] = $('meta[name="csrf-token"]').attr('content')
$httpProvider.defaults.headers.put['X-CSRF-Token'] = $('meta[name="csrf-token"]').attr('content')
$httpProvider.defaults.headers['common']['X-Requested-With'] = 'XMLHttpRequest'
$httpProvider.defaults.headers.post['X-CSRF-Token'] = $('meta[name="csrf-token"]').attr('content')
$httpProvider.defaults.headers.put['X-CSRF-Token'] = $('meta[name="csrf-token"]').attr('content')
$httpProvider.defaults.headers['common']['X-Requested-With'] = 'XMLHttpRequest'
$httpProvider.defaults.headers.common.Accept = "application/json, text/javascript, */*"
# This allows us to trigger these two events on tooltips
@@ -20,4 +21,3 @@ window.Darkswarm = angular.module("Darkswarm", ["ngResource",
# We manually handle our scrolling
$anchorScrollProvider.disableAutoScrolling()

View File

@@ -0,0 +1,13 @@
Darkswarm.factory "EnterpriseImageService", (EnterpriseRegistrationService, FileUploader, spreeApiKey) ->
new class EnterpriseImageService
imageSrc: null
imageUploader: new FileUploader
headers:
'X-Spree-Token': spreeApiKey
url: "/api/enterprises/#{EnterpriseRegistrationService.enterprise.id}/update_image"
autoUpload: true
constructor: ->
@imageUploader.onSuccessItem = (image, response) =>
@imageSrc = response

View File

@@ -54,4 +54,4 @@ Darkswarm.factory "EnterpriseRegistrationService", ($http, RegistrationService,
enterprise[key] = value
enterprise.address_attributes = @enterprise.address if @enterprise.address?
enterprise.address_attributes.country_id = @enterprise.country.id if @enterprise.country?
enterprise
enterprise

View File

@@ -1,4 +1,4 @@
Darkswarm.factory "RegistrationService", (Navigation, $modal, Loading)->
angular.module('Darkswarm').factory "RegistrationService", (Navigation, $modal, Loading)->
new class RegistrationService
constructor: ->
@@ -20,4 +20,4 @@ Darkswarm.factory "RegistrationService", (Navigation, $modal, Loading)->
close: ->
Loading.message = "Taking you back to the home page"
Navigation.go "/"
Navigation.go "/"

View File

@@ -9,7 +9,7 @@
{{ enterprise.name }}
%ng-include{ src: "'registration/steps.html'" }
%form{ name: 'about', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "update('social',about)" } }
%form{ name: 'about', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "update('images',about)" } }
.row
.small-12.columns
.alert-box.alert{"data-alert" => ""}

View File

@@ -1,14 +1,20 @@
.container#registration-images
.container#registration-images{ 'nv-file-drop' => true, uploader: "imageUploader", options:"{ alias: imageStep }", ng: { controller: "EnterpriseImageCtrl" } }
.header
%h2 Thanks!
%h5 Let's upload some pretty pictures so your profile looks great! :)
%ng-include{ src: "'registration/steps.html'" }
.row.content
%form{ name: 'images', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "select('social')" } }
.row{ ng: { repeat: 'image_step in imageSteps', show: "imageStep == image_step" } }
%ng-include{ src: "'registration/images/'+ image_step + '.html'" }
.row.buttons
.small-12.columns
%input.button.primary.left{ type: "button", value: "Back", ng: { click: "select('about')" } }
 
%input.button.primary.right{ type: "button", value: "Continue", ng: { click: "select('social')" } }
.row.buttons.pad-top{ ng: { if: "imageStep == 'logo'" } }
.small-12.columns
%input.button.primary{ type: "button", value: "Back", ng: { click: "select('about')" } }
 
%input.button.primary{ type: "button", value: "Continue", ng: { click: "imageSelect('promo')" } }
.row.buttons.pad-top{ ng: { if: "imageStep == 'promo'" } }
.small-12.columns
%input.button.primary{ type: "button", value: "Back", ng: { click: "imageSelect('logo')" } }
 
%input.button.primary{ type: "submit", value: "Continue" }

View File

@@ -0,0 +1,41 @@
.small-12.medium-12.large-6.columns
.row
.small-12.columns.center
.row
.small-12.columns.center
%h4
Step 1. Select Logo Image
.row
.small-12.columns.center
%span.small
Tip: Square images will work best, preferably at least 300×300px
.row.pad-top
.small-12.columns
.image-select.small-12.columns
%label.small-12.columns.button{ for: 'image-select' } Choose a logo image
%input#image-select{ type: 'file', hidden: true, 'nv-file-select' => true, uploader: "imageUploader", options: '{ alias: imageStep }' }
.row.show-for-large-up
.large-12.columns
%span#or.large-12.columns
OR
.row.show-for-large-up
.large-12.columns
#image-over{ 'nv-file-over' => true, uploader: "imageUploader" }
Drag and drop your logo here
.small-12.medium-12.large-6.columns
.row
.small-12.columns.center
.row
.small-12.columns.center
%h4
Step 2. Review Your Logo
.row
.small-12.columns.center
%span.small
Tip: for best results, your logo should fill the available space
.row.pad-top
.small-12.columns.center
#image-placeholder.logo
%img{ ng: { show: "imageSrc()", src: '{{ imageSrc() }}' } }
.message{ ng: { hide: "imageSrc()" } }
Your logo will appear here for review once uploaded

View File

@@ -0,0 +1,39 @@
.small-12.medium-12.large-12.columns
.row
.small-12.columns.center
%h4
Step 3. Select Promo Image
.row
.small-12.medium-12.large-5.columns.center
.row
.small-12.columns.center
%span.small
Tip: Shown as a banner, preferred size is 1200×260px
.row.pad-top
.small-12.columns
.image-select.small-12.columns
%label.small-12.columns.button{ for: 'image-select' } Choose a promo image
%input#image-select{ type: 'file', hidden: true, 'nv-file-select' => true, uploader: "imageUploader", options: '{ alias: imageStep }' }
.large-2.columns
%span#or.horizontal.large-12.columns
OR
.large-5.columns
#image-over{ 'nv-file-over' => true, uploader: "imageUploader" }
Drag and drop your promo here
.small-12.medium-12.large-12.columns.pad-top
.row
.small-12.columns.center
%h4
Step 4. Review Your Promo Banner
.row
.small-12.columns.center
.row
.small-12.columns.center
%span.small
Tip: for best results, your promo image should fill the available space
.row.pad-top
.small-12.columns.center
#image-placeholder.promo
%img{ ng: { show: "imageSrc()", src: '{{ imageSrc() }}' } }
.message{ ng: { hide: "imageSrc()" } }
Your logo will appear here for review once uploaded

View File

@@ -6,35 +6,34 @@
.small-12.medium-3.large-2.columns.text-right.hide-for-small-only
%img{:src => "/assets/potatoes.png"}
.small-12.medium-9.large-10.columns
%p
Your profile gives you an online presence on the
%strong Open Food Network,
%p
Your profile gives you an online presence on the
%strong Open Food Network,
allowing you to easily connect with potential customers or partners. You can always choose to update your info later, as well as choose to upgrade your Profile to and Online Store, where you can sell products, track orders and receive payments. Creating a profile takes about 5-10 minutes.
.row{ 'data-equalizer' => true }
.small-12.medium-6.large-6.columns.pad-top{ 'data-equalizer-watch' => true }
%h5 You'll need the following:
%ul.check-list
%li
%li
Your enterprise address and contact details
%li
%li
Your logo image
%li
%li
A pretty picture for your profile header
%li
%li
Some 'About Us' text
.small-12.medium-6.large-6.columns{ 'data-equalizer-watch' => true}
.highlight-box
%h5 Your profile entitles you to:
%ul.small-block-grid-1
%li
%li
%i.ofn-i_020-search
A searchable listing
%li
%li
%i.ofn-i_040-hub
A pin on the OFN map
.row
.small-12.columns
%hr
%input.button.primary{ type: "button", value: "Let's get started!", ng: { click: "select('details')" } }

View File

@@ -30,6 +30,6 @@
.row.buttons
.small-12.columns
%input.button.secondary{ type: "button", value: "Back", ng: { click: "select('about')" } }
%input.button.secondary{ type: "button", value: "Back", ng: { click: "select('images')" } }
 
%input.button.primary{ type: "submit", value: "Continue" }
%input.button.primary{ type: "submit", value: "Continue" }

View File

@@ -80,6 +80,53 @@
color: #333
@include box-shadow(inset 0 0 1px 0 #fff)
.image-select
label
font-size: 18px
padding: 21px 0px
#logo-select
display: none
#image-over
font-size: 18px
padding: 41px 0px
border: 3px dashed #494949
text-align: center
font-weight: bold
color: #494949
&.nv-file-over
background-color: #78cd91
#or
text-align: center
font-weight: bold
font-size: 18px
padding: 21px 0px
&.horizontal
padding: 41px 0px
#image-placeholder
font-size: 18px
font-weight: bold
color: #373737
background-color: #e1e1e1
text-align: center
border: 3px dashed #494949
margin-left: auto
margin-right: auto
&.logo
.message
padding-top: 6em
width: 306px
height: 306px
&.promo
.message
padding-top: 4em
width: 726px
height: 166px
#registration-details
#enterprise-types
a.panel
@@ -112,4 +159,3 @@
p
clear: both
font-size: 0.875rem

View File

@@ -27,9 +27,9 @@ module Api
end
def update
authorize! :update, Enterprise
@enterprise = Enterprise.find(params[:id])
authorize! :update, @enterprise
if @enterprise.update_attributes(params[:enterprise])
render text: @enterprise.id, :status => 200
else
@@ -37,6 +37,19 @@ module Api
end
end
def update_image
@enterprise = Enterprise.find(params[:id])
authorize! :update, @enterprise
if params[:logo] && @enterprise.update_attributes( { logo: params[:logo] } )
render text: @enterprise.logo.url(:medium), :status => 200
elsif params[:promo] && @enterprise.update_attributes( { promo_image: params[:promo] } )
render text: @enterprise.promo_image.url(:medium), :status => 200
else
invalid_resource!(@enterprise)
end
end
private
def override_owner

View File

@@ -34,7 +34,7 @@ class Enterprise < ActiveRecord::Base
path: 'public/images/enterprises/logos/:id/:style/:basename.:extension'
has_attached_file :promo_image,
styles: { large: "1200x260#", thumb: "100x100>" },
styles: { large: "1200x260#", medium: "720x156#", thumb: "100x100>" },
url: '/images/enterprises/promo_images/:id/:style/:basename.:extension',
path: 'public/images/enterprises/promo_images/:id/:style/:basename.:extension'

View File

@@ -71,7 +71,7 @@ class AbilityDecorator
can [:admin, :index, :read, :create, :edit, :update, :fire], Spree::Shipment
can [:admin, :index, :read, :create, :edit, :update, :fire], Spree::Adjustment
can [:admin, :index, :read, :create, :edit, :update, :fire], Spree::ReturnAuthorization
can [:create], OrderCycle
can [:admin, :index, :read, :edit, :update, :bulk_update, :clone], OrderCycle do |order_cycle|
user.enterprises.include? order_cycle.coordinator

View File

@@ -67,6 +67,7 @@ Openfoodnetwork::Application.routes.draw do
namespace :api do
resources :enterprises do
post :update_image, on: :member
get :managed, on: :collection
get :accessible, on: :collection
end

View File

@@ -0,0 +1,54 @@
require 'spec_helper'
module Api
describe EnterprisesController do
include AuthenticationWorkflow
render_views
let(:enterprise) { create(:distributor_enterprise) }
before do
stub_authentication!
Enterprise.stub(:find).and_return(enterprise)
end
describe "as an enterprise manager" do
let(:enterprise_manager) { create_enterprise_user }
before do
enterprise_manager.enterprise_roles.build(enterprise: enterprise).save
Spree.user_class.stub :find_by_spree_api_key => enterprise_manager
end
describe "submitting a valid image" do
before do
enterprise.stub(:update_attributes).and_return(true)
end
it "I can update enterprise image" do
spree_post :update_image, logo: 'a logo'
response.should be_success
end
end
end
describe "as an non-managing user" do
let(:non_managing_user) { create_enterprise_user }
before do
Spree.user_class.stub :find_by_spree_api_key => non_managing_user
end
describe "submitting a valid image" do
before do
enterprise.stub(:update_attributes).and_return(true)
end
it "I can't update enterprise image" do
spree_post :update_image, logo: 'a logo'
assert_unauthorized!
end
end
end
end
end

View File

@@ -60,15 +60,22 @@ feature "Registration", js: true do
fill_in 'enterprise_acn', with: '54321'
click_button 'Continue'
# Enterprise should be updated
expect(page).to have_content 'Last step!'
# Enterprise should be update
expect(page).to have_content "Let's upload some pretty pictures so your profile looks great!"
e.reload
expect(e.description).to eq "Short description"
expect(e.long_description).to eq "Long description"
expect(e.abn).to eq '12345'
expect(e.acn).to eq '54321'
# Images
# Move from logo page
click_button 'Continue'
# Move from promo page
click_button 'Continue'
# Filling in social
expect(page).to have_content 'Last step!'
fill_in 'enterprise_website', with: 'www.shop.com'
fill_in 'enterprise_facebook', with: 'FaCeBoOk'
fill_in 'enterprise_linkedin', with: 'LiNkEdIn'

View File

@@ -7,7 +7,7 @@ describe "enterpriseCtrl", ->
beforeEach ->
module('admin.enterprises')
Enterprise =
Enterprise =
enterprise:
payment_method_ids: [ 1, 3 ]
shipping_method_ids: [ 2, 4 ]
@@ -15,7 +15,7 @@ describe "enterpriseCtrl", ->
paymentMethods: [ { id: 1 }, { id: 2 }, { id: 3 }, { id: 4 } ]
ShippingMethods =
shippingMethods: [ { id: 1 }, { id: 2 }, { id: 3 }, { id: 4 } ]
inject ($controller) ->
scope = {}
ctrl = $controller 'enterpriseCtrl', {$scope: scope, Enterprise: Enterprise, PaymentMethods: PaymentMethods, ShippingMethods: ShippingMethods}
@@ -82,4 +82,4 @@ describe "enterpriseCtrl", ->
describe "counting selected shipping methods", ->
it "counts only shipping methods with selected: true", ->
scope.ShippingMethods = [ { selected: true }, { selected: true }, { selected: false }, { selected: true } ]
expect(scope.selectedShippingMethodsCount()).toBe 3
expect(scope.selectedShippingMethodsCount()).toBe 3