mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-24 20:36:49 +00:00
Adding images to registration process
This commit is contained in:
1
Gemfile
1
Gemfile
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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 "/"
|
||||
|
||||
@@ -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" => ""}
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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')" } }
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
54
spec/controllers/api/enterprises_controller_spec.rb
Normal file
54
spec/controllers/api/enterprises_controller_spec.rb
Normal 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
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user