├── .gitignore ├── README.md ├── app ├── app.js ├── components │ ├── Employee.js │ ├── Main.js │ ├── Manager.js │ ├── children │ │ ├── AnnouncementsBuild.js │ │ ├── AnnouncementsView.js │ │ ├── EmployeeHome.js │ │ ├── Login.js │ │ ├── ManagerEmployeeAll.js │ │ ├── ManagerHome.js │ │ ├── ManagerSchedulesCreate.js │ │ ├── Register.js │ │ └── ScheduleView.js │ └── utils │ │ └── helpers.js └── config │ └── routes.js ├── controllers └── db_controller.js ├── db └── db.js ├── models ├── announcements.js ├── employee.js ├── employeeSchedule.js └── user.js ├── package-lock.json ├── package.json ├── public ├── 404.html ├── assets │ ├── images │ │ ├── favicon.ico │ │ ├── logo-small.png │ │ └── logo.png │ ├── javascript.js │ └── style.css ├── error.html ├── index.html └── notauth.html ├── server.js └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | public/bundle.js 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ![Icon](https://raw.githubusercontent.com/clsavino/react-shift-scheduler/master/public/assets/images/logo-small.png) Schedulr 2 | Schedulr is an employee management and scheduling app that allows… 3 | * Managers to manage and schedule employees 4 | * Employees to view work schedules 5 | 6 | View app live on [Heroku](https://reactschedulr.herokuapp.com) 7 | 8 | ## Run locally 9 | 10 | Schedulr requires [Node.js](https://nodejs.org/) and [MongoDB](https://docs.mongodb.com/manual/installation/) to run 11 | 12 | ### Installation 13 | Once mongo is installed, open a new terminal and run 14 | 15 | 16 | `$ mongod` 17 | 18 | Open another terminal window and navigate to project directory and run 19 | 20 | `$ npm install` 21 | 22 | Create a .env file with and add the code below (not strings) 23 | 24 | ``` 25 | GOOGLE_CLIENT_ID= 26 | GOOGLE_CLIENT_SECRET= 27 | GOOGLE_CALLBACK_URL=http://YOUR_DOMAIN/auth/google/callback 28 | 29 | LINKEDIN_ID= 30 | LINKEDIN_SECRET= 31 | LINKEDIN_CALLBACK=http://YOUR_DOMAIN/auth/linkedin/callback 32 | ``` 33 | 34 | If you dont want to go through the trouble of creating the API keys, put in dummy numbers/text and the app should still work, however passport social login will not. 35 | 36 | ### Run App 37 | 38 | `$ npm run build` 39 | 40 | Wait for webpack to bundle then 41 | 42 | `$ npm start` 43 | 44 | 45 | Open a browser and go to [http://localhost:8080](http://localhost:8080) 46 | 47 | ## Team 48 | * Andrea Roche [@amr08](https://github.com/amr08) 49 | * Christi Savino [@clsavino](https://github.com/clsavino) 50 | * Houston Breedlove [@hcbreedl](https://github.com/hcbreedl) 51 | * Nicolás Cáceres [@mr-attack](https://github.com/mr-attack) 52 | 53 | ## Tech 54 | Built with React, Node, Express, MongoDB, Passport.js -------------------------------------------------------------------------------- /app/app.js: -------------------------------------------------------------------------------- 1 | var React = require("react"); 2 | var ReactDOM = require("react-dom"); 3 | var routes = require("./config/routes"); 4 | 5 | ReactDOM.render(routes, document.getElementById("app")); -------------------------------------------------------------------------------- /app/components/Employee.js: -------------------------------------------------------------------------------- 1 | var React = require("react"); 2 | var helpers = require("./utils/helpers"); 3 | 4 | var Employee = React.createClass({ 5 | 6 | getInitialState: function() { 7 | return { 8 | username: "", 9 | picture: "" 10 | }; 11 | }, 12 | 13 | componentDidMount: function() { 14 | helpers.getCurrentUser().then(function(response) { 15 | if (response !== this.state.username) { 16 | this.setState({ picture: response.data.picture, username: response.data.username }); 17 | } 18 | }.bind(this)); 19 | }, 20 | 21 | render: function() { 22 | return ( 23 |
24 | 27 | 49 |
50 | {this.props.children} 51 |
52 |
53 | ); 54 | } 55 | }); 56 | 57 | module.exports = Employee; -------------------------------------------------------------------------------- /app/components/Main.js: -------------------------------------------------------------------------------- 1 | var React = require("react"); 2 | var helpers = require("./utils/helpers"); 3 | 4 | var Main = React.createClass({ 5 | render: function() { 6 | return ( 7 |
8 | {this.props.children} 9 |
10 | ); 11 | } 12 | }); 13 | 14 | module.exports = Main; -------------------------------------------------------------------------------- /app/components/Manager.js: -------------------------------------------------------------------------------- 1 | var React = require("react"); 2 | var helpers = require("./utils/helpers"); 3 | 4 | var Manager = React.createClass({ 5 | 6 | getInitialState: function() { 7 | return { 8 | username: "", 9 | picture: "" 10 | }; 11 | }, 12 | 13 | componentDidMount: function() { 14 | helpers.getCurrentUser().then(function(response) { 15 | if (response !== this.state.username) { 16 | this.setState({ picture: response.data.picture, username: response.data.username }); 17 | } 18 | }.bind(this)); 19 | }, 20 | 21 | render: function() { 22 | return ( 23 |
24 | 27 | 53 |
54 | {this.props.children} 55 |
56 |
57 | ); 58 | } 59 | }); 60 | 61 | module.exports = Manager; -------------------------------------------------------------------------------- /app/components/children/AnnouncementsBuild.js: -------------------------------------------------------------------------------- 1 | var React = require("react"); 2 | var helpers = require("../utils/helpers"); 3 | 4 | var AnnouncementsBuild = React.createClass({ 5 | getInitialState: function() { 6 | return { 7 | title: "", 8 | content: "" 9 | }; 10 | }, 11 | 12 | // componentDidMount: function() { 13 | // this.getAnnouncements(); 14 | // }, 15 | // 16 | // getAnnouncements: function() { 17 | // helpers.getAnnouncements().then(function(response) { 18 | // 19 | // }.bind(this)); 20 | // }, 21 | 22 | handleAnnouncementBuild(event) { 23 | this.setState({ [event.target.id]: event.target.value}); 24 | }, 25 | 26 | addAnnouncements: function(event) { 27 | event.preventDefault(event); 28 | helpers.addAnnouncements(this.state.title, this.state.content).then(function(response) { 29 | this.clearStates(); 30 | }.bind(this)); 31 | Materialize.toast('Announcement added', 3000); 32 | this.clearForm(); 33 | }, 34 | 35 | clearForm: function() { 36 | var elements = document.getElementsByTagName("input"); 37 | for (var i=0; i < elements.length; i++) { 38 | elements[i].value = ""; 39 | elements[i].classList.remove("valid"); 40 | }; 41 | }, 42 | 43 | clearStates: function() { 44 | this.setState({ title: "", content: "" }); 45 | }, 46 | 47 | render: function() { 48 | return ( 49 |
50 |
51 |
52 |
Make an announcement
53 |
54 |
55 |
56 |
57 |
58 | 66 |
67 |
68 |
69 |
70 | 79 |
80 |
81 |
82 |
83 | 84 |
85 |
86 |
87 |
88 | ); 89 | } 90 | }); 91 | 92 | module.exports = AnnouncementsBuild; -------------------------------------------------------------------------------- /app/components/children/AnnouncementsView.js: -------------------------------------------------------------------------------- 1 | var React = require("react"); 2 | 3 | var AnnouncementsView = React.createClass({ 4 | render: function() { 5 | return ( 6 |
7 |
8 |
9 |
Latest announcement
10 |
11 |
12 |
13 |
14 | {/*
{this.state.title}
*/} 15 |
{this.props.title}
16 |

{this.props.content}

17 | {/*

{this.state.content}

*/} 18 |
19 |
20 |
21 | ); 22 | } 23 | }); 24 | 25 | module.exports = AnnouncementsView; -------------------------------------------------------------------------------- /app/components/children/EmployeeHome.js: -------------------------------------------------------------------------------- 1 | var React = require("react"); 2 | var helpers = require("../utils/helpers"); 3 | var ScheduleView = require("./ScheduleView"); 4 | var AnnouncementsView = require("./AnnouncementsView"); 5 | 6 | var EmployeeHome = React.createClass({ 7 | getInitialState: function() { 8 | return { 9 | title: "", 10 | content: "" 11 | }; 12 | }, 13 | 14 | componentDidMount: function() { 15 | this.getAnnouncements(); 16 | }, 17 | 18 | // componentDidUpdate: function(prevState) { 19 | // if (prevState.title !== this.state.title || prevState.content !== this.state.content) { 20 | // this.getAnnouncements(); 21 | // } 22 | // }, 23 | 24 | getAnnouncements: function() { 25 | helpers.getAnnouncements().then(function(response) { 26 | this.setState({ 27 | title: response.data[response.data.length -1].title, 28 | content: response.data[response.data.length -1].content 29 | }); 30 | }.bind(this)); 31 | }, 32 | 33 | render: function() { 34 | return ( 35 |
36 | 37 | 38 |
39 | ); 40 | } 41 | }); 42 | 43 | module.exports = EmployeeHome; -------------------------------------------------------------------------------- /app/components/children/Login.js: -------------------------------------------------------------------------------- 1 | var React = require("react"); 2 | 3 | class Login extends React.Component { 4 | constructor(props){ 5 | super(props); 6 | this.state = { 7 | username: "", 8 | password: "", 9 | } 10 | this.handleUserChange = this.handleUserChange.bind(this); 11 | this.handleLogin = this.handleLogin.bind(this); 12 | } 13 | 14 | handleUserChange(event) { 15 | this.setState({ [event.target.name]: event.target.value}); 16 | } 17 | 18 | handleLogin() { 19 | // just in case we need it 20 | } 21 | render() { 22 | return ( 23 |
24 |
25 |
26 |
27 |
28 |
29 |

Schedulr

30 |
31 |
32 |
33 |
34 |
35 | 43 |
44 |
45 |
46 |
47 | 55 |
56 |
57 |
58 |
59 | 60 |
61 |
62 |
63 |
64 |
Or login with
65 |
66 |
67 |
68 |
69 | 70 |
71 |
72 | 73 |
74 |
75 |
76 |
77 |
78 |
Don't have an account?
79 |
80 | 81 |
82 |
83 |
84 | Registerperson_add 85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | ); 93 | } 94 | }; 95 | 96 | module.exports = Login; -------------------------------------------------------------------------------- /app/components/children/ManagerEmployeeAll.js: -------------------------------------------------------------------------------- 1 | var React = require("react"); 2 | var helpers = require("../utils/helpers"); 3 | 4 | var ManagerEmployeeAll = React.createClass({ 5 | getInitialState: function() { 6 | return { 7 | firstName: "", 8 | lastName: "", 9 | addressOne: "", 10 | addressTwo: "", 11 | city: "", 12 | state: "", 13 | zip: "", 14 | email: "", 15 | phone: "", 16 | phoneType: "", 17 | allEmployees: [], 18 | selectedEmployee: "", 19 | emp_id: "" 20 | }; 21 | }, 22 | 23 | componentDidMount: function() { 24 | this.getEmployees(); 25 | }, 26 | 27 | getEmployees: function() { 28 | helpers.getAllEmployees().then(function(response) { 29 | if (response !== this.state.allEmployees) { 30 | this.setState({ allEmployees: response.data }); 31 | this.activeButtons(); 32 | } 33 | }.bind(this)); 34 | }, 35 | 36 | handleUserChange(event) { 37 | this.setState({ [event.target.name]: event.target.value}); 38 | }, 39 | 40 | handleAddForm: function(event) { 41 | event.preventDefault(); 42 | helpers.addEmployee(this.state.firstName, this.state.lastName, this.state.addressOne, this.state.addressTwo, this.state.city, this.state.state, this.state.zip, this.state.email, this.state.phone, this.state.phoneType).then(function(response) { 43 | this.state.emp_id = response.data._id; 44 | 45 | helpers.addEmpSchedule(this.state.emp_id, this.state.firstName, this.state.lastName).then(function(response) { 46 | this.clearStates(); 47 | }.bind(this)); 48 | 49 | }.bind(this)); 50 | Materialize.toast('Employee added', 3000); 51 | this.clearForm(); 52 | this.getEmployees(); 53 | }, 54 | 55 | handleUpdateForm: function(event) { 56 | event.preventDefault(); 57 | helpers.updateEmployee(this.state.selectedEmployee, this.state.firstName, this.state.lastName, this.state.addressOne, this.state.addressTwo, this.state.city, this.state.state, this.state.zip, this.state.email, this.state.phone, this.state.phoneType).then(function(response) { 58 | }.bind(this)); 59 | 60 | helpers.updateEmpName(this.state.emp_id, this.state.firstName, this.state.lastName).then(function(response) { 61 | this.clearStates(); 62 | }.bind(this)); 63 | Materialize.toast("Employee updated", 3000); 64 | this.clearForm(); 65 | this.getEmployees(); 66 | }, 67 | 68 | handleRemoveForm: function(event) { 69 | event.preventDefault(); 70 | helpers.removeEmployee(this.state.selectedEmployee).then(function(response) { 71 | }.bind(this)); 72 | helpers.removeEmpSchedule(this.state.emp_id).then(function(response) { 73 | this.clearStates(); 74 | }.bind(this)); 75 | Materialize.toast("Employee removed", 3000); 76 | this.clearForm(); 77 | this.getEmployees(); 78 | }, 79 | 80 | clickEmployee: function(event) { 81 | this.setState({selectedEmployee: event.target.id}, function() { 82 | for (var i = 0; i < this.state.allEmployees.length; i++) { 83 | if (this.state.allEmployees[i]._id == this.state.selectedEmployee) { 84 | this.setState({ 85 | firstName: this.state.allEmployees[i].firstName, 86 | lastName: this.state.allEmployees[i].lastName, 87 | addressOne: this.state.allEmployees[i].addressOne, 88 | addressTwo: this.state.allEmployees[i].addressTwo, 89 | city: this.state.allEmployees[i].city, 90 | state: this.state.allEmployees[i].state, 91 | zip: this.state.allEmployees[i].zip, 92 | email: this.state.allEmployees[i].email, 93 | phone: this.state.allEmployees[i].phone, 94 | phoneType: this.state.allEmployees[i].phoneType, 95 | emp_id: this.state.selectedEmployee 96 | }); 97 | this.activeButtons(); 98 | } 99 | } 100 | }); 101 | }, 102 | 103 | newEmployee: function() { 104 | this.clearForm(); 105 | this.clearStates(); 106 | this.activeButtons(); 107 | }, 108 | 109 | clearForm: function() { 110 | var elements = document.getElementsByTagName("input"); 111 | for (var i=0; i < elements.length; i++) { 112 | if ((elements[i].type == "text") || (elements[i].type == "number") || (elements[i].type == "email")) { 113 | elements[i].value = ""; 114 | elements[i].classList.remove("valid"); 115 | } 116 | }; 117 | this.getEmployees(); 118 | }, 119 | 120 | clearStates: function() { 121 | this.setState({ firstName: "", lastName: "", addressOne: "", addressTwo: "", city: "", state: "", zip: "", email: "", phone: "", phoneType: "", selectedEmployee: ""}); 122 | }, 123 | 124 | activeButtons: function() { 125 | // don't allow updating or removing on empty form 126 | if (this.state.selectedEmployee == "") { 127 | document.getElementById("addEmployee").className = "btn btn-large waves-effect waves-light green accent-3"; 128 | document.getElementById("updateEmployee").className += " disabled"; 129 | document.getElementById("removeEmployee").className += " disabled"; 130 | } else { 131 | document.getElementById("addEmployee").className += " disabled"; 132 | document.getElementById("updateEmployee").className = "btn btn-large waves-effect waves-light blue accent-3"; 133 | document.getElementById("removeEmployee").className = "btn btn-large waves-effect waves-light red accent-3"; 134 | } 135 | }, 136 | 137 | render: function() { 138 | return ( 139 |
140 |
141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 152 | 153 | {this.state.allEmployees.map(function(ManagerEmployeeAll, i) { 154 | return ( 155 | 156 | 159 | 160 | ); 161 | }, this)} 162 | 163 |
Employees
150 | New Employeeadd 151 |
157 | {ManagerEmployeeAll.firstName} {ManagerEmployeeAll.lastName} 158 |
164 |
165 |
166 |
167 |
168 |
169 |
170 | 178 |
179 |
180 | 188 |
189 |
190 |
191 |
192 | 200 |
201 |
202 |
203 |
204 | 211 |
212 |
213 |
214 |
215 | 223 |
224 |
225 | 278 |
279 |
280 | 288 |
289 |
290 |
291 |
292 | 300 |
301 |
302 |
303 |
304 | 312 |
313 |
314 | 320 |
321 |
322 |
323 |
324 | 327 |
328 | 333 | 338 |
339 |
340 |
341 |
342 |
343 | ); 344 | } 345 | }); 346 | 347 | module.exports = ManagerEmployeeAll; -------------------------------------------------------------------------------- /app/components/children/ManagerHome.js: -------------------------------------------------------------------------------- 1 | var React = require("react"); 2 | var helpers = require("../utils/helpers"); 3 | var ScheduleView = require("./ScheduleView"); 4 | var AnnouncementsBuild = require("./AnnouncementsBuild"); 5 | var AnnouncementsView = require("./AnnouncementsView"); 6 | 7 | var ManagerHome = React.createClass({ 8 | getInitialState: function() { 9 | return { 10 | title: "", 11 | content: "" 12 | }; 13 | }, 14 | 15 | componentDidMount: function() { 16 | this.getAnnouncements(); 17 | }, 18 | 19 | // componentDidUpdate: function(prevState) { 20 | // if (prevState.title !== this.state.title || prevState.content !== this.state.content) { 21 | // this.getAnnouncements(); 22 | // } 23 | // }, 24 | 25 | getAnnouncements: function() { 26 | helpers.getAnnouncements().then(function(response) { 27 | this.setState({ 28 | title: response.data[response.data.length -1].title, 29 | content: response.data[response.data.length -1].content 30 | }); 31 | }.bind(this)); 32 | }, 33 | 34 | render: function() { 35 | return ( 36 |
37 | 38 |
39 |
40 | 41 |
42 |
43 | 44 |
45 |
46 |
47 | ); 48 | } 49 | }); 50 | 51 | module.exports = ManagerHome; -------------------------------------------------------------------------------- /app/components/children/ManagerSchedulesCreate.js: -------------------------------------------------------------------------------- 1 | var React = require("react"); 2 | var helpers = require("../utils/helpers"); 3 | 4 | var ManagerSchedulesCreate = React.createClass({ 5 | 6 | getInitialState: function() { 7 | return { 8 | firstName:"", 9 | lastName:"", 10 | monday:"", 11 | tuesday:"", 12 | wednesday:"", 13 | thursday:"", 14 | friday:"", 15 | saturday:"", 16 | sunday:"", 17 | selectedEmpId:"", 18 | selectedEmpSchedule:"", 19 | empSchedules: [], 20 | }; 21 | }, 22 | 23 | componentDidMount: function() { 24 | helpers.getEmpSchedules().then(function(response) { 25 | if (response !== this.state.empSchedules) { 26 | this.setState({ empSchedules: response.data }); 27 | } 28 | }.bind(this)); 29 | }, 30 | 31 | handleUserChange: function(index, event) { 32 | let updatedEmpSchedules = this.state.empSchedules.map((empSchedule, j) => { 33 | if(index === j){ 34 | //index is the index of the currently selected employee 35 | empSchedule[event.target.name] = event.target.value; 36 | this.setState({selectedEmpSchedule: empSchedule}); 37 | this.setState({ selectedEmpId: empSchedule._id }); 38 | } 39 | return empSchedule; 40 | }); 41 | this.setState({ empSchedules: updatedEmpSchedules}); 42 | }, 43 | 44 | handleUpdateEmpSchedule: function(event) { 45 | var saveButtonBlue = document.getElementById(event); 46 | saveButtonBlue.innerHTML = "Add"; 47 | saveButtonBlue.className = "btn btn-small waves-effect waves-light green accent-3"; 48 | 49 | if (this.state.selectedEmpSchedule !== "") { 50 | helpers.updateEmpSchedule(this.state.selectedEmpSchedule).then(function(response) { 51 | var empName = this.state.selectedEmpSchedule.firstName + " " + this.state.selectedEmpSchedule.lastName + "'s "; 52 | Materialize.toast(empName + "schedule updated", 2000); 53 | this.clearStates(); 54 | }.bind(this)); 55 | } 56 | }, 57 | 58 | handleClearEmpSchedule: function(i,event) { 59 | // i is the index of the currently selected employee 60 | event.preventDefault(); 61 | 62 | let updatedEmpSchedules = this.state.empSchedules.map((empSchedule, j) => { 63 | if(i === j){ 64 | var saveButton = document.getElementById(i); 65 | saveButton.innerHTML = "save"; 66 | saveButton.className = "btn btn-small waves-effect waves-light blue accent-3"; 67 | 68 | empSchedule.monday = ""; 69 | empSchedule.tuesday = ""; 70 | empSchedule.wednesday = ""; 71 | empSchedule.thursday = ""; 72 | empSchedule.friday = ""; 73 | empSchedule.saturday = ""; 74 | empSchedule.sunday = ""; 75 | this.state.selectedEmpSchedule = empSchedule; 76 | } 77 | return empSchedule; 78 | }); 79 | this.setState({ empSchedules: updatedEmpSchedules}); 80 | }, 81 | 82 | clearStates: function() { 83 | this.setState({ firstName: "", lastName: "", monday: "", tuesday: "", wednesday: "", thursday: "", friday: "", saturday: "", sunday: "", emp_id: "", selectedEmpSchedule: "", selectedEmpId: ""}); 84 | }, 85 | 86 | render: function() { 87 | return ( 88 | 89 |
90 |
91 |
92 |
Schedule Editor
93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | {this.state.empSchedules.map(function(schedules, i) { 109 | return ( 110 | 111 | 114 | 129 | 144 | 159 | 174 | 189 | 204 | 219 | 222 | 225 | 226 | ); 227 | }, this)} 228 | 229 |
NameMonTuesWedThursFriSatSun
112 | {schedules.firstName} {schedules.lastName} 113 | 115 |
116 | 127 |
128 |
130 |
131 | 142 |
143 |
145 |
146 | 157 |
158 |
160 |
161 | 172 |
173 |
175 |
176 | 187 |
188 |
190 |
191 | 202 |
203 |
205 |
206 | 217 |
218 |
220 | 221 | 223 | 224 |
230 |
231 |
232 |
233 | 234 | ); 235 | } 236 | }); 237 | 238 | module.exports = ManagerSchedulesCreate; 239 | -------------------------------------------------------------------------------- /app/components/children/Register.js: -------------------------------------------------------------------------------- 1 | var React = require("react"); 2 | var helpers = require("../utils/helpers"); 3 | 4 | class Register extends React.Component { 5 | constructor(props){ 6 | super(props); 7 | this.state = { 8 | username: "", 9 | password: "", 10 | email:"", 11 | passwordConfirmation:"", 12 | error: "" 13 | } 14 | 15 | this.handleUserChange = this.handleUserChange.bind(this); 16 | this.handleLogin = this.handleLogin.bind(this); 17 | } 18 | 19 | handleUserChange(event) { 20 | this.setState({ [event.target.name]: event.target.value}); 21 | } 22 | 23 | handleLogin() { 24 | 25 | // helpers.errorMessage().then(function(response) { 26 | // console.log(response) 27 | // this.setState({ error: response.data}); 28 | // }.bind(this)); 29 | 30 | } 31 | render() { 32 | return ( 33 |
34 |
35 |
36 |
37 |
38 |
39 |

Register

40 |

{this.state.error}

41 |
42 |
43 |
44 |
45 |
46 | 54 |
55 |
56 |
57 |
58 | 66 |
67 |
68 |
69 |
70 | 78 |
79 |
80 |
81 |
82 | 90 |
91 |
92 |
93 |
94 | 99 |
100 |
101 |
102 |
103 | 104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 | ); 112 | } 113 | }; 114 | 115 | module.exports = Register; 116 | -------------------------------------------------------------------------------- /app/components/children/ScheduleView.js: -------------------------------------------------------------------------------- 1 | var React = require("react"); 2 | var helpers = require("../utils/helpers"); 3 | 4 | var ScheduleView = React.createClass({ 5 | 6 | getInitialState: function() { 7 | return { 8 | empSchedules: [], 9 | }; 10 | }, 11 | 12 | componentDidMount: function() { 13 | helpers.getEmpSchedules().then(function(response) { 14 | if (response !== this.state.empSchedules) { 15 | this.setState({ empSchedules: response.data }); 16 | } 17 | }.bind(this)); 18 | }, 19 | 20 | render: function() { 21 | return ( 22 |
23 |
24 |
25 |
Week at a glance
26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | {this.state.empSchedules.map(function(schedules, i) { 41 | return ( 42 | 43 | 46 | 49 | 52 | 55 | 58 | 61 | 64 | 67 | 68 | ); 69 | }, this)} 70 | 71 |
NameMonTuesWedThursFriSatSun
44 | {schedules.firstName} {schedules.lastName} 45 | 47 | {schedules.monday} 48 | 50 | {schedules.tuesday} 51 | 53 | {schedules.wednesday} 54 | 56 | {schedules.thursday} 57 | 59 | {schedules.friday} 60 | 62 | {schedules.saturday} 63 | 65 | {schedules.sunday} 66 |
72 |
73 |
74 |
75 | ); 76 | } 77 | }); 78 | 79 | module.exports = ScheduleView; -------------------------------------------------------------------------------- /app/components/utils/helpers.js: -------------------------------------------------------------------------------- 1 | var axios = require("axios"); 2 | 3 | var helper = { 4 | 5 | getAllEmployees: function() { 6 | return axios.get("/getAllEmployees"); 7 | }, 8 | 9 | getCurrentUser: function() { 10 | return axios.get("/user"); 11 | }, 12 | 13 | // errorMessage: function() { 14 | // return axios.get("/register"); 15 | // }, 16 | 17 | getEmployee: function(id) { 18 | return axios.get("/getEmployee/" + id); 19 | }, 20 | 21 | getEmpSchedules:function() { 22 | return axios.get('/getEmpSchedules') 23 | .then(function(response){ 24 | return response; 25 | }) 26 | }, 27 | 28 | addEmpSchedule:function(emp_id, firstName, lastName) { 29 | return axios.post('/addEmpSchedule', { 30 | emp_id: emp_id, 31 | firstName: firstName, 32 | lastName: lastName 33 | }); 34 | }, 35 | 36 | updateEmpSchedule: function(empSchedule) { 37 | return axios.put('/updateSchedule/' + empSchedule._id, { 38 | employeeSchedule: empSchedule 39 | }); 40 | }, 41 | 42 | addEmployee: function(firstName, lastName, addressOne, addressTwo, city, state, zip, email, phone, phoneType) { 43 | return axios.post("/addEmployee", { 44 | firstName: firstName, 45 | lastName: lastName, 46 | addressOne: addressOne, 47 | addressTwo: addressTwo, 48 | city: city, 49 | state: state, 50 | zip: zip, 51 | email: email, 52 | phone: phone, 53 | phoneType: phoneType }); 54 | }, 55 | 56 | updateEmployee: function(id, firstName, lastName, addressOne, addressTwo, city, state, zip, email, phone, phoneType) { 57 | return axios.put("/updateEmployee/" + id, { 58 | firstName: firstName, 59 | lastName: lastName, 60 | addressOne: addressOne, 61 | addressTwo: addressTwo, 62 | city: city, 63 | state: state, 64 | zip: zip, 65 | email: email, 66 | phone: phone, 67 | phoneType: phoneType 68 | }); 69 | }, 70 | 71 | updateEmpName: function(emp_id, firstName, lastName) { 72 | return axios.put("/updateEmpName/" + emp_id, { 73 | firstName: firstName, 74 | lastName: lastName 75 | }); 76 | }, 77 | 78 | removeEmployee: function(id) { 79 | return axios.put("/removeEmployee/" + id); 80 | }, 81 | 82 | removeEmpSchedule: function(emp_id) { 83 | return axios.put("/removeEmpSchedule/" + emp_id); 84 | }, 85 | 86 | getAnnouncements: function() { 87 | return axios.get("/getAnnouncements"); 88 | }, 89 | 90 | addAnnouncements: function(title, content) { 91 | return axios.post("/addAnnouncements", { 92 | title: title, 93 | content: content }); 94 | } 95 | }; 96 | 97 | module.exports = helper; 98 | -------------------------------------------------------------------------------- /app/config/routes.js: -------------------------------------------------------------------------------- 1 | var React = require("react"); 2 | var router = require("react-router"); 3 | var Route = router.Route; 4 | var Router = router.Router; 5 | 6 | var browserHistory = router.browserHistory; 7 | var IndexRoute = router.IndexRoute; 8 | 9 | // landing components 10 | var Main = require("../components/Main"); 11 | var Login = require("../components/children/Login"); 12 | var Register = require("../components/children/Register"); 13 | // manager components 14 | var Manager = require("../components/Manager"); 15 | var ManagerHome = require("../components/children/ManagerHome"); 16 | var ManagerEmployeeAll = require("../components/children/ManagerEmployeeAll"); 17 | var ManagerSchedulesCreate = require("../components/children/ManagerSchedulesCreate"); 18 | // employee components 19 | var Employee = require("../components/Employee"); 20 | var EmployeeHome = require("../components/children/EmployeeHome"); 21 | 22 | module.exports = ( 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | ); -------------------------------------------------------------------------------- /controllers/db_controller.js: -------------------------------------------------------------------------------- 1 | var express = require("express"); 2 | var router = express.Router(); 3 | var db = require("../db/db.js"); 4 | var path = require("path"); 5 | 6 | var employee = require("../models/employee"); 7 | var EmployeeSchedule = require("../models/employeeSchedule"); 8 | var announcements = require("../models/announcements") 9 | 10 | //Getting Employees from the database 11 | router.get("/getAllEmployees", function(req, res) { 12 | employee.find({ "active": 1 }).exec(function(err, doc) { 13 | if (err) { 14 | console.log(err); 15 | } 16 | else { 17 | res.send(doc); 18 | } 19 | }); 20 | }); 21 | 22 | //Get employee schedules from database 23 | router.get("/getEmpSchedules", function(req, res) { 24 | EmployeeSchedule.find({ "active": 1 }).exec(function(err,docs) { 25 | if (err) { 26 | console.log(err); 27 | res.send(err); 28 | } 29 | else { 30 | res.send(docs); 31 | } 32 | }); 33 | }); 34 | 35 | //Posting Employee Schedule to the database 36 | router.post("/addEmpSchedule", function(req, res) { 37 | EmployeeSchedule.create({ 38 | emp_id: req.body.emp_id, 39 | firstName: req.body.firstName, 40 | lastName: req.body.lastName 41 | }, function(err) { 42 | if (err) { 43 | console.log(err); 44 | } 45 | else { 46 | res.send("Employee Schedule Saved!"); 47 | } 48 | }); 49 | }); 50 | 51 | //Updating existing employee schedule 52 | router.put("/updateSchedule/:id", function(req, res) { 53 | var newSchedule = req.body.employeeSchedule; 54 | EmployeeSchedule.findOneAndUpdate({ "_id": req.params.id }, { 55 | monday: newSchedule.monday, 56 | tuesday: newSchedule.tuesday, 57 | wednesday: newSchedule.wednesday, 58 | thursday: newSchedule.thursday, 59 | friday: newSchedule.friday, 60 | saturday: newSchedule.saturday, 61 | sunday: newSchedule.sunday 62 | }, function(err) { 63 | if (err) { 64 | console.log(err); 65 | } else { 66 | res.send("Employee schedule updated"); 67 | } 68 | }); 69 | }); 70 | 71 | //Posting new Employee to the database 72 | router.post("/addEmployee", function(req, res) { 73 | employee.create({ 74 | firstName: req.body.firstName, 75 | lastName: req.body.lastName, 76 | addressOne: req.body.addressOne, 77 | addressTwo: req.body.addressTwo, 78 | city: req.body.city, 79 | state: req.body.state, 80 | zip: req.body.zip, 81 | email: req.body.email, 82 | phone: req.body.phone, 83 | phoneType: req.body.phoneType 84 | }, function(err,doc) { 85 | if (err) { 86 | console.log(err); 87 | } 88 | else { 89 | res.send(doc); 90 | } 91 | }); 92 | }); 93 | 94 | //Updating existing employee 95 | router.put("/updateEmployee/:id", function(req, res) { 96 | employee.findOneAndUpdate({ "_id": req.params.id }, { 97 | firstName: req.body.firstName, 98 | lastName: req.body.lastName, 99 | addressOne: req.body.addressOne, 100 | addressTwo: req.body.addressTwo, 101 | city: req.body.city, 102 | state: req.body.state, 103 | zip: req.body.zip, 104 | email: req.body.email, 105 | phone: req.body.phone, 106 | phoneType: req.body.phoneType 107 | }, function(err) { 108 | if (err) { 109 | console.log(err); 110 | } else { 111 | res.send("Employee updated"); 112 | } 113 | }); 114 | }); 115 | 116 | // Update employee's name in employee schedule collection 117 | router.put("/updateEmpName/:emp_id", function(req, res) { 118 | EmployeeSchedule.findOneAndUpdate({"emp_id":req.params.emp_id}, { 119 | firstName: req.body.firstName, 120 | lastName: req.body.lastName, 121 | }, function(err) { 122 | if (err) { 123 | console.log(err); 124 | } else { 125 | res.send("Employee's name updated"); 126 | } 127 | }); 128 | }); 129 | 130 | // "Remove" existing employee 131 | router.put("/removeEmployee/:id", function(req, res) { 132 | employee.findOneAndUpdate({ "_id": req.params.id }, { "active": 0 }) 133 | .exec(function(err, doc) { 134 | if (err) { 135 | console.log(err); 136 | } else { 137 | res.send(doc); 138 | } 139 | }) 140 | }); 141 | 142 | // "Remove" existing employee schedule 143 | router.put("/removeEmpSchedule/:emp_id", function(req, res) { 144 | EmployeeSchedule.findOneAndUpdate({ "emp_id": req.params.emp_id }, { "active": 0 }) 145 | .exec(function(err, doc) { 146 | if (err) { 147 | console.log(err); 148 | } else { 149 | res.send(doc); 150 | } 151 | }) 152 | }); 153 | 154 | //Getting announcements from the database 155 | router.get("/getAnnouncements", function(req, res) { 156 | announcements.find({ "active": 1 }).exec(function(err, doc) { 157 | if (err) { 158 | console.log(err); 159 | } 160 | else { 161 | res.send(doc); 162 | } 163 | }); 164 | }); 165 | 166 | //Put announcements to database 167 | router.post("/addAnnouncements", function(req, res) { 168 | announcements.create({ 169 | title: req.body.title, 170 | content: req.body.content 171 | }, function(err, doc) { 172 | if (err) { 173 | console.log(err); 174 | } 175 | else { 176 | res.send(doc); 177 | } 178 | }); 179 | }); 180 | 181 | module.exports = router; 182 | -------------------------------------------------------------------------------- /db/db.js: -------------------------------------------------------------------------------- 1 | var mongoose = require("mongoose"); 2 | var Promise = require("bluebird"); 3 | mongoose.Promise = Promise; 4 | 5 | //DB 6 | var databaseUri ="mongodb://localhost/schedulr"; 7 | 8 | if(process.env.MONGODB_URI) { 9 | mongoose.connect(process.env.MONGODB_URI); 10 | } else { 11 | mongoose.connect(databaseUri); 12 | } 13 | 14 | var db = mongoose.connection; 15 | 16 | db.on("error", function(err) { 17 | console.log("Mongoose Error: ", err); 18 | }); 19 | 20 | db.once("open", function() { 21 | console.log("Mongoose connection successful."); 22 | }); -------------------------------------------------------------------------------- /models/announcements.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var Schema = mongoose.Schema; 3 | 4 | var AnnouncementSchema = new Schema({ 5 | title: { 6 | type: String 7 | }, 8 | content: { 9 | type: String 10 | }, 11 | // datetime: { 12 | // type: Date, 13 | // default: Date.now, 14 | // }, 15 | active: { 16 | type: Number, 17 | default: 1 18 | } 19 | }); 20 | 21 | var Announcement = mongoose.model('Announcement', AnnouncementSchema); 22 | module.exports = Announcement; 23 | -------------------------------------------------------------------------------- /models/employee.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var Schema = mongoose.Schema; 3 | 4 | var EmployeeSchema = new Schema({ 5 | firstName: { 6 | type: String 7 | }, 8 | lastName: { 9 | type: String 10 | }, 11 | addressOne: { 12 | type: String 13 | }, 14 | addressTwo: { 15 | type: String 16 | }, 17 | city: { 18 | type: String 19 | }, 20 | state: { 21 | type: String 22 | }, 23 | zip: { 24 | type: String 25 | }, 26 | email: { 27 | type: String 28 | }, 29 | phone: { 30 | type: String 31 | }, 32 | phoneType: { 33 | type: String 34 | }, 35 | active: { 36 | type: Number, 37 | default: 1 38 | } 39 | }); 40 | 41 | var Employee = mongoose.model('Employee', EmployeeSchema); 42 | module.exports = Employee; -------------------------------------------------------------------------------- /models/employeeSchedule.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var Schema = mongoose.Schema; 3 | 4 | var EmployeeScheduleSchema = new Schema({ 5 | emp_id: { 6 | type: String 7 | }, 8 | firstName: { 9 | type: String 10 | }, 11 | lastName: { 12 | type: String 13 | }, 14 | monday: { 15 | type: String, 16 | default: "" 17 | }, 18 | tuesday: { 19 | type: String, 20 | default: "" 21 | }, 22 | wednesday: { 23 | type: String, 24 | default: "" 25 | }, 26 | thursday: { 27 | type: String, 28 | default: "" 29 | }, 30 | friday: { 31 | type: String, 32 | default: "" 33 | }, 34 | saturday:{ 35 | type: String, 36 | default: "" 37 | }, 38 | sunday: { 39 | type: String, 40 | default: "" 41 | }, 42 | active: { 43 | type: Number, 44 | default: 1, 45 | } 46 | }); 47 | 48 | var EmployeeSchedule = mongoose.model('EmployeeSchedule', EmployeeScheduleSchema); 49 | module.exports = EmployeeSchedule; -------------------------------------------------------------------------------- /models/user.js: -------------------------------------------------------------------------------- 1 | var mongoose = require("mongoose"); 2 | var passportLocalMongoose = require("passport-local-mongoose"); 3 | 4 | var UserSchema = new mongoose.Schema({ 5 | username: String, 6 | email: String, 7 | userType: String, 8 | picture: String, 9 | password: String 10 | }); 11 | 12 | UserSchema.plugin(passportLocalMongoose); 13 | 14 | module.exports = mongoose.model("User", UserSchema); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-shift-scheduler", 3 | "version": "1.0.0", 4 | "description": "Employee management system", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node server.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/clsavino/react-shift-scheduler.git" 13 | }, 14 | "author": "HB,NC,AR,CS", 15 | "license": "ISC", 16 | "bugs": { 17 | "url": "https://github.com/clsavino/react-shift-scheduler/issues" 18 | }, 19 | "homepage": "https://github.com/clsavino/react-shift-scheduler#readme", 20 | "dependencies": { 21 | "axios": "^0.15.3", 22 | "bluebird": "^3.4.6", 23 | "body-parser": "^1.15.2", 24 | "dotenv": "^4.0.0", 25 | "express": "^4.14.0", 26 | "express-session": "^1.14.2", 27 | "mongoose": "^4.7.3", 28 | "morgan": "^1.7.0", 29 | "passport": "^0.3.2", 30 | "passport-google-auth": "^1.0.1", 31 | "passport-google-oauth": "^1.0.0", 32 | "passport-linkedin-oauth2": "^1.4.1", 33 | "passport-local": "^1.0.0", 34 | "passport-local-mongoose": "^4.0.0", 35 | "path": "^0.12.7", 36 | "react": "^15.4.1", 37 | "react-dom": "^15.4.1", 38 | "react-router": "^3.0.0" 39 | }, 40 | "devDependencies": { 41 | "babel-core": "^6.3.13", 42 | "babel-loader": "^6.2.0", 43 | "babel-preset-es2015": "^6.3.13", 44 | "babel-preset-react": "^6.3.13", 45 | "webpack": "^1.13.1" 46 | }, 47 | "scripts": { 48 | "build": "webpack", 49 | "start": "node server" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Schedulr - 404 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |

15 |
16 |
17 |

404

18 |

Page not found :(

19 |
20 |
21 |

22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /public/assets/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clsavino/react-shift-scheduler/22af41bfa79d1be807e51c44307b4a781a1c926b/public/assets/images/favicon.ico -------------------------------------------------------------------------------- /public/assets/images/logo-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clsavino/react-shift-scheduler/22af41bfa79d1be807e51c44307b4a781a1c926b/public/assets/images/logo-small.png -------------------------------------------------------------------------------- /public/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clsavino/react-shift-scheduler/22af41bfa79d1be807e51c44307b4a781a1c926b/public/assets/images/logo.png -------------------------------------------------------------------------------- /public/assets/javascript.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function(){ 2 | $(".button-collapse").sideNav(); 3 | $("select").material_select(); 4 | }); -------------------------------------------------------------------------------- /public/assets/style.css: -------------------------------------------------------------------------------- 1 | .brand-logo { 2 | margin-left: 20px; 3 | } 4 | 5 | .brand-logo:hover { 6 | border-bottom: 3px solid #1e88e5; 7 | } 8 | 9 | #logo { 10 | height: 24px; 11 | margin-right: 10px; 12 | } 13 | 14 | .loginButtons { 15 | width: 100%; 16 | } 17 | 18 | #google { 19 | background-color: #ea4335; 20 | } 21 | 22 | #linkedin { 23 | background-color: #0077b5; 24 | } 25 | 26 | #loginForm { 27 | margin-top: 80px; 28 | } 29 | 30 | #noAccount { 31 | margin-top: 20px; 32 | } 33 | 34 | #newEmployee { 35 | cursor: pointer; 36 | } 37 | 38 | #error { 39 | font-size: 175px; 40 | } 41 | 42 | /*#announcementBuilder { 43 | padding: 10px; 44 | }*/ 45 | 46 | .circle-small { 47 | height: 30px; 48 | margin-left: 15px; 49 | margin-bottom: -10px; 50 | } -------------------------------------------------------------------------------- /public/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Schedulr - Not Authorized 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |

15 |
16 |
17 |

warning

18 |

User already registered

19 |
20 |
21 |

22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Schedulr 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /public/notauth.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Schedulr - Not Authorized 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |

15 |
16 |
17 |

warning

18 |

Access Not Authorized :/

19 |
20 |
21 |

22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var express = require("express"); 2 | var dotenv = require("dotenv").config(); 3 | var bodyParser = require("body-parser"); 4 | var logger = require("morgan"); 5 | var passport = require("passport"); 6 | var LocalStrategy= require("passport-local"); 7 | var passportLocalMongoose = require("passport-local-mongoose"); 8 | var GoogleStrategy = require("passport-google-oauth").OAuth2Strategy; 9 | var LinkedInStrategy = require("passport-linkedin-oauth2").Strategy; 10 | var path = require("path"); 11 | var db = require("./db/db.js") 12 | var User = require("./models/user") 13 | 14 | //Initialize Express 15 | var app = express(); 16 | var PORT = process.env.PORT || 8080; 17 | 18 | //Express session 19 | app.use(require("express-session")({ 20 | secret: "This is our secret session 2016 for users!", 21 | resave: false, 22 | saveUninitialized: false 23 | })); 24 | 25 | //Passport 26 | app.use(passport.initialize()); 27 | app.use(passport.session()); 28 | passport.use(new LocalStrategy(User.authenticate())); 29 | passport.serializeUser(function(user,done) { 30 | done(null, user.id); 31 | }); 32 | 33 | passport.deserializeUser(function(id,done) { 34 | User.findById(id, function (err,user) { 35 | done(err,user); 36 | }); 37 | }); 38 | 39 | //Body-Parser 40 | app.use(logger("dev")); 41 | app.use(bodyParser.json()); 42 | app.use(bodyParser.urlencoded({ extended: true })); 43 | app.use(bodyParser.text()); 44 | app.use(bodyParser.json({ type: "application/vnd.api+json" })); 45 | 46 | //Landing 47 | app.get("/", autoRedirect, function(req, res){ 48 | res.sendFile(path.resolve(__dirname, "public", "index.html")); 49 | }); 50 | 51 | //Public files