Add full ability to add/delete attendees
This commit is contained in:
		
							
								
								
									
										63
									
								
								attendees.go
									
									
									
									
									
								
							
							
						
						
									
										63
									
								
								attendees.go
									
									
									
									
									
								
							| @@ -2,6 +2,7 @@ package main | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"log" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| @@ -71,7 +72,7 @@ func GetAttendees(userid int64, date time.Time) (*[]*Attendee, error) { | ||||
|  | ||||
| func GetPopularAttendees() (*[]*PopularAttendee, error) { | ||||
| 	var attendees []*Attendee | ||||
| 	var attendeeMap map[string]int64 | ||||
| 	attendeeMap := make(map[string]int64) | ||||
| 	popularAttendees := make([]*PopularAttendee, 0) | ||||
|  | ||||
| 	_, err := DB.Select(&attendees, "SELECT * from attendees") | ||||
| @@ -98,7 +99,7 @@ func InsertAttendee(a *Attendee) error { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	existing, err := transaction.SelectInt("SELECT count(*) from users where Name=?", a.Name) | ||||
| 	existing, err := transaction.SelectInt("SELECT count(*) from attendees where UserId=? AND Name=? AND Date=?", a.UserId, a.Name, a.Date) | ||||
| 	if err != nil { | ||||
| 		transaction.Rollback() | ||||
| 		return err | ||||
| @@ -123,6 +124,41 @@ func InsertAttendee(a *Attendee) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func GetAttendee(attendeeid int64, userid int64, date time.Time) (*Attendee, error) { | ||||
| 	var a Attendee | ||||
|  | ||||
| 	err := DB.SelectOne(&a, "SELECT * from attendees where UserId=? AND AttendeeId=? AND Date=?", userid, attendeeid, date) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &a, nil | ||||
| } | ||||
|  | ||||
| func DeleteAttendee(a *Attendee) error { | ||||
| 	transaction, err := DB.Begin() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	count, err := transaction.Delete(a) | ||||
| 	if err != nil { | ||||
| 		transaction.Rollback() | ||||
| 		return err | ||||
| 	} | ||||
| 	if count != 1 { | ||||
| 		transaction.Rollback() | ||||
| 		return errors.New("Was going to delete more than one attendee") | ||||
| 	} | ||||
|  | ||||
| 	err = transaction.Commit() | ||||
| 	if err != nil { | ||||
| 		transaction.Rollback() | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func AttendeeHandler(w http.ResponseWriter, r *http.Request) { | ||||
| 	user, err := GetUserFromSession(r) | ||||
| 	if err != nil { | ||||
| @@ -184,8 +220,29 @@ func AttendeeHandler(w http.ResponseWriter, r *http.Request) { | ||||
| 			log.Print(err) | ||||
| 			return | ||||
| 		} | ||||
| 	} else if r.Method == "DELETE" { | ||||
| 		attendeeid, err := GetURLID(r.URL.Path) | ||||
| 		if err != nil { | ||||
| 			WriteError(w, 3 /* Invalid Request */) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		attendee, err := GetAttendee(attendeeid, user.UserId, today) | ||||
| 		if err != nil { | ||||
| 			WriteError(w, 3 /*Invalid Request*/) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		err = DeleteAttendee(attendee) | ||||
| 		if err != nil { | ||||
| 			WriteError(w, 999 /*Internal Error*/) | ||||
| 			log.Print(err) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		WriteSuccess(w) | ||||
| 	} else { | ||||
| 		/* No PUT or DELETE */ | ||||
| 		/* No PUT */ | ||||
| 		WriteError(w, 3 /*Invalid Request*/) | ||||
| 		return | ||||
| 	} | ||||
|   | ||||
| @@ -2,8 +2,8 @@ var AttendeeConstants = require('../constants/AttendeeConstants'); | ||||
|  | ||||
| var ErrorActions = require('./ErrorActions'); | ||||
|  | ||||
| var models = require('../models.js'); | ||||
| var Attendee = models.Attendee; | ||||
| var PopularAttendee = models.PopularAttendee; | ||||
| var Error = models.Error; | ||||
|  | ||||
| function fetchAttendees() { | ||||
| @@ -32,6 +32,19 @@ function attendeeCreated(attendee) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| function removeAttendee() { | ||||
| 	return { | ||||
| 		type: AttendeeConstants.REMOVE_ATTENDEE | ||||
| 	} | ||||
| } | ||||
|  | ||||
| function attendeeRemoved(attendeeId) { | ||||
| 	return { | ||||
| 		type: AttendeeConstants.ATTENDEE_REMOVED, | ||||
| 		attendeeId: attendeeId | ||||
| 	} | ||||
| } | ||||
|  | ||||
| function fetchPopularAttendees() { | ||||
| 	return { | ||||
| 		type: AttendeeConstants.FETCH_POPULAR_ATTENDEES | ||||
| @@ -114,8 +127,8 @@ function fetchPopular() { | ||||
| 				if (e.isError()) { | ||||
| 					ErrorActions.serverError(e); | ||||
| 				} else { | ||||
| 					dispatch(popularAttendeesFetched(data.attendees.map(function(json) { | ||||
| 						var a = new Attendee(); | ||||
| 					dispatch(popularAttendeesFetched(data.popularattendees.map(function(json) { | ||||
| 						var a = new PopularAttendee(); | ||||
| 						a.fromJSON(json); | ||||
| 						return a; | ||||
| 					}))); | ||||
| @@ -128,8 +141,33 @@ function fetchPopular() { | ||||
| 	}; | ||||
| } | ||||
|  | ||||
| function remove(attendee) { | ||||
| 	return function(dispatch) { | ||||
| 		dispatch(removeAttendee()); | ||||
|  | ||||
| 		$.ajax({ | ||||
| 			type: "DELETE", | ||||
| 			dataType: "json", | ||||
| 			url: "attendee/"+attendee.AttendeeId+"/", | ||||
| 			success: function(data, status, jqXHR) { | ||||
| 				var e = new Error(); | ||||
| 				e.fromJSON(data); | ||||
| 				if (e.isError()) { | ||||
| 					ErrorActions.serverError(e); | ||||
| 				} else { | ||||
| 					dispatch(attendeeRemoved(attendee.AttendeeId)); | ||||
| 				} | ||||
| 			}, | ||||
| 			error: function(jqXHR, status, error) { | ||||
| 				ErrorActions.ajaxError(e); | ||||
| 			} | ||||
| 		}); | ||||
| 	}; | ||||
| } | ||||
|  | ||||
| module.exports = { | ||||
| 	fetchAll: fetchAll, | ||||
| 	create: create, | ||||
| 	remove: remove, | ||||
| 	fetchPopular: fetchPopular | ||||
| }; | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| var ErrorConstants = require('../constants/ErrorConstants'); | ||||
|  | ||||
| var models = require('../models.js'); | ||||
| var models = require('../models'); | ||||
| var Error = models.Error; | ||||
|  | ||||
| function serverError(error) { | ||||
|   | ||||
| @@ -2,8 +2,9 @@ var SuggestionConstants = require('../constants/SuggestionConstants'); | ||||
|  | ||||
| var ErrorActions = require('./ErrorActions'); | ||||
|  | ||||
| var models = require('../models.js'); | ||||
| var models = require('../models'); | ||||
| var Suggestion = models.Suggestion; | ||||
| var PopularSuggestion = models.PopularSuggestion; | ||||
| var Error = models.Error; | ||||
|  | ||||
| function fetchSuggestions() { | ||||
| @@ -114,8 +115,8 @@ function fetchPopular() { | ||||
| 				if (e.isError()) { | ||||
| 					ErrorActions.serverError(e); | ||||
| 				} else { | ||||
| 					dispatch(popularSuggestionsFetched(data.suggestions.map(function(json) { | ||||
| 						var a = new Suggestion(); | ||||
| 					dispatch(popularSuggestionsFetched(data.popularsuggestions.map(function(json) { | ||||
| 						var a = new PopularSuggestion(); | ||||
| 						a.fromJSON(json); | ||||
| 						return a; | ||||
| 					}))); | ||||
|   | ||||
| @@ -4,7 +4,7 @@ var AttendeeActions = require('./AttendeeActions'); | ||||
| var SuggestionActions = require('./SuggestionActions'); | ||||
| var ErrorActions = require('./ErrorActions'); | ||||
|  | ||||
| var models = require('../models.js'); | ||||
| var models = require('../models'); | ||||
| var User = models.User; | ||||
| var Session = models.Session; | ||||
| var Error = models.Error; | ||||
|   | ||||
| @@ -7,8 +7,9 @@ var Tab = ReactBootstrap.Tab; | ||||
| var Modal = ReactBootstrap.Modal; | ||||
|  | ||||
| var TopBarContainer = require('../containers/TopBarContainer'); | ||||
| var NewUserForm = require('./NewUserForm'); | ||||
| var RecordLunchContainer = require('../containers/RecordLunchContainer'); | ||||
| var AccountSettingsModalContainer = require('../containers/AccountSettingsModalContainer'); | ||||
| var NewUserForm = require('./NewUserForm'); | ||||
|  | ||||
| module.exports = React.createClass({ | ||||
| 	displayName: "LunchApp", | ||||
| @@ -62,7 +63,8 @@ module.exports = React.createClass({ | ||||
| 			if (this.props.user.isUser()) | ||||
| 				mainContent = ( | ||||
| 					<Tabs defaultActiveKey={1} id='mainNavigationTabs'> | ||||
| 						<Tab title="New Entry" eventKey={1} >accounts | ||||
| 						<Tab title="Record Lunch" eventKey={1} > | ||||
| 							<RecordLunchContainer /> | ||||
| 						</Tab> | ||||
| 						<Tab title="Statistics" eventKey={2} >stats will go here | ||||
| 						</Tab> | ||||
|   | ||||
							
								
								
									
										71
									
								
								js/components/RecordLunch.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								js/components/RecordLunch.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| var React = require('react'); | ||||
|  | ||||
| var ReactBootstrap = require('react-bootstrap'); | ||||
| var FormGroup = ReactBootstrap.FormGroup; | ||||
| var ControlLabel = ReactBootstrap.ControlLabel; | ||||
|  | ||||
| var Multiselect = require('react-widgets').Multiselect; | ||||
|  | ||||
| var models = require('../models') | ||||
| var Attendee = models.Attendee | ||||
|  | ||||
| module.exports = React.createClass({ | ||||
| 	displayName: "RecordLunch", | ||||
| 	getInitialState: function() { | ||||
| 		return { | ||||
| 		}; | ||||
| 	}, | ||||
| 	getAttendeeList: function() { | ||||
| 		var attendeeList = []; | ||||
| 		for (var attendeeId in this.props.attendees) { | ||||
| 			attendeeList.push(this.props.attendees[attendeeId]); | ||||
| 		} | ||||
| 		return attendeeList; | ||||
| 	}, | ||||
| 	getAttendeeMap: function() { | ||||
| 		var attendeeMap = {}; | ||||
| 		for (var attendeeId in this.props.attendees) { | ||||
| 			var attendee = this.props.attendees[attendeeId]; | ||||
| 			attendeeMap[attendee.Name] = attendee; | ||||
| 		} | ||||
| 		return attendeeMap; | ||||
| 	}, | ||||
| 	onChangeAttendees: function(attendees) { | ||||
| 		var attendeeMap = this.getAttendeeMap(); | ||||
| 		for (var i in attendees) { | ||||
| 			if (attendeeMap.hasOwnProperty(attendees[i].Name)) { | ||||
| 				delete attendeeMap[attendees[i].Name]; | ||||
| 			} else { | ||||
| 				var attendee = new Attendee(); | ||||
| 				attendee.Name = attendees[i].Name; | ||||
| 				this.props.createAttendee(attendee); | ||||
| 			} | ||||
| 		} | ||||
| 		for (var i in attendeeMap) { | ||||
| 			this.props.removeAttendee(attendeeMap[i]); | ||||
| 		} | ||||
| 	}, | ||||
| 	onCreateAttendee: function(attendeeName) { | ||||
| 		var attendee = new Attendee(); | ||||
| 		attendee.Name = attendeeName; | ||||
| 		this.props.createAttendee(attendee); | ||||
| 	}, | ||||
| 	render: function() { | ||||
| 		var attendeeList = this.getAttendeeList(); | ||||
| 		return ( | ||||
| 			<div><form> | ||||
| 				<FormGroup> | ||||
| 				<ControlLabel>Attendees</ControlLabel> | ||||
| 				<Multiselect | ||||
| 					value={attendeeList} | ||||
| 					data={this.props.popularAttendees} | ||||
| 					valueField='Name' | ||||
| 					textField='Name' | ||||
| 					messages={{'createNew': "(create new attendee)"}} | ||||
| 					onChange={this.onChangeAttendees} | ||||
| 					onCreate={this.onCreateAttendee} /> | ||||
| 				</FormGroup> | ||||
| 			</form></div> | ||||
| 		); | ||||
| 	} | ||||
| }); | ||||
| @@ -5,6 +5,8 @@ module.exports = keyMirror({ | ||||
| 	ATTENDEES_FETCHED: null, | ||||
| 	CREATE_ATTENDEE: null, | ||||
| 	ATTENDEE_CREATED: null, | ||||
| 	REMOVE_ATTENDEE: null, | ||||
| 	ATTENDEE_REMOVED: null, | ||||
| 	FETCH_POPULAR_ATTENDEES: null, | ||||
| 	POPULAR_ATTENDEES_FETCHED: null | ||||
| }); | ||||
|   | ||||
							
								
								
									
										29
									
								
								js/containers/RecordLunchContainer.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								js/containers/RecordLunchContainer.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| var connect = require('react-redux').connect; | ||||
|  | ||||
| var UserActions = require('../actions/UserActions'); | ||||
| var AttendeeActions = require('../actions/AttendeeActions'); | ||||
| var SuggestionActions = require('../actions/SuggestionActions'); | ||||
|  | ||||
| var RecordLunch = require('../components/RecordLunch'); | ||||
|  | ||||
| function mapStateToProps(state) { | ||||
| 	return { | ||||
| 		attendees: state.attendees, | ||||
| 		popularAttendees: state.popularAttendees, | ||||
| 		suggestions: state.suggestions, | ||||
| 		popularSuggestions: state.popularSuggestions | ||||
| 	} | ||||
| } | ||||
|  | ||||
| function mapDispatchToProps(dispatch) { | ||||
| 	return { | ||||
| 		createAttendee: function(attendee) {dispatch(AttendeeActions.create(attendee))}, | ||||
| 		removeAttendee: function(attendee) {dispatch(AttendeeActions.remove(attendee))}, | ||||
| 		createSuggestion: function(suggestion) {dispatch(SuggestionActions.create(suggestion))} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| module.exports = connect( | ||||
| 	mapStateToProps, | ||||
| 	mapDispatchToProps | ||||
| )(RecordLunch) | ||||
							
								
								
									
										58
									
								
								js/models.js
									
									
									
									
									
								
							
							
						
						
									
										58
									
								
								js/models.js
									
									
									
									
									
								
							| @@ -101,6 +101,33 @@ Attendee.prototype.isAttendee = function() { | ||||
| 		this.Name != empty_attendee.Name; | ||||
| } | ||||
|  | ||||
| function PopularAttendee() { | ||||
| 	this.Name = ""; | ||||
| 	this.Popularity = 0; | ||||
| } | ||||
|  | ||||
| PopularAttendee.prototype.toJSON = function() { | ||||
| 	var json_obj = {}; | ||||
| 	json_obj.Name = this.Name; | ||||
| 	json_obj.Popularity = this.Popularity; | ||||
| 	return JSON.stringify(json_obj); | ||||
| } | ||||
|  | ||||
| PopularAttendee.prototype.fromJSON = function(json_input) { | ||||
| 	var json_obj = getJSONObj(json_input); | ||||
|  | ||||
| 	if (json_obj.hasOwnProperty("Popularity")) | ||||
| 		this.Popularity = json_obj.Popularity; | ||||
| 	if (json_obj.hasOwnProperty("Name")) | ||||
| 		this.Name = json_obj.Name; | ||||
| } | ||||
|  | ||||
| PopularAttendee.prototype.isPopularAttendee = function() { | ||||
| 	var empty_attendee = new PopularAttendee(); | ||||
| 	return this.Popularity != empty_attendee.Popularity || | ||||
| 		this.Name != empty_attendee.Name; | ||||
| } | ||||
|  | ||||
| function Suggestion() { | ||||
| 	this.SuggestionId = -1; | ||||
| 	this.VetoingId = -1; | ||||
| @@ -134,6 +161,33 @@ Suggestion.prototype.isSuggestion = function() { | ||||
| 		this.RestaurantName != empty_suggestion.RestaurantName; | ||||
| } | ||||
|  | ||||
| function PopularSuggestion() { | ||||
| 	this.RestaurantName = ""; | ||||
| 	this.Popularity = 0; | ||||
| } | ||||
|  | ||||
| PopularSuggestion.prototype.toJSON = function() { | ||||
| 	var json_obj = {}; | ||||
| 	json_obj.RestaurantName = this.RestaurantName; | ||||
| 	json_obj.Popularity = this.Popularity; | ||||
| 	return JSON.stringify(json_obj); | ||||
| } | ||||
|  | ||||
| PopularSuggestion.prototype.fromJSON = function(json_input) { | ||||
| 	var json_obj = getJSONObj(json_input); | ||||
|  | ||||
| 	if (json_obj.hasOwnProperty("RestaurantName")) | ||||
| 		this.RestaurantName = json_obj.RestaurantName; | ||||
| 	if (json_obj.hasOwnProperty("Popularity")) | ||||
| 		this.Popularity = json_obj.Popularity; | ||||
| } | ||||
|  | ||||
| PopularSuggestion.prototype.isPopularSuggestion = function() { | ||||
| 	var empty_suggestion = new PopularSuggestion(); | ||||
| 	return this.RestaurantName != empty_suggestion.RestaurantName && | ||||
| 		this.Popularity != empty_suggestion.Popularity; | ||||
| } | ||||
|  | ||||
| function Error() { | ||||
| 	this.ErrorId = -1; | ||||
| 	this.ErrorString = ""; | ||||
| @@ -167,6 +221,10 @@ module.exports = models = { | ||||
| 	User: User, | ||||
| 	Session: Session, | ||||
| 	Error: Error, | ||||
| 	Attendee: Attendee, | ||||
| 	PopularAttendee: PopularAttendee, | ||||
| 	Suggestion: Suggestion, | ||||
| 	PopularSuggestion: PopularSuggestion, | ||||
|  | ||||
| 	// Constants | ||||
| 	BogusPassword: "password" | ||||
|   | ||||
| @@ -18,6 +18,10 @@ module.exports = function(state = {}, action) { | ||||
| 				[attendee.AttendeeId]: attendee | ||||
| 			}); | ||||
| 			return attendees; | ||||
| 		case AttendeeConstants.ATTENDEE_REMOVED: | ||||
| 			var attendees = assign({}, state); | ||||
| 			delete attendees[action.attendeeId]; | ||||
| 			return attendees; | ||||
| 		case UserConstants.USER_LOGGEDOUT: | ||||
| 			return {}; | ||||
| 		default: | ||||
|   | ||||
| @@ -3,17 +3,17 @@ var assign = require('object-assign'); | ||||
| var AttendeeConstants = require('../constants/AttendeeConstants'); | ||||
| var UserConstants = require('../constants/UserConstants'); | ||||
|  | ||||
| module.exports = function(state = {}, action) { | ||||
| module.exports = function(state = [], action) { | ||||
| 	switch (action.type) { | ||||
| 		case AttendeeConstants.POPULAR_ATTENDEES_FETCHED: | ||||
| 			var attendees = {}; | ||||
| 			var attendees = []; | ||||
| 			for (var i = 0; i < action.attendees.length; i++) { | ||||
| 				var attendee = action.attendees[i]; | ||||
| 				attendees[attendee.AttendeeId] = attendee; | ||||
| 				attendees.push(action.attendees[i]); | ||||
| 			} | ||||
| 			attendees.sort(function(a, b){return a.Popularity - b.Popularity}); | ||||
| 			return attendees; | ||||
| 		case UserConstants.USER_LOGGEDOUT: | ||||
| 			return {}; | ||||
| 			return []; | ||||
| 		default: | ||||
| 			return state; | ||||
| 	} | ||||
|   | ||||
| @@ -3,17 +3,17 @@ var assign = require('object-assign'); | ||||
| var SuggestionConstants = require('../constants/SuggestionConstants'); | ||||
| var UserConstants = require('../constants/UserConstants'); | ||||
|  | ||||
| module.exports = function(state = {}, action) { | ||||
| module.exports = function(state = [], action) { | ||||
| 	switch (action.type) { | ||||
| 		case SuggestionConstants.POPULAR_SUGGESTIONS_FETCHED: | ||||
| 			var suggestions = {}; | ||||
| 			var suggestions = []; | ||||
| 			for (var i = 0; i < action.suggestions.length; i++) { | ||||
| 				var suggestion = action.suggestions[i]; | ||||
| 				suggestions[suggestion.SuggestionId] = suggestion; | ||||
| 				suggestions.push(action.suggestions[i]); | ||||
| 			} | ||||
| 			suggestions.sort(function(a, b){return a.Popularity - b.Popularity}); | ||||
| 			return suggestions; | ||||
| 		case UserConstants.USER_LOGGEDOUT: | ||||
| 			return {}; | ||||
| 			return []; | ||||
| 		default: | ||||
| 			return state; | ||||
| 	} | ||||
|   | ||||
| @@ -5,12 +5,14 @@ div#content { | ||||
| 	display: block; | ||||
| 	width: 95%; | ||||
| 	height: 100%; | ||||
| 	min-width: 75em; | ||||
| 	min-width: 20em; | ||||
| 	max-width: 100em; | ||||
| 	margin: auto; | ||||
| } | ||||
|  | ||||
| /* Keep the main windows sized to the full viewable height */ | ||||
| .fullheight { | ||||
| 	height: 100%; | ||||
| div.tab-content { | ||||
| 	padding: 1em; | ||||
| 	border-bottom: 1px solid #ddd; | ||||
| 	border-left: 1px solid #ddd; | ||||
| 	border-right: 1px solid #ddd; | ||||
| } | ||||
|   | ||||
| @@ -73,7 +73,7 @@ func GetSuggestions(userid int64, date time.Time) (*[]*Suggestion, error) { | ||||
|  | ||||
| func GetPopularSuggestions() (*[]*PopularSuggestion, error) { | ||||
| 	var suggestions []*Suggestion | ||||
| 	var suggestionMap map[string]int64 | ||||
| 	suggestionMap := make(map[string]int64) | ||||
| 	popularSuggestions := make([]*PopularSuggestion, 0) | ||||
|  | ||||
| 	_, err := DB.Select(&suggestions, "SELECT * from suggestions") | ||||
|   | ||||
		Reference in New Issue
	
	Block a user