diff --git a/app/controllers/items.js b/app/controllers/items.js
index 32f80f8..3f47c37 100644
--- a/app/controllers/items.js
+++ b/app/controllers/items.js
@@ -9,3 +9,15 @@ exports.create = function(req, res){
});
};
+exports.index = function(req, res){
+ Item.findForSale(req.query, function(err, items){
+ res.render('items/index', {items:items});
+ });
+};
+
+exports.markonsale = function(req, res){
+ Item.markOnSale(req.params.itemId, function(){
+ res.redirect('/profile');
+ });
+};
+
diff --git a/app/models/item.js b/app/models/item.js
index cbabc44..22a2251 100644
--- a/app/models/item.js
+++ b/app/models/item.js
@@ -38,6 +38,18 @@ Item.create = function(data, cb){
Item.collection.save(i, cb);
};
+Item.findForSale = function(query, cb){
+ var filter = {onSale:true},
+ sort = {};
+ if(query.sort){sort[query.sort] = query.order * 1;}
+ Item.collection.find(filter).sort(sort).toArray(cb);
+};
+
+Item.markOnSale = function(itemId, cb){
+ var _id = Mongo.ObjectID(itemId);
+ Item.collection.update({_id:_id}, {$set: {onSale: true}}, cb);
+};
+
module.exports = Item;
function getNumberOfBids(item, cb){
diff --git a/app/routes/routes.js b/app/routes/routes.js
index c2f426c..222bf62 100644
--- a/app/routes/routes.js
+++ b/app/routes/routes.js
@@ -35,6 +35,8 @@ module.exports = function(app, express){
app.put('/profile', users.update);
app.get('/profile', users.profile);
app.post('/items', items.create);
+ app.get('/marketplace', items.index);
+ app.put('/items/:itemId', items.markonsale);
console.log('Express: Routes Loaded');
};
diff --git a/app/static/css/style.css b/app/static/css/style.css
index 22faa3b..193578f 100644
--- a/app/static/css/style.css
+++ b/app/static/css/style.css
@@ -1 +1 @@
-.profile-photo{height:200px;width:150px;background-size:cover}.item-photo{height:100px;width:100px;background-size:cover}
\ No newline at end of file
+.profile-photo{height:200px;width:150px;background-size:cover}.item-photo{height:100px;width:100px;background-size:cover}#marketplace-map{height:400px}
\ No newline at end of file
diff --git a/app/static/css/style.less b/app/static/css/style.less
index 8711eb1..52f9fbc 100644
--- a/app/static/css/style.less
+++ b/app/static/css/style.less
@@ -13,3 +13,7 @@ body{
width: 100px;
background-size: cover;
}
+
+#marketplace-map {
+ height: 400px;
+}
diff --git a/app/static/img/marker.png b/app/static/img/marker.png
new file mode 100644
index 0000000..5876a56
Binary files /dev/null and b/app/static/img/marker.png differ
diff --git a/app/static/js/user/items-index.js b/app/static/js/user/items-index.js
new file mode 100644
index 0000000..e9e6ac7
--- /dev/null
+++ b/app/static/js/user/items-index.js
@@ -0,0 +1,22 @@
+/* jshint unused:false, camelcase:false */
+/* global google, cartographer, addMarker */
+
+(function(){
+ 'use strict';
+
+ var map;
+
+ $(document).ready(function(){
+ map = cartographer('marketplace-map', 39.8282, -98.5795, 4);
+ var rows = $('.item').toArray();
+ // console.log(rows);
+ if(rows.length){
+ rows.forEach(function(row){
+ // console.log(row);
+ addMarker(map, parseFloat($(row).attr('data-lat')), parseFloat($(row).attr('data-lng')), $(row).attr('data-loc'), '/img/marker.png');
+ });
+ }
+ });
+
+})();
+
diff --git a/app/static/js/user/main.js b/app/static/js/user/main.js
index dd6924f..204628b 100644
--- a/app/static/js/user/main.js
+++ b/app/static/js/user/main.js
@@ -1,8 +1,29 @@
-(function(){
- 'use strict';
+/* jshint unused:false, camelcase:false */
+/* global google */
- $(document).ready(function(){
+function geocode(address, cb){
+ 'use strict';
+ var geocoder = new google.maps.Geocoder();
+ geocoder.geocode({address:address}, function(results, status){
+ //console.log('results', results);
+ var name = results[0].formatted_address,
+ lat = results[0].geometry.location.lat(),
+ lng = results[0].geometry.location.lng();
+ cb(name, lat, lng);
});
+}
-})();
+function cartographer(cssId, lat, lng, zoom){
+ 'use strict';
+ var mapOptions = {center: new google.maps.LatLng(lat, lng), zoom: zoom, mapTypeId: google.maps.MapTypeId.ROADMAP},
+ map = new google.maps.Map(document.getElementById(cssId), mapOptions);
+ return map;
+}
+
+function addMarker(map, lat, lng, name, icon){
+ 'use strict';
+ var latLng = new google.maps.LatLng(lat, lng);
+ // console.log(latLng);
+ new google.maps.Marker({map: map, position: latLng, title: name, animation: google.maps.Animation.DROP, icon: icon});
+}
diff --git a/app/static/js/user/profile.js b/app/static/js/user/profile.js
index 3d9cc2f..8e8b551 100644
--- a/app/static/js/user/profile.js
+++ b/app/static/js/user/profile.js
@@ -1,34 +1,23 @@
/* jshint unused:false, camelcase:false */
-/* global google */
+/* global google, geocode */
(function(){
'use strict';
$(document).ready(function(){
- $('button[type=submit]').click(geocodeAndSubmit);
+ $('#submitItem').click(geocodeAndSubmit);
});
function geocodeAndSubmit(e){
var location = $('input[name=location]').val();
geocode(location, function(name, lat, lng){
$('input[name=location]').val(name);
- $('form').append('');
- $('form').append('');
- $('form').submit();
+ $('#newItem').append('');
+ $('#newItem').append('');
+ $('#newItem').submit();
});
e.preventDefault();
}
- function geocode(address, cb){
- var geocoder = new google.maps.Geocoder();
- geocoder.geocode({address:address}, function(results, status){
- //console.log('results', results);
- var name = results[0].formatted_address,
- lat = results[0].geometry.location.lat(),
- lng = results[0].geometry.location.lng();
- cb(name, lat, lng);
- });
- }
-
})();
diff --git a/app/views/items/index.jade b/app/views/items/index.jade
new file mode 100644
index 0000000..3a760c9
--- /dev/null
+++ b/app/views/items/index.jade
@@ -0,0 +1,28 @@
+extends ../shared/template
+block content
+ h2 Profile
+ .panel.panel-default
+ .panel-body
+ .row
+ .col-xs-12
+ #marketplace-map
+ .row
+ .col-xs-12
+ table.table
+ thead
+ tr
+ th Photo
+ th Name
+ th Location
+ th Description
+ tbody
+ each item in items
+ tr.item(data-loc='#{item.location}', data-lat='#{item.lat}', data-lng='#{item.lng}')
+ td.item-photo(style='background-image:url(#{item.photo})')
+ td: a(href='/items/#{item._id}')= item.name
+ td= item.location
+ td= item.description
+
+block scripts
+ script(src='/js/user/items-index.js')
+
diff --git a/app/views/users/profile.jade b/app/views/users/profile.jade
index 5a698c5..0f0ffd3 100644
--- a/app/views/users/profile.jade
+++ b/app/views/users/profile.jade
@@ -23,7 +23,7 @@ block content
if user.photo
.profile-photo(style='background-image:url(#{user.photo});')
.col-xs-4
- form(role='form', method='post', action='/items')
+ form#newItem(role='form', method='post', action='/items')
.form-group
label Name
input.form-control(name='name', type='text', placeholder='Red Wine')
@@ -39,7 +39,7 @@ block content
.form-group
label Description
textarea.form-control(name='description')
- button.btn.btn-default(type='submit') Add Vintage
+ button#submitItem.btn.btn-default(type='submit') Add Vintage
.row
.col-xs-6
table.table
@@ -48,15 +48,22 @@ block content
th Photo
th Name
th Out for Trade?
- th Mark For Sale
+ th Want to Trade?
tbody
each item in items
unless item.onSale
tr
td.item-photo(style='background-image:url(#{item.photo});')
td= item.name
- td= item.isBiddable
- td Checkbox + submit button
+ if(!item.isBiddable)
+ td No
+ if(item.isBiddable)
+ td Yes
+ td
+ form(method='post', action='/items/#{item._id}')
+ input(type='hidden', name='_method', value='put')
+ button(type='submit') Trade
+
.col-xs-1
.col-xs-5
table.table
diff --git a/test/acceptance/items.js b/test/acceptance/items.js
new file mode 100644
index 0000000..463d960
--- /dev/null
+++ b/test/acceptance/items.js
@@ -0,0 +1,73 @@
+/* global describe, before, beforeEach, it */
+
+'use strict';
+
+process.env.DB = 'wine-seller-test';
+
+var expect = require('chai').expect,
+ cp = require('child_process'),
+ app = require('../../app/index'),
+ cookie = null,
+ request = require('supertest');
+
+describe('users', function(){
+ before(function(done){
+ request(app).get('/').end(done);
+ });
+
+ beforeEach(function(done){
+ cp.execFile(__dirname + '/../scripts/clean-db.sh', [process.env.DB], {cwd:__dirname + '/../scripts'}, function(err, stdout, stderr){
+ request(app)
+ .post('/login')
+ .send('email=nodeapptest%2Bbob%40gmail.com&password=1234')
+ .end(function(err, res){
+ cookie = res.headers['set-cookie'][0];
+ done();
+ });
+ });
+ });
+
+ describe('post /items', function(){
+ it('should redirect to the profile page', function(done){
+ request(app)
+ .post('/items')
+ .set('cookie', cookie)
+ .send('name=Red+Wine&photo=http%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fc%2Fcc%2FFrench_beaujolais_red_wine_bottle.jpg&tags=red%2C+wine&location=Nashville%2C+TN%2C+USA&description=Red+Wine')
+ .end(function(err, res){
+ expect(res.status).to.equal(302);
+ expect(res.headers.location).to.equal('/profile');
+ done();
+ });
+ });
+ });
+
+ describe('get /marketplace', function(){
+ it('should display the marketplace page', function(done){
+ request(app)
+ .get('/marketplace')
+ .set('cookie', cookie)
+ .end(function(err, res){
+ expect(res.status).to.equal(200);
+ expect(res.text).to.include('Marketplace');
+ expect(res.text).to.include('White Wine');
+ done();
+ });
+ });
+ });
+
+ describe('put /profile', function(){
+ it('should redirect to the profile page after item is put on sale', function(done){
+ request(app)
+ .post('/items/a00000000000000000000001')
+ .send('_method=put')
+ .set('cookie', cookie)
+ .end(function(err, res){
+ expect(res.status).to.equal(302);
+ expect(res.headers.location).to.equal('/profile');
+ done();
+ });
+ });
+ });
+
+});
+
diff --git a/test/acceptance/npm-debug.log b/test/acceptance/npm-debug.log
deleted file mode 100644
index 4c4d405..0000000
--- a/test/acceptance/npm-debug.log
+++ /dev/null
@@ -1,17 +0,0 @@
-0 info it worked if it ends with ok
-1 verbose cli [ '/usr/local/node/bin/node', '/usr/local/node/bin/npm', 'test' ]
-2 info using npm@1.4.14
-3 info using node@v0.10.29
-4 error Error: ENOENT, open '/home/nss/apps/code/wine-seller/test/acceptance/package.json'
-5 error If you need help, you may report this *entire* log,
-5 error including the npm and node versions, at:
-5 error
-6 error System Linux 3.13.0-24-generic
-7 error command "/usr/local/node/bin/node" "/usr/local/node/bin/npm" "test"
-8 error cwd /home/nss/apps/code/wine-seller/test/acceptance
-9 error node -v v0.10.29
-10 error npm -v 1.4.14
-11 error path /home/nss/apps/code/wine-seller/test/acceptance/package.json
-12 error code ENOENT
-13 error errno 34
-14 verbose exit [ 34, true ]
diff --git a/test/acceptance/users.js b/test/acceptance/users.js
index 19225fb..19f3ab7 100644
--- a/test/acceptance/users.js
+++ b/test/acceptance/users.js
@@ -2,7 +2,7 @@
'use strict';
-process.env.DB = 'template-test';
+process.env.DB = 'wine-seller-test';
var expect = require('chai').expect,
cp = require('child_process'),
diff --git a/test/unit/item.js b/test/unit/item.js
index bb6197f..0112e31 100644
--- a/test/unit/item.js
+++ b/test/unit/item.js
@@ -62,5 +62,26 @@ describe('Item', function(){
});
});
+ describe('.findForSale', function(){
+ it('should find items in database that are for sale based on query parameters', function(done){
+ Item.findForSale({}, function(err, items){
+ expect(items).to.have.length(1);
+ expect(items[0].name).to.equal('White Wine');
+ done();
+ });
+ });
+ });
+
+ describe('.markForSale', function(){
+ it('should set an item onSale to true', function(done){
+ Item.markOnSale('a00000000000000000000001', function(){
+ Item.findById('a00000000000000000000001', function(err, item){
+ expect(item.onSale).to.be.true;
+ done();
+ });
+ });
+ });
+ });
+
});