diff --git a/Gemfile b/Gemfile
index 3f7b5d986..00fbaa298 100755
--- a/Gemfile
+++ b/Gemfile
@@ -38,7 +38,6 @@ group :development, :test do
gem 'pry'
gem 'factory_girl_rails'
gem 'faker'
- gem 'jasmine-rails'
gem 'smarf_doc', github: 'RickCarlino/smarf_doc'
end
diff --git a/Gemfile.lock b/Gemfile.lock
index 3bcc56918..9e5a9e835 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -153,12 +153,6 @@ GEM
i18n (0.7.0)
ice_cube (0.12.1)
ice_nine (0.11.1)
- jasmine-core (2.2.0)
- jasmine-rails (0.10.7)
- jasmine-core (>= 1.3, < 3.0)
- phantomjs (< 2.0)
- railties (>= 3.1.0)
- sprockets-rails
json (1.8.2)
json_pure (1.8.2)
launchy (2.4.3)
@@ -379,7 +373,6 @@ DEPENDENCIES
haml
high_voltage (~> 2.1.0)
ice_cube
- jasmine-rails
launchy
metric_fu
mongoid (~> 4.0.0)!
@@ -405,3 +398,6 @@ DEPENDENCIES
simplecov
smarf_doc!
uglifier
+
+BUNDLED WITH
+ 1.10.6
diff --git a/app/assets/javascripts/application.js.jsx b/app/assets/javascripts/application.js.jsx
index f6382f620..733fc6a62 100644
--- a/app/assets/javascripts/application.js.jsx
+++ b/app/assets/javascripts/application.js.jsx
@@ -2,3 +2,13 @@
//= require jquery
//= require react
//= require react_ujs
+$(function(){
+ // Append Rails CSRF token to requests.
+ var token = $( 'meta[name="csrf-token"]' ).attr( 'content' );
+
+ $.ajaxSetup( {
+ beforeSend: function ( xhr ) {
+ xhr.setRequestHeader( 'X-CSRF-Token', token );
+ }
+ });
+});
diff --git a/app/controllers/api/crops_controller.rb b/app/controllers/api/crops_controller.rb
new file mode 100644
index 000000000..e8bf4953e
--- /dev/null
+++ b/app/controllers/api/crops_controller.rb
@@ -0,0 +1,26 @@
+module Api
+ class CropsController < Api::AbstractController
+
+ def index
+ render json: Crop.where(device: current_device)
+ end
+
+ def create
+ mutate Crops::Create.run(params, device: current_device)
+ end
+
+ def destroy
+ if (crop.device == current_device) && crop.destroy
+ render nothing: true
+ else
+ raise Errors::Forbidden, "Not your Crop object."
+ end
+ end
+
+ private
+
+ def crop
+ @crop ||= Crop.find(params[:id])
+ end
+ end
+end
diff --git a/app/models/crop.rb b/app/models/crop.rb
new file mode 100644
index 000000000..4dae153e0
--- /dev/null
+++ b/app/models/crop.rb
@@ -0,0 +1,9 @@
+#
+class Crop
+ include Mongoid::Document
+
+ belongs_to :device
+
+ field :x, type: Integer
+ field :y, type: Integer
+end
diff --git a/app/models/device.rb b/app/models/device.rb
index 464634da0..44d08852e 100644
--- a/app/models/device.rb
+++ b/app/models/device.rb
@@ -6,6 +6,7 @@ class Device
has_many :users
has_many :schedules, dependent: :destroy
has_many :sequences
+ has_many :crops, dependent: :destroy
# The SkyNet UUID of the device
diff --git a/app/mutations/crops/create.rb b/app/mutations/crops/create.rb
new file mode 100644
index 000000000..f25e14380
--- /dev/null
+++ b/app/mutations/crops/create.rb
@@ -0,0 +1,13 @@
+module Crops
+ class Create < Mutations::Command
+ required do
+ model :device, class: Device
+ integer :x
+ integer :y
+ end
+
+ def execute
+ Crop.create!(inputs)
+ end
+ end
+end
diff --git a/app/views/pages/farm_designer.html.erb b/app/views/pages/farm_designer.html.erb
index c60aed28d..028b3b6c5 100644
--- a/app/views/pages/farm_designer.html.erb
+++ b/app/views/pages/farm_designer.html.erb
@@ -1,4 +1,18 @@
diff --git a/javascripts/menus/garden_map.js b/javascripts/menus/garden_map.js
index fc98aef4a..b5f68e436 100644
--- a/javascripts/menus/garden_map.js
+++ b/javascripts/menus/garden_map.js
@@ -1,5 +1,24 @@
-export class GardenMap extends React.Component {
+export class MapPointView extends React.Component {
render() {
- return
Hello, GardenMap
;
+ var style = {
+ position: 'absolute',
+ left: (this.props.point.x - 20),
+ top: (this.props.point.y - 40)
+ };
+ return
;
+ }
+};
+
+export class GardenMap extends React.Component {
+ points() {
+ return this.props.crops.map((p, k) =>
);
+ }
+
+ render() {
+ return
+
+ { this.points() }
+
+
;
}
}
diff --git a/javascripts/menus/plant_catalog.js b/javascripts/menus/plant_catalog.js
index 975a97d9f..800db46f4 100644
--- a/javascripts/menus/plant_catalog.js
+++ b/javascripts/menus/plant_catalog.js
@@ -4,7 +4,7 @@ export class PlantCatalogTile extends React.Component {
showCropInfo(){
this.props.dispatch({
type: 'CROP_INFO_SHOW',
- crop: this.props.crop
+ payload: this.props.crop
});
};
diff --git a/javascripts/redux/actions.js b/javascripts/redux/actions.js
index 98b2a2d56..50531fb45 100644
--- a/javascripts/redux/actions.js
+++ b/javascripts/redux/actions.js
@@ -1,3 +1,7 @@
+//actually, these are 'action creators'.
+import { store } from './store';
+import { addons } from 'react/addons';
+
let actions = {
'@@redux/INIT': empty,
DEFAULT: function (s, a) {
@@ -5,12 +9,28 @@ let actions = {
console.trace();
return s;
},
+ CROP_ADD_REQUEST: function (s, a) {
+ var req = $.ajax({method: "POST", url: "/api/crops", data: a.payload})
+ .done(function (crop) {
+ store.dispatch({type: "CROP_ADD_SUCCESS", payload: crop});
+ })
+ .fail(function (a, b, c) { store.dispatch({type: "CROP_ADD_FAILURE"}) });
+ return s;
+ },
+ CROP_ADD_FAILURE: function (s = store.getState(), a) {
+ alert("Failed to add crop, and also failed to write an error handler :(");
+ return s;
+ },
+ CROP_ADD_SUCCESS: function (s = store.getState(), a) {
+ var new_array = s.middleMenu.crops.concat(a.payload);
+ return update(s, {middleMenu: {crops: new_array}});
+ },
CROP_INFO_SHOW: function(s, a) {
// TODO: add type system to check for presence of `crop` Object?
let fragment = {
leftMenu: {
component: 'CropInfo',
- crop: a.crop
+ crop: a.payload
}
};
return update(s, fragment);
@@ -22,8 +42,8 @@ let actions = {
return changeLeftComponent(s, 'CropInventory');
},
INVENTORY_SHOW_TAB: function(s, a) {
- return update(s, {leftMenu: {tab: a.tab}});
- },
+ return update(s, {leftMenu: {tab: a.payload}});
+ }
}
function empty(s, a) {
diff --git a/javascripts/redux/initial_state.js b/javascripts/redux/initial_state.js
deleted file mode 100644
index 7b9bd42d5..000000000
--- a/javascripts/redux/initial_state.js
+++ /dev/null
@@ -1,14 +0,0 @@
-var initialState = {
- leftMenu: {
- component: 'CropInventory',
- tab: 'Plants'
- },
- middleMenu: {
- mapPoints: []
- },
- rightMenu: {
-
- }
-};
-
-export { initialState };
diff --git a/javascripts/redux/reducer.js b/javascripts/redux/reducer.js
index 0a1d37040..280eb150b 100644
--- a/javascripts/redux/reducer.js
+++ b/javascripts/redux/reducer.js
@@ -1,6 +1,10 @@
import { actions } from './actions';
+import { isFSA } from 'flux-standard-action';
export function reducer(state, action) {
- console.log(action.type)
- return (actions[action.type] || actions.DEFAULT)(state, action);
+ if (isFSA(action)){
+ return (actions[action.type] || actions.DEFAULT)(state, action);
+ } else {
+ console.error("Action does not conform to 'flux-standard-action", action);
+ };
};
diff --git a/javascripts/redux/store.js b/javascripts/redux/store.js
index ca2e51a3e..1b6618019 100644
--- a/javascripts/redux/store.js
+++ b/javascripts/redux/store.js
@@ -1,7 +1,7 @@
import { createStore } from 'redux';
-import { initialState } from './initial_state';
import { reducer } from './reducer';
-var store = createStore(reducer, initialState);
+// var store = createStore(reducer, initialState);
+var store = createStore(reducer, window.initialState);
export { store };
diff --git a/package.json b/package.json
index 7ab7d981e..d05c81f8c 100644
--- a/package.json
+++ b/package.json
@@ -17,22 +17,25 @@
},
"homepage": "https://github.com/rickcarlino/farmbot-web-app",
"browserify": {
- "transform": ["babelify"]
+ "transform": [
+ "babelify"
+ ]
},
"dependencies": {
"angular": "^1.3.19",
"angular-ui-sortable": "^0.13.4",
+ "babelify": "^6.3.0",
"browserify": "^11.1.0",
"browserify-incremental": "^3.0.1",
+ "flux-standard-action": "^0.6.0",
+ "gulp": "^3.9.0",
+ "gulp-concat": "^2.6.0",
+ "gulp-util": "^3.0.6",
"react": "^0.13.3",
"react-redux": "^2.1.2",
"reactify": "^1.1.1",
"redux": "^3.0.0",
- "gulp": "^3.9.0",
- "babelify": "^6.3.0",
- "browserify": "^11.1.0",
- "gulp-concat": "^2.6.0",
- "gulp-util": "^3.0.6",
+ "redux-router": "^1.0.0-beta3",
"vinyl-source-stream": "^1.1.0"
}
}