diff --git a/back-office/app/controllers/places_controller.rb b/back-office/app/controllers/places_controller.rb new file mode 100644 index 0000000..91fa775 --- /dev/null +++ b/back-office/app/controllers/places_controller.rb @@ -0,0 +1,4 @@ +class PlacesController < ApplicationController + active_scaffold :"place" do |conf| + end +end diff --git a/back-office/app/helpers/places_helper.rb b/back-office/app/helpers/places_helper.rb new file mode 100644 index 0000000..558c7df --- /dev/null +++ b/back-office/app/helpers/places_helper.rb @@ -0,0 +1,2 @@ +module PlacesHelper +end \ No newline at end of file diff --git a/back-office/app/models/place.rb b/back-office/app/models/place.rb new file mode 100644 index 0000000..8d92248 --- /dev/null +++ b/back-office/app/models/place.rb @@ -0,0 +1,2 @@ +class Place < ActiveRecord::Base +end diff --git a/back-office/config/routes.rb b/back-office/config/routes.rb index e8d76e3..84a1a15 100644 --- a/back-office/config/routes.rb +++ b/back-office/config/routes.rb @@ -1,5 +1,6 @@ Rails.application.routes.draw do + resources :places do as_routes end resources :delivery_destinations do as_routes end mount RailsAdmin::Engine => '/admin', as: 'rails_admin' resources :filter_criteria_values do as_routes end diff --git a/back-office/test/controllers/places_controller_test.rb b/back-office/test/controllers/places_controller_test.rb new file mode 100644 index 0000000..8f872a2 --- /dev/null +++ b/back-office/test/controllers/places_controller_test.rb @@ -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 diff --git a/back-office/test/fixtures/places.yml b/back-office/test/fixtures/places.yml new file mode 100644 index 0000000..7879e27 --- /dev/null +++ b/back-office/test/fixtures/places.yml @@ -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 diff --git a/back-office/test/models/place_test.rb b/back-office/test/models/place_test.rb new file mode 100644 index 0000000..b086a70 --- /dev/null +++ b/back-office/test/models/place_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class PlaceTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/front-api/controllers/place.rb b/front-api/controllers/place.rb new file mode 100644 index 0000000..f7ebc6b --- /dev/null +++ b/front-api/controllers/place.rb @@ -0,0 +1,5 @@ + + +get '/place/:postal_code' do + Place.by_code_or_default(params["postal_code"]).to_json +end \ No newline at end of file diff --git a/front-api/db/migrate/20150312073820_create_places.rb b/front-api/db/migrate/20150312073820_create_places.rb new file mode 100644 index 0000000..575f66d --- /dev/null +++ b/front-api/db/migrate/20150312073820_create_places.rb @@ -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 diff --git a/front-api/db/schema.rb b/front-api/db/schema.rb index 15568c9..32f4764 100644 --- a/front-api/db/schema.rb +++ b/front-api/db/schema.rb @@ -11,7 +11,7 @@ # # 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 enable_extension "plpgsql" @@ -100,6 +100,14 @@ ActiveRecord::Schema.define(version: 20150222055517) do t.datetime "updated_at", null: false 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| t.string "name" end diff --git a/front-api/models/place.rb b/front-api/models/place.rb new file mode 100644 index 0000000..1acd08f --- /dev/null +++ b/front-api/models/place.rb @@ -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 diff --git a/front-ui/app/components/cart/cartPage.js b/front-ui/app/components/cart/cartPage.js index 6750f39..c15507c 100644 --- a/front-ui/app/components/cart/cartPage.js +++ b/front-ui/app/components/cart/cartPage.js @@ -38,7 +38,7 @@ var CartPage = React.createClass({ console.log("bla :" , this.state.items.length); var cartTotal = (
- +
diff --git a/front-ui/app/components/cart/cartTotal.js b/front-ui/app/components/cart/cartTotal.js index 5416961..4a8cee0 100644 --- a/front-ui/app/components/cart/cartTotal.js +++ b/front-ui/app/components/cart/cartTotal.js @@ -5,6 +5,10 @@ var React = require('react'), var Router = require('react-router'); + + + + var CartTotal = React.createClass({ render: function() { @@ -20,9 +24,20 @@ var CartTotal = React.createClass({ }); + var deliveryCosts = this.props.deliveryCosts.get('delivery_price'); + return (
- Ukupno: {Globals.FormatCurrency(total)} + Dostava +
+ Roba: {Globals.FormatCurrency(total)} +
+
+ Dostava: {Globals.FormatCurrency(deliveryCosts)} +
+
+ Ukupno: {Globals.FormatCurrency(total + (+deliveryCosts))} +
+
); diff --git a/front-ui/app/components/cart/checkoutPage.js b/front-ui/app/components/cart/checkoutPage.js index 529e766..317b4ac 100644 --- a/front-ui/app/components/cart/checkoutPage.js +++ b/front-ui/app/components/cart/checkoutPage.js @@ -599,10 +599,10 @@ var CheckoutPage = React.createClass({
- +
- +
); diff --git a/front-ui/app/components/rootApp.js b/front-ui/app/components/rootApp.js index 46a477b..6552d7c 100644 --- a/front-ui/app/components/rootApp.js +++ b/front-ui/app/components/rootApp.js @@ -16,7 +16,8 @@ var RootApp = React.createClass({

- ribica.ba + +

diff --git a/front-ui/app/models/place.js b/front-ui/app/models/place.js new file mode 100644 index 0000000..9fa24ab --- /dev/null +++ b/front-ui/app/models/place.js @@ -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; diff --git a/front-ui/app/stores/cartStore.js b/front-ui/app/stores/cartStore.js index afe5e7f..ea8b91e 100644 --- a/front-ui/app/stores/cartStore.js +++ b/front-ui/app/stores/cartStore.js @@ -8,6 +8,7 @@ var ItemInCartCollection = require('../models/itemInCartCollection'); var ItemCollection = require('../models/itemCollection'); var DeliveryDestination = require('../models/deliveryDestination'); var OrderConfirmation = require('../models/orderConfirmation'); +var Place = require('../models/place'); var _ = require('underscore'); @@ -18,6 +19,9 @@ var _itemsForDisplay = new ItemCollection(); _itemsForDisplay.setFromCart(true); var _deliveryDestination = new DeliveryDestination(); var _deliveryDestinationErrors = {}; +var _deliveryCosts = new Place({ + postalCode: _deliveryDestination.get('place') +}) var loadCart = function() { @@ -41,6 +45,7 @@ var loadCart = function() { _deliveryDestination.fetch({ success: function() { validateDeliveryDestinationForm(); + fetchPlace(); 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 item = CartStore.getStateFor(itemId); item.save({ @@ -84,15 +102,23 @@ var takeItemOut = function(itemId) { var changeDeliveryDestinationProperty = function(property, value) { _deliveryDestination.set(property, value); + + if (property === 'place') { + fetchPlace(); + } validateDeliveryDestinationForm(); }; -var confirmOrder = function () { +var confirmOrder = function() { console.log("confirming"); - var oc = new OrderConfirmation({ hamo: 'meho' }); - oc.save({b:'b'}, { - success: function () { + var oc = new OrderConfirmation({ + hamo: 'meho' + }); + oc.save({ + b: 'b' + }, { + success: function() { console.log("done"); NavigationActions.goToThankYou(); } @@ -102,7 +128,7 @@ var confirmOrder = function () { var saveDeliveryDestination = function() { console.log("saving delivery destination"); - _deliveryDestination.save(null,{ + _deliveryDestination.save(null, { success: function() { console.log("saved delivery destination"); confirmOrder(); @@ -110,32 +136,32 @@ var saveDeliveryDestination = function() { }) }; -var validateDeliveryDestinationForm = function () { +var validateDeliveryDestinationForm = function() { _deliveryDestinationErrors = {}; - + var nameRegex = /.+\s+.+/i; - if(_deliveryDestination.get('name').search(nameRegex) < 0) { - _deliveryDestinationErrors['name'] = "I prezime i ime su obavezni"; + if (_deliveryDestination.get('name').search(nameRegex) < 0) { + _deliveryDestinationErrors['name'] = "I prezime i ime su obavezni"; } var addressRegex = /.+\s+.+/i; - if(_deliveryDestination.get('address').search(addressRegex) < 0) { - _deliveryDestinationErrors['address'] = "Adresa mora biti ispravna"; + if (_deliveryDestination.get('address').search(addressRegex) < 0) { + _deliveryDestinationErrors['address'] = "Adresa mora biti ispravna"; } var emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/i; - if(_deliveryDestination.get('email').search(emailRegex) < 0) { - _deliveryDestinationErrors['email'] = "Email mora biti ispravno upisan"; + if (_deliveryDestination.get('email').search(emailRegex) < 0) { + _deliveryDestinationErrors['email'] = "Email mora biti ispravno upisan"; } var phoneRegex = /[\d\s-]{7,8}/i; - if(_deliveryDestination.get('phone').search(phoneRegex) < 0) { - _deliveryDestinationErrors['phone'] = "Telefon mora biti ispravan"; + if (_deliveryDestination.get('phone').search(phoneRegex) < 0) { + _deliveryDestinationErrors['phone'] = "Telefon mora biti ispravan"; } var placeRegex = /^\s{0,1}\d{5}$/i; - if(_deliveryDestination.get('place').search(placeRegex) < 0) { - _deliveryDestinationErrors['place'] = "Mjesto mora biti izabrano"; + if (_deliveryDestination.get('place').search(placeRegex) < 0) { + _deliveryDestinationErrors['place'] = "Mjesto mora biti izabrano"; } var requiredFields = ["name", "email", "place", 'address', 'phone']; @@ -143,17 +169,17 @@ var validateDeliveryDestinationForm = function () { var value = _deliveryDestination.get(requiredFields[i]); if (value === undefined || value === null || value === "") { // if it's required there will be a star there - _deliveryDestinationErrors[requiredFields[i]] = "*"; + _deliveryDestinationErrors[requiredFields[i]] = "*"; } } } -var isDeliveryDestinationValid = function () { - return Object.getOwnPropertyNames(_deliveryDestinationErrors).length === 0; -} -// Extend CartStore with EventEmitter to add eventing capabilities +var isDeliveryDestinationValid = function() { + return Object.getOwnPropertyNames(_deliveryDestinationErrors).length === 0; + } + // Extend CartStore with EventEmitter to add eventing capabilities var CartStore = _.extend({}, EventEmitter.prototype, { getStateFor: function(itemId) { @@ -184,8 +210,8 @@ var CartStore = _.extend({}, EventEmitter.prototype, { itemCounts: states, deliveryDestination: _deliveryDestination, deliveryDestinationErrors: _deliveryDestinationErrors, - isDeliveryDestinationValid: isDeliveryDestinationValid() - + isDeliveryDestinationValid: isDeliveryDestinationValid(), + deliveryCosts: _deliveryCosts }; return state; }, @@ -235,7 +261,9 @@ AppDispatcher.register(function(payload) { // do nothing - jsut emmit change break; case CartConstants.SAVE_CART_STATE_FOR_ITEM: - saveCartStateForItem(action.itemId); + if (isDeliveryDestinationValid()) { + saveCartStateForItem(action.itemId); + } break; case CartConstants.CHANGE_DELIVERY_DESTINATION_PROPERTY: changeDeliveryDestinationProperty(action.propertyName, action.value)