added delivery costs to total / added ribica logo just for fun / added validation check when confirming delivery
This commit is contained in:
4
back-office/app/controllers/places_controller.rb
Normal file
4
back-office/app/controllers/places_controller.rb
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
class PlacesController < ApplicationController
|
||||||
|
active_scaffold :"place" do |conf|
|
||||||
|
end
|
||||||
|
end
|
||||||
2
back-office/app/helpers/places_helper.rb
Normal file
2
back-office/app/helpers/places_helper.rb
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
module PlacesHelper
|
||||||
|
end
|
||||||
2
back-office/app/models/place.rb
Normal file
2
back-office/app/models/place.rb
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
class Place < ActiveRecord::Base
|
||||||
|
end
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
Rails.application.routes.draw do
|
Rails.application.routes.draw do
|
||||||
|
|
||||||
|
resources :places do as_routes end
|
||||||
resources :delivery_destinations do as_routes end
|
resources :delivery_destinations do as_routes end
|
||||||
mount RailsAdmin::Engine => '/admin', as: 'rails_admin'
|
mount RailsAdmin::Engine => '/admin', as: 'rails_admin'
|
||||||
resources :filter_criteria_values do as_routes end
|
resources :filter_criteria_values do as_routes end
|
||||||
|
|||||||
49
back-office/test/controllers/places_controller_test.rb
Normal file
49
back-office/test/controllers/places_controller_test.rb
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
require 'test_helper'
|
||||||
|
|
||||||
|
class PlacesControllerTest < ActionController::TestCase
|
||||||
|
setup do
|
||||||
|
@place = places(:one)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "should get index" do
|
||||||
|
get :index
|
||||||
|
assert_response :success
|
||||||
|
assert_not_nil assigns(:places)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "should get new" do
|
||||||
|
get :new
|
||||||
|
assert_response :success
|
||||||
|
end
|
||||||
|
|
||||||
|
test "should create place" do
|
||||||
|
assert_difference('Place.count') do
|
||||||
|
post :create, place: { delivery_price: @place.delivery_price, name: @place.name, postal_code: @place.postal_code }
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_redirected_to place_path(assigns(:place))
|
||||||
|
end
|
||||||
|
|
||||||
|
test "should show place" do
|
||||||
|
get :show, id: @place
|
||||||
|
assert_response :success
|
||||||
|
end
|
||||||
|
|
||||||
|
test "should get edit" do
|
||||||
|
get :edit, id: @place
|
||||||
|
assert_response :success
|
||||||
|
end
|
||||||
|
|
||||||
|
test "should update place" do
|
||||||
|
patch :update, id: @place, place: { delivery_price: @place.delivery_price, name: @place.name, postal_code: @place.postal_code }
|
||||||
|
assert_redirected_to place_path(assigns(:place))
|
||||||
|
end
|
||||||
|
|
||||||
|
test "should destroy place" do
|
||||||
|
assert_difference('Place.count', -1) do
|
||||||
|
delete :destroy, id: @place
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_redirected_to places_path
|
||||||
|
end
|
||||||
|
end
|
||||||
11
back-office/test/fixtures/places.yml
vendored
Normal file
11
back-office/test/fixtures/places.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
|
||||||
|
|
||||||
|
one:
|
||||||
|
postal_code: MyString
|
||||||
|
delivery_price: 9.99
|
||||||
|
name: MyString
|
||||||
|
|
||||||
|
two:
|
||||||
|
postal_code: MyString
|
||||||
|
delivery_price: 9.99
|
||||||
|
name: MyString
|
||||||
7
back-office/test/models/place_test.rb
Normal file
7
back-office/test/models/place_test.rb
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
require 'test_helper'
|
||||||
|
|
||||||
|
class PlaceTest < ActiveSupport::TestCase
|
||||||
|
# test "the truth" do
|
||||||
|
# assert true
|
||||||
|
# end
|
||||||
|
end
|
||||||
5
front-api/controllers/place.rb
Normal file
5
front-api/controllers/place.rb
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
|
||||||
|
get '/place/:postal_code' do
|
||||||
|
Place.by_code_or_default(params["postal_code"]).to_json
|
||||||
|
end
|
||||||
11
front-api/db/migrate/20150312073820_create_places.rb
Normal file
11
front-api/db/migrate/20150312073820_create_places.rb
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
class CreatePlaces < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
create_table :places do |t|
|
||||||
|
t.string :postal_code
|
||||||
|
t.decimal :delivery_price
|
||||||
|
t.string :name
|
||||||
|
|
||||||
|
t.timestamps null: false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 20150222055517) do
|
ActiveRecord::Schema.define(version: 20150312073820) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
@@ -100,6 +100,14 @@ ActiveRecord::Schema.define(version: 20150222055517) do
|
|||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "places", force: :cascade do |t|
|
||||||
|
t.string "postal_code"
|
||||||
|
t.decimal "delivery_price"
|
||||||
|
t.string "name"
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
end
|
||||||
|
|
||||||
create_table "sections", force: :cascade do |t|
|
create_table "sections", force: :cascade do |t|
|
||||||
t.string "name"
|
t.string "name"
|
||||||
end
|
end
|
||||||
|
|||||||
11
front-api/models/place.rb
Normal file
11
front-api/models/place.rb
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
class Place < ActiveRecord::Base
|
||||||
|
|
||||||
|
def self.by_code_or_default(code)
|
||||||
|
# removes garbage and converts whitespace prefixed codes correctly - like " 71000" -> 71000
|
||||||
|
valid_code = code.to_i.to_s
|
||||||
|
place = Place.where(postal_code: valid_code).first
|
||||||
|
place ||= Place.where("postal_code is null or postal_code = ''").first
|
||||||
|
return place
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -38,7 +38,7 @@ var CartPage = React.createClass({
|
|||||||
console.log("bla :" , this.state.items.length);
|
console.log("bla :" , this.state.items.length);
|
||||||
var cartTotal = (
|
var cartTotal = (
|
||||||
<div className="row cart-total">
|
<div className="row cart-total">
|
||||||
<CartTotal items={this.state.items} itemCounts={this.state.itemCounts} />
|
<CartTotal items={this.state.items} itemCounts={this.state.itemCounts} deliveryCosts={this.state.deliveryCosts}/>
|
||||||
<div className="col-md-1 span1">
|
<div className="col-md-1 span1">
|
||||||
<button className="btn btn-warning" onClick={this._onOrderClick}>Izgleda OK</button>
|
<button className="btn btn-warning" onClick={this._onOrderClick}>Izgleda OK</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ var React = require('react'),
|
|||||||
var Router = require('react-router');
|
var Router = require('react-router');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var CartTotal = React.createClass({
|
var CartTotal = React.createClass({
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
@@ -20,9 +24,20 @@ var CartTotal = React.createClass({
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var deliveryCosts = this.props.deliveryCosts.get('delivery_price');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="col-md-3 cart-total">
|
<div className="col-md-3 cart-total">
|
||||||
Ukupno: {Globals.FormatCurrency(total)} + Dostava
|
<div>
|
||||||
|
Roba: {Globals.FormatCurrency(total)}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Dostava: {Globals.FormatCurrency(deliveryCosts)}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Ukupno: {Globals.FormatCurrency(total + (+deliveryCosts))}
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -599,10 +599,10 @@ var CheckoutPage = React.createClass({
|
|||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label className="col-md-4 control-label" htmlFor="order"></label>
|
<label className="col-md-4 control-label" htmlFor="order"></label>
|
||||||
<div className="col-md-8">
|
<div className="col-md-8">
|
||||||
<CartTotal items={this.state.items} itemCounts={this.state.itemCounts} /> <button id="order" name="order" className="btn btn-success" disabled={!this.state.isDeliveryDestinationValid} onClick={this._onOrderClick}>Naruči</button>
|
<CartTotal items={this.state.items} itemCounts={this.state.itemCounts} deliveryCosts={this.state.deliveryCosts} /> <button id="order" name="order" className="btn btn-success" disabled={!this.state.isDeliveryDestinationValid} onClick={this._onOrderClick}>Naruči</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ var RootApp = React.createClass({
|
|||||||
<div className='page-header'>
|
<div className='page-header'>
|
||||||
<h1 className="main-heading">
|
<h1 className="main-heading">
|
||||||
|
|
||||||
<Link to="app"><span style={{color: "#cd3071"}}>ribica.ba</span></Link>
|
<Link to="app"><img src="https://res.cloudinary.com/lfvt7ps2n/image/upload/c_scale,w_132/v1426226452/ribica-ispunjava-zelje_nng0gn.png" /></Link>
|
||||||
|
|
||||||
</h1>
|
</h1>
|
||||||
<div style={{float:'right'}}>
|
<div style={{float:'right'}}>
|
||||||
<div style={{display: 'inline-block'}}>
|
<div style={{display: 'inline-block'}}>
|
||||||
|
|||||||
22
front-ui/app/models/place.js
Normal file
22
front-ui/app/models/place.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
var Globals = require('../globals');
|
||||||
|
var Backbone = require('backbone');
|
||||||
|
var _ = require('underscore');
|
||||||
|
|
||||||
|
var FREE_SHIPPING_LIMIT = 50;
|
||||||
|
|
||||||
|
var Place = Backbone.Model.extend({
|
||||||
|
|
||||||
|
initialize: function(options) {
|
||||||
|
options || (options = {});
|
||||||
|
this.postalCode = options.postalCode;
|
||||||
|
},
|
||||||
|
|
||||||
|
url: function() {
|
||||||
|
return Globals.ApiUrl + '/place/' + this.postalCode.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = Place;
|
||||||
@@ -8,6 +8,7 @@ var ItemInCartCollection = require('../models/itemInCartCollection');
|
|||||||
var ItemCollection = require('../models/itemCollection');
|
var ItemCollection = require('../models/itemCollection');
|
||||||
var DeliveryDestination = require('../models/deliveryDestination');
|
var DeliveryDestination = require('../models/deliveryDestination');
|
||||||
var OrderConfirmation = require('../models/orderConfirmation');
|
var OrderConfirmation = require('../models/orderConfirmation');
|
||||||
|
var Place = require('../models/place');
|
||||||
|
|
||||||
var _ = require('underscore');
|
var _ = require('underscore');
|
||||||
|
|
||||||
@@ -18,6 +19,9 @@ var _itemsForDisplay = new ItemCollection();
|
|||||||
_itemsForDisplay.setFromCart(true);
|
_itemsForDisplay.setFromCart(true);
|
||||||
var _deliveryDestination = new DeliveryDestination();
|
var _deliveryDestination = new DeliveryDestination();
|
||||||
var _deliveryDestinationErrors = {};
|
var _deliveryDestinationErrors = {};
|
||||||
|
var _deliveryCosts = new Place({
|
||||||
|
postalCode: _deliveryDestination.get('place')
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
var loadCart = function() {
|
var loadCart = function() {
|
||||||
@@ -41,6 +45,7 @@ var loadCart = function() {
|
|||||||
_deliveryDestination.fetch({
|
_deliveryDestination.fetch({
|
||||||
success: function() {
|
success: function() {
|
||||||
validateDeliveryDestinationForm();
|
validateDeliveryDestinationForm();
|
||||||
|
fetchPlace();
|
||||||
CartActions.dataLoaded();
|
CartActions.dataLoaded();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -48,6 +53,19 @@ var loadCart = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var fetchPlace = function() {
|
||||||
|
_deliveryCosts = new Place({
|
||||||
|
postalCode: _deliveryDestination.get('place')
|
||||||
|
})
|
||||||
|
_deliveryCosts.fetch({
|
||||||
|
success: function() {
|
||||||
|
CartActions.dataLoaded();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var saveCartStateForItem = function(itemId) {
|
var saveCartStateForItem = function(itemId) {
|
||||||
var item = CartStore.getStateFor(itemId);
|
var item = CartStore.getStateFor(itemId);
|
||||||
item.save({
|
item.save({
|
||||||
@@ -84,15 +102,23 @@ var takeItemOut = function(itemId) {
|
|||||||
|
|
||||||
var changeDeliveryDestinationProperty = function(property, value) {
|
var changeDeliveryDestinationProperty = function(property, value) {
|
||||||
_deliveryDestination.set(property, value);
|
_deliveryDestination.set(property, value);
|
||||||
|
|
||||||
|
if (property === 'place') {
|
||||||
|
fetchPlace();
|
||||||
|
}
|
||||||
validateDeliveryDestinationForm();
|
validateDeliveryDestinationForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
var confirmOrder = function () {
|
var confirmOrder = function() {
|
||||||
console.log("confirming");
|
console.log("confirming");
|
||||||
var oc = new OrderConfirmation({ hamo: 'meho' });
|
var oc = new OrderConfirmation({
|
||||||
oc.save({b:'b'}, {
|
hamo: 'meho'
|
||||||
success: function () {
|
});
|
||||||
|
oc.save({
|
||||||
|
b: 'b'
|
||||||
|
}, {
|
||||||
|
success: function() {
|
||||||
console.log("done");
|
console.log("done");
|
||||||
NavigationActions.goToThankYou();
|
NavigationActions.goToThankYou();
|
||||||
}
|
}
|
||||||
@@ -102,7 +128,7 @@ var confirmOrder = function () {
|
|||||||
|
|
||||||
var saveDeliveryDestination = function() {
|
var saveDeliveryDestination = function() {
|
||||||
console.log("saving delivery destination");
|
console.log("saving delivery destination");
|
||||||
_deliveryDestination.save(null,{
|
_deliveryDestination.save(null, {
|
||||||
success: function() {
|
success: function() {
|
||||||
console.log("saved delivery destination");
|
console.log("saved delivery destination");
|
||||||
confirmOrder();
|
confirmOrder();
|
||||||
@@ -110,32 +136,32 @@ var saveDeliveryDestination = function() {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
var validateDeliveryDestinationForm = function () {
|
var validateDeliveryDestinationForm = function() {
|
||||||
_deliveryDestinationErrors = {};
|
_deliveryDestinationErrors = {};
|
||||||
|
|
||||||
var nameRegex = /.+\s+.+/i;
|
var nameRegex = /.+\s+.+/i;
|
||||||
if(_deliveryDestination.get('name').search(nameRegex) < 0) {
|
if (_deliveryDestination.get('name').search(nameRegex) < 0) {
|
||||||
_deliveryDestinationErrors['name'] = "I prezime i ime su obavezni";
|
_deliveryDestinationErrors['name'] = "I prezime i ime su obavezni";
|
||||||
}
|
}
|
||||||
|
|
||||||
var addressRegex = /.+\s+.+/i;
|
var addressRegex = /.+\s+.+/i;
|
||||||
if(_deliveryDestination.get('address').search(addressRegex) < 0) {
|
if (_deliveryDestination.get('address').search(addressRegex) < 0) {
|
||||||
_deliveryDestinationErrors['address'] = "Adresa mora biti ispravna";
|
_deliveryDestinationErrors['address'] = "Adresa mora biti ispravna";
|
||||||
}
|
}
|
||||||
|
|
||||||
var emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/i;
|
var emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/i;
|
||||||
if(_deliveryDestination.get('email').search(emailRegex) < 0) {
|
if (_deliveryDestination.get('email').search(emailRegex) < 0) {
|
||||||
_deliveryDestinationErrors['email'] = "Email mora biti ispravno upisan";
|
_deliveryDestinationErrors['email'] = "Email mora biti ispravno upisan";
|
||||||
}
|
}
|
||||||
|
|
||||||
var phoneRegex = /[\d\s-]{7,8}/i;
|
var phoneRegex = /[\d\s-]{7,8}/i;
|
||||||
if(_deliveryDestination.get('phone').search(phoneRegex) < 0) {
|
if (_deliveryDestination.get('phone').search(phoneRegex) < 0) {
|
||||||
_deliveryDestinationErrors['phone'] = "Telefon mora biti ispravan";
|
_deliveryDestinationErrors['phone'] = "Telefon mora biti ispravan";
|
||||||
}
|
}
|
||||||
|
|
||||||
var placeRegex = /^\s{0,1}\d{5}$/i;
|
var placeRegex = /^\s{0,1}\d{5}$/i;
|
||||||
if(_deliveryDestination.get('place').search(placeRegex) < 0) {
|
if (_deliveryDestination.get('place').search(placeRegex) < 0) {
|
||||||
_deliveryDestinationErrors['place'] = "Mjesto mora biti izabrano";
|
_deliveryDestinationErrors['place'] = "Mjesto mora biti izabrano";
|
||||||
}
|
}
|
||||||
|
|
||||||
var requiredFields = ["name", "email", "place", 'address', 'phone'];
|
var requiredFields = ["name", "email", "place", 'address', 'phone'];
|
||||||
@@ -143,17 +169,17 @@ var validateDeliveryDestinationForm = function () {
|
|||||||
var value = _deliveryDestination.get(requiredFields[i]);
|
var value = _deliveryDestination.get(requiredFields[i]);
|
||||||
if (value === undefined || value === null || value === "") {
|
if (value === undefined || value === null || value === "") {
|
||||||
// if it's required there will be a star there
|
// if it's required there will be a star there
|
||||||
_deliveryDestinationErrors[requiredFields[i]] = "*";
|
_deliveryDestinationErrors[requiredFields[i]] = "*";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var isDeliveryDestinationValid = function () {
|
var isDeliveryDestinationValid = function() {
|
||||||
return Object.getOwnPropertyNames(_deliveryDestinationErrors).length === 0;
|
return Object.getOwnPropertyNames(_deliveryDestinationErrors).length === 0;
|
||||||
}
|
}
|
||||||
// Extend CartStore with EventEmitter to add eventing capabilities
|
// Extend CartStore with EventEmitter to add eventing capabilities
|
||||||
var CartStore = _.extend({}, EventEmitter.prototype, {
|
var CartStore = _.extend({}, EventEmitter.prototype, {
|
||||||
|
|
||||||
getStateFor: function(itemId) {
|
getStateFor: function(itemId) {
|
||||||
@@ -184,8 +210,8 @@ var CartStore = _.extend({}, EventEmitter.prototype, {
|
|||||||
itemCounts: states,
|
itemCounts: states,
|
||||||
deliveryDestination: _deliveryDestination,
|
deliveryDestination: _deliveryDestination,
|
||||||
deliveryDestinationErrors: _deliveryDestinationErrors,
|
deliveryDestinationErrors: _deliveryDestinationErrors,
|
||||||
isDeliveryDestinationValid: isDeliveryDestinationValid()
|
isDeliveryDestinationValid: isDeliveryDestinationValid(),
|
||||||
|
deliveryCosts: _deliveryCosts
|
||||||
};
|
};
|
||||||
return state;
|
return state;
|
||||||
},
|
},
|
||||||
@@ -235,7 +261,9 @@ AppDispatcher.register(function(payload) {
|
|||||||
// do nothing - jsut emmit change
|
// do nothing - jsut emmit change
|
||||||
break;
|
break;
|
||||||
case CartConstants.SAVE_CART_STATE_FOR_ITEM:
|
case CartConstants.SAVE_CART_STATE_FOR_ITEM:
|
||||||
saveCartStateForItem(action.itemId);
|
if (isDeliveryDestinationValid()) {
|
||||||
|
saveCartStateForItem(action.itemId);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case CartConstants.CHANGE_DELIVERY_DESTINATION_PROPERTY:
|
case CartConstants.CHANGE_DELIVERY_DESTINATION_PROPERTY:
|
||||||
changeDeliveryDestinationProperty(action.propertyName, action.value)
|
changeDeliveryDestinationProperty(action.propertyName, action.value)
|
||||||
|
|||||||
Reference in New Issue
Block a user