Commit d0099dd8829621efd696b2820ac21ec7bc2cf36c
1 parent
13ef5ba8c6
Exists in
master
student detail page view
Showing
18 changed files
with
406 additions
and
130 deletions
Show diff stats
client/main.html
1 | <head> | 1 | <head> |
2 | <meta charset="utf-8"> | 2 | <meta charset="utf-8"> |
3 | <title>YoungDesk</title> | 3 | <title>YoungDesk</title> |
4 | <meta name="description" content="All your organization's need."> | 4 | <meta name="description" content="All your organization's need."> |
5 | <meta name="viewport" content="initial-scale=1, minimal-ui, maximum-scale=1, minimum-scale=1" /> | 5 | <meta name="viewport" content="initial-scale=1, minimal-ui, maximum-scale=1, minimum-scale=1" /> |
6 | <link rel="shortcut icon" type="image/png" href="/favicon.png?v1" sizes="16x16 32x32 64x64"> | 6 | <link rel="shortcut icon" type="image/png" href="/favicon.png?v1" sizes="16x16 32x32 64x64"> |
7 | <link rel="apple-touch-icon" sizes="120x120" href="/apple-touch-icon-precomposed.png"> | 7 | <link rel="apple-touch-icon" sizes="120x120" href="/apple-touch-icon-precomposed.png"> |
8 | <!-- Latest compiled and minified CSS --> | ||
9 | <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/latest/css/bootstrap.min.css"> | ||
10 | |||
8 | <link | 11 | <link |
9 | href='https://fonts.googleapis.com/css?family=Alef' | 12 | href='https://fonts.googleapis.com/css?family=Alef' |
10 | rel='stylesheet' | 13 | rel='stylesheet' |
11 | type='text/css' | 14 | type='text/css' |
12 | /> | 15 | /> |
13 | <link | 16 | <link |
14 | href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css" | 17 | href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css" |
15 | rel="stylesheet" | 18 | rel="stylesheet" |
16 | integrity="sha384-T8Gy5hrqNKT+hzMclPo118YTQO6cYprQmhrYwIiQ/3axmI1hQomh7Ud2hPOy8SP1" | 19 | integrity="sha384-T8Gy5hrqNKT+hzMclPo118YTQO6cYprQmhrYwIiQ/3axmI1hQomh7Ud2hPOy8SP1" |
17 | crossorigin="anonymous" | 20 | crossorigin="anonymous" |
18 | /> | 21 | /> |
19 | </head> | 22 | </head> |
20 | 23 | ||
21 | <body> | 24 | <body> |
22 | <div id="app"></div> | 25 | <div id="app"></div> |
23 | </body> | 26 | </body> |
24 | 27 |
imports/client/app/routes.js
1 | /* eslint-disable max-len */ | 1 | /* eslint-disable max-len */ |
2 | 2 | ||
3 | import React from 'react'; | 3 | import React from 'react'; |
4 | import { render } from 'react-dom'; | 4 | import { render } from 'react-dom'; |
5 | import { Router, Route, | 5 | import { Router, Route, |
6 | IndexRoute, browserHistory } from 'react-router'; | 6 | IndexRoute, browserHistory } from 'react-router'; |
7 | import { Meteor } from 'meteor/meteor'; | 7 | import { Meteor } from 'meteor/meteor'; |
8 | 8 | ||
9 | /** | 9 | /** |
10 | * General Components | 10 | * General Components |
11 | */ | 11 | */ |
12 | import Index from '/imports/client/views/app/module/Index'; | 12 | import Index from '/imports/client/views/app/module/Index'; |
13 | 13 | ||
14 | /** | 14 | /** |
15 | * Org Components | 15 | * Org Components |
16 | */ | 16 | */ |
17 | 17 | ||
18 | import { App } from '/imports/client/layouts/OrgApp'; | 18 | import { App } from '/imports/client/layouts/OrgApp'; |
19 | import { AppModule } from '/imports/client/views/org/app/module/Index'; | 19 | import { AppModule } from '/imports/client/views/org/app/module/Index'; |
20 | import { Orgs } from '/imports/collections/orgs/index'; | 20 | import { Orgs } from '/imports/collections/orgs/index'; |
21 | import { importCsvController } from '/imports/client/views/org/importCsv/index' | 21 | import { importCsvController } from '/imports/client/views/org/importCsv/index' |
22 | 22 | ||
23 | //admin | ||
24 | import { StudentDataController } from '/imports/client/views/org/admin/students/index' | ||
25 | |||
23 | 26 | ||
24 | import NotFound from '/imports/client/views/org/NotFound'; | 27 | import NotFound from '/imports/client/views/org/NotFound'; |
25 | 28 | ||
26 | /** | 29 | /** |
27 | * NonOrg Components | 30 | * NonOrg Components |
28 | */ | 31 | */ |
29 | import Signup from '/imports/client/views/nonOrg/enter/SignupView'; | 32 | import Signup from '/imports/client/views/nonOrg/enter/SignupView'; |
30 | 33 | ||
31 | /** | 34 | /** |
32 | * Invalid Org Components | 35 | * Invalid Org Components |
33 | */ | 36 | */ |
34 | 37 | ||
35 | const authenticate = (nextState, replace) => { | 38 | const authenticate = (nextState, replace) => { |
36 | if (!Meteor.loggingIn() && !Meteor.userId()) { | 39 | if (!Meteor.loggingIn() && !Meteor.userId()) { |
37 | replace({ | 40 | replace({ |
38 | pathname: '/login', | 41 | pathname: '/login', |
39 | state: { nextPathname: nextState.location.pathname }, | 42 | state: { nextPathname: nextState.location.pathname }, |
40 | }); | 43 | }); |
41 | } | 44 | } |
42 | }; | 45 | }; |
43 | 46 | ||
44 | 47 | ||
45 | const detectOrg = () => { | 48 | const detectOrg = () => { |
46 | orgSlug = ""; | 49 | orgSlug = ""; |
47 | var hostnameArray = document.location.hostname.split( "." ); | 50 | var hostnameArray = document.location.hostname.split( "." ); |
48 | if(hostnameArray[1]=='localhost'){ | 51 | if(hostnameArray[1]=='localhost'){ |
49 | orgSlug = hostnameArray[0]; | 52 | orgSlug = hostnameArray[0]; |
50 | } | 53 | } |
51 | if(orgSlug!=""){ | 54 | if(orgSlug!=""){ |
52 | Meteor.call('checkExistingOrg', {slug:orgSlug}, function(err, res) { | 55 | Meteor.call('checkExistingOrg', {slug:orgSlug}, function(err, res) { |
53 | if(res){ | 56 | if(res){ |
54 | Session.set('orgId', res._id); | 57 | Session.set('orgId', res._id); |
55 | Session.set('orgSlug', orgSlug); | 58 | Session.set('orgSlug', orgSlug); |
56 | render(getOrgRoutes(),document.getElementById('app')); | 59 | render(getOrgRoutes(),document.getElementById('app')); |
57 | }else{ | 60 | }else{ |
58 | render(getInvalidOrgRoute(),document.getElementById('app')); | 61 | render(getInvalidOrgRoute(),document.getElementById('app')); |
59 | } | 62 | } |
60 | }); | 63 | }); |
61 | }else{ | 64 | }else{ |
62 | render(getNonOrgRoutes(),document.getElementById('app')); | 65 | render(getNonOrgRoutes(),document.getElementById('app')); |
63 | } | 66 | } |
64 | } | 67 | } |
65 | const checkSlug = (nextState, replace) => { | 68 | const checkSlug = (nextState, replace) => { |
66 | orgId = Session.get('orgId'); | 69 | orgId = Session.get('orgId'); |
67 | } | 70 | } |
68 | 71 | ||
69 | /** | 72 | /** |
70 | There are three types of routes | 73 | There are three types of routes |
71 | 1)getOrgRoutes: all the routes that should be present for a registered org | 74 | 1)getOrgRoutes: all the routes that should be present for a registered org |
72 | 2)getInvalidOrgRoute: all the routes where someone tries to enter a subdomain which hasn't been registered yet (404 mostly :D) | 75 | 2)getInvalidOrgRoute: all the routes where someone tries to enter a subdomain which hasn't been registered yet (404 mostly :D) |
73 | 3)getNonOrgRoutes: all routes linked to normal site, ie signing up a new org. CHeking out demo and everything internal | 76 | 3)getNonOrgRoutes: all routes linked to normal site, ie signing up a new org. CHeking out demo and everything internal |
74 | **/ | 77 | **/ |
75 | const getOrgRoutes = () => ( | 78 | const getOrgRoutes = () => ( |
76 | <Router history={ browserHistory }> | 79 | <Router history={ browserHistory }> |
77 | <Route path="/" component={ App }> | 80 | <Route path="/" component={ App }> |
78 | <IndexRoute name="index" component={ AppModule } /> | 81 | <IndexRoute name="index" component={ AppModule } /> |
79 | <Route name="import" path="/import" component={ importCsvController } /> | 82 | <Route name="import" path="/import" component={ importCsvController } /> |
83 | <Route name="student" path="/students" component={ StudentDataController } /> | ||
80 | <Route path="*" component={ NotFound } /> | 84 | <Route path="*" component={ NotFound } /> |
81 | </Route> | 85 | </Route> |
82 | </Router> | 86 | </Router> |
83 | ) | 87 | ) |
84 | 88 | ||
85 | 89 | ||
86 | const getInvalidOrgRoute = () => ( | 90 | const getInvalidOrgRoute = () => ( |
87 | <Router history={ browserHistory }> | 91 | <Router history={ browserHistory }> |
88 | <Route path="/" component={ App }> | 92 | <Route path="/" component={ App }> |
89 | <IndexRoute name="index" component={ NotFound } /> | 93 | <IndexRoute name="index" component={ NotFound } /> |
90 | <Route path="*" component={ NotFound } /> | 94 | <Route path="*" component={ NotFound } /> |
91 | </Route> | 95 | </Route> |
92 | </Router> | 96 | </Router> |
93 | ) | 97 | ) |
94 | 98 | ||
95 | const getNonOrgRoutes = () => ( | 99 | const getNonOrgRoutes = () => ( |
96 | <Router history={ browserHistory }> | 100 | <Router history={ browserHistory }> |
97 | <Route path="/" component={ App }> | 101 | <Route path="/" component={ App }> |
98 | <IndexRoute name="index" component={ Index } /> | 102 | <IndexRoute name="index" component={ Index } /> |
99 | <Route name="signup" path="/signup" component={ Signup } /> | 103 | <Route name="signup" path="/signup" component={ Signup } /> |
100 | <Route path="*" component={ NotFound } /> | 104 | <Route path="*" component={ NotFound } /> |
101 | </Route> | 105 | </Route> |
102 | </Router> | 106 | </Router> |
103 | ) | 107 | ) |
104 | 108 | ||
105 | 109 | ||
106 | Meteor.startup(() => { | 110 | Meteor.startup(() => { |
107 | detectOrg(); | 111 | detectOrg(); |
108 | }); | 112 | }); |
109 | 113 |
imports/client/views/etc/index.js
1 | // import { InviteSignupController } from '/imports/client/views/invite/signup/index' | 1 | // import { InviteSignupController } from '/imports/client/views/invite/signup/index' |
2 | import _ from 'lodash'; | 2 | import _ from 'lodash'; |
3 | import { | 3 | import { |
4 | composeWithTracker, | 4 | composeWithTracker, |
5 | compose, | 5 | compose, |
6 | composeAll | 6 | composeAll |
7 | } from 'react-komposer'; | 7 | } from 'react-komposer'; |
8 | import { Loading } from '/imports/client/components/Loading'; | 8 | import { Loading } from '/imports/client/components/Loading'; |
9 | 9 | ||
10 | import { Orgs } from '/imports/collections/orgs/index'; | 10 | import { Orgs } from '/imports/collections/orgs/index'; |
11 | import { Users } from '/imports/collections/users/index'; | 11 | import { Users } from '/imports/collections/users/index'; |
12 | 12 | ||
13 | const meteorTick = (props, onData) => { | 13 | const meteorTick = (props, onData) => { |
14 | 14 | ||
15 | const handles = [ | 15 | const handles = [ |
16 | Meteor.subscribe('users.current') | 16 | Meteor.subscribe('users.current'), |
17 | Meteor.subscribe('orgs.current') | ||
17 | ]; | 18 | ]; |
18 | 19 | ||
19 | if(_.every(handles, (handle) => (handle.ready()) )) { | 20 | if(_.every(handles, (handle) => (handle.ready()) )) { |
20 | const user = Users.current(); | 21 | const user = Users.current(); |
22 | const org = Orgs.current(); | ||
21 | onData(null, { | 23 | onData(null, { |
22 | data: { | 24 | data: { |
23 | user: user, | 25 | user: user, |
26 | org: org | ||
24 | }, | 27 | }, |
25 | }); | 28 | }); |
26 | } | 29 | } |
27 | 30 | ||
28 | return () => { | 31 | return () => { |
29 | _.each(handles, (handle) => handle.stop() ); | 32 | _.each(handles, (handle) => handle.stop() ); |
30 | }; | 33 | }; |
31 | }; | 34 | }; |
32 | 35 | ||
33 | 36 | ||
34 | const reduxTick = (props, onData) => { | 37 | const reduxTick = (props, onData) => { |
35 | onData(null, { | 38 | onData(null, { |
36 | data: {} | 39 | data: {} |
37 | }); | 40 | }); |
38 | }; | 41 | }; |
39 | 42 | ||
40 | 43 | ||
41 | export const InviteSignupController = composeAll( | 44 | export const InviteSignupController = composeAll( |
42 | composeWithTracker(meteorTick, Loading), | 45 | composeWithTracker(meteorTick, Loading), |
43 | compose(reduxTick, Loading), | 46 | compose(reduxTick, Loading), |
44 | )(InviteSignupView); | 47 | )(InviteSignupView); |
45 | 48 |
imports/client/views/etc/signup.js
1 | import _ from 'lodash'; | 1 | import _ from 'lodash'; |
2 | import { Meteor } from 'meteor/meteor'; | 2 | import { Meteor } from 'meteor/meteor'; |
3 | 3 | ||
4 | import React, { Component } from 'react'; | 4 | import React, { Component } from 'react'; |
5 | import { Link,browserHistory } from 'react-router'; | 5 | import { Link,browserHistory } from 'react-router'; |
6 | import { FormGroup,, | 6 | import { FormGroup,, |
7 | FormControl,Glyphicon,Button } from 'react-bootstrap'; | 7 | FormControl,Glyphicon,Button } from 'react-bootstrap'; |
8 | 8 | ||
9 | 9 | ||
10 | export class InviteSignupView extends Component { | 10 | export class InviteSignupView extends Component { |
11 | 11 | ||
12 | constructor(props) { | 12 | constructor(props) { |
13 | super(props); | 13 | super(props); |
14 | this.state = { | 14 | this.state = { |
15 | 15 | ||
16 | }; | 16 | }; |
17 | this.onUpdate = this.onUpdate.bind(this); | 17 | this.onUpdate = this.onUpdate.bind(this); |
18 | }; | 18 | }; |
19 | 19 | ||
20 | onUpdate(key, value) { | 20 | onUpdate(key, value) { |
21 | this.setState({[key]: value}); | 21 | this.setState({[key]: value}); |
22 | }; | 22 | }; |
23 | 23 | ||
24 | render() { | 24 | render() { |
25 | const {user, org} = this.props.data; | ||
25 | return ( | 26 | return ( |
26 | <div className = "enterPane-box"> | 27 | <div className = "enterPane-box"> |
27 | 28 | ||
28 | </div> | 29 | </div> |
29 | ); | 30 | ); |
30 | }; | 31 | }; |
31 | 32 | ||
32 | }; | 33 | }; |
33 | 34 |
imports/client/views/org/admin/students/StudentDataView.js
File was created | 1 | import _ from 'lodash'; | |
2 | import { Meteor } from 'meteor/meteor'; | ||
3 | |||
4 | import React, { Component } from 'react'; | ||
5 | import { Link,browserHistory } from 'react-router'; | ||
6 | import { FormGroup,Panel,Table, | ||
7 | ButtonToolbar,Modal, | ||
8 | FormControl,Glyphicon,Button } from 'react-bootstrap'; | ||
9 | import { AddStudentForm } from './addStudentForm'; | ||
10 | |||
11 | export class StudentDataView extends Component { | ||
12 | |||
13 | constructor(props) { | ||
14 | super(props); | ||
15 | this.state = { | ||
16 | show: false | ||
17 | }; | ||
18 | this.showModal = this.showModal.bind(this); | ||
19 | this.hideModal = this.hideModal.bind(this); | ||
20 | this.onUpdate = this.onUpdate.bind(this); | ||
21 | }; | ||
22 | |||
23 | showModal() { | ||
24 | this.setState({show: true}); | ||
25 | } | ||
26 | |||
27 | hideModal() { | ||
28 | this.setState({show: false}); | ||
29 | } | ||
30 | onUpdate(key, value) { | ||
31 | this.setState({[key]: value}); | ||
32 | }; | ||
33 | |||
34 | render() { | ||
35 | return ( | ||
36 | <div className = "enterPane-box"> | ||
37 | <Panel header="Students Info" bsStyle="primary"> | ||
38 | <Table striped bordered condensed hover> | ||
39 | <thead> | ||
40 | <tr> | ||
41 | <th>#</th> | ||
42 | <th>First Name</th> | ||
43 | <th>Last Name</th> | ||
44 | </tr> | ||
45 | </thead> | ||
46 | <tbody> | ||
47 | { | ||
48 | this.props.data.students.map(function(student, i) | ||
49 | { | ||
50 | return( | ||
51 | <tr> | ||
52 | <td>{i+1}</td> | ||
53 | <td>{student.firstName}</td> | ||
54 | <td>{student.lastName}</td> | ||
55 | </tr> | ||
56 | ) | ||
57 | }) | ||
58 | } | ||
59 | |||
60 | </tbody> | ||
61 | </Table> | ||
62 | <ButtonToolbar> | ||
63 | <Button bsStyle="primary" onClick={this.showModal}> | ||
64 | Add Student | ||
65 | </Button> | ||
66 | <Modal | ||
67 | {...this.props} | ||
68 | show={this.state.show} | ||
69 | onHide={this.hideModal} | ||
70 | dialogClassName="custom-modal" | ||
71 | > | ||
72 | <Modal.Header closeButton> | ||
73 | <Modal.Title id="contained-modal-title-lg">New Student</Modal.Title> | ||
74 | </Modal.Header> | ||
75 | <Modal.Body> | ||
76 | <AddStudentForm /> | ||
77 | </Modal.Body> | ||
78 | <Modal.Footer> | ||
79 | <Button onClick={this.hideModal}>Close</Button> | ||
80 | </Modal.Footer> | ||
81 | </Modal> | ||
82 | </ButtonToolbar> | ||
83 | </Panel> | ||
84 | </div> | ||
85 | ); | ||
86 | }; | ||
87 | |||
88 | }; | ||
89 |
imports/client/views/org/admin/students/addStudentForm.js
File was created | 1 | import _ from 'lodash'; | |
2 | import { Meteor } from 'meteor/meteor'; | ||
3 | |||
4 | import React, { Component } from 'react'; | ||
5 | import { Link,browserHistory } from 'react-router'; | ||
6 | import { FormGroup,InputGroup, | ||
7 | DropdownButton,MenuItem,ControlLabel, | ||
8 | SplitButton, | ||
9 | FormControl,Glyphicon,Button } from 'react-bootstrap'; | ||
10 | import {DatePicker} from 'react-bootstrap-date-picker' | ||
11 | import {addStudentManually} from '/imports/collections/students/methods'; | ||
12 | |||
13 | export class AddStudentForm extends Component { | ||
14 | |||
15 | constructor(props) { | ||
16 | super(props); | ||
17 | this.state = { | ||
18 | firstName: "", | ||
19 | lastName: "", | ||
20 | middleName: "", | ||
21 | dob: "", | ||
22 | }; | ||
23 | this.onUpdate = this.onUpdate.bind(this); | ||
24 | this.aFunction = this.aFunction.bind(this); | ||
25 | }; | ||
26 | |||
27 | onUpdate(key, value) { | ||
28 | this.setState({[key]: value}); | ||
29 | }; | ||
30 | aFunction(e){ | ||
31 | console.log(e); | ||
32 | console.log(e); | ||
33 | } | ||
34 | addStudent(e){ | ||
35 | e.preventDefault(); | ||
36 | e.persist(); | ||
37 | const firstName = this.state.firstName; | ||
38 | const middleName = this.state.middleName; | ||
39 | const lastName = this.state.lastName; | ||
40 | if(firstName==""){ | ||
41 | Bert.alert('Enter Fist Name', 'danger'); | ||
42 | } else if(middleName==""){ | ||
43 | Bert.alert('Enter Middle name!', 'danger'); | ||
44 | } else{ | ||
45 | addStudentManually.call({ | ||
46 | firstName: firstName, | ||
47 | middleName: middleName, | ||
48 | lastName: lastName | ||
49 | }, function (error, result) { | ||
50 | console.log(error); | ||
51 | console.log(result); | ||
52 | }); | ||
53 | } | ||
54 | } | ||
55 | render() { | ||
56 | return ( | ||
57 | <form onSubmit={ (e) => this.addStudent(e) }> | ||
58 | <FormGroup controlId="formBasicText"> | ||
59 | <ControlLabel>First Name</ControlLabel> | ||
60 | <FormControl | ||
61 | type="text" | ||
62 | value={this.state.firstName} | ||
63 | placeholder="First Name" | ||
64 | onChange={e=>this.onUpdate('firstName',e.target.value)} | ||
65 | /> | ||
66 | </FormGroup> | ||
67 | <FormGroup controlId="formBasicText"> | ||
68 | <ControlLabel>Middle Name</ControlLabel> | ||
69 | <FormControl | ||
70 | type="text" | ||
71 | value={this.state.middleName} | ||
72 | placeholder="Middle Name" | ||
73 | onChange={e=>this.onUpdate('middleName',e.target.value)} | ||
74 | /> | ||
75 | </FormGroup> | ||
76 | <FormGroup controlId="formBasicText"> | ||
77 | <ControlLabel>Last Name</ControlLabel> | ||
78 | <FormControl | ||
79 | type="text" | ||
80 | value={this.state.lastName} | ||
81 | placeholder="Last Name" | ||
82 | onChange={e=>this.onUpdate('lastName',e.target.value)} | ||
83 | /> | ||
84 | </FormGroup> | ||
85 | <Button type="submit" bsStyle="default">Add Student</Button> | ||
86 | </form> | ||
87 | ); | ||
88 | }; | ||
89 | |||
90 | }; | ||
91 |
imports/client/views/org/admin/students/index.js
File was created | 1 | // import { StudentDataController } from '/imports/client/views/org/admin/students/index' | |
2 | import _ from 'lodash'; | ||
3 | import { | ||
4 | composeWithTracker, | ||
5 | compose, | ||
6 | composeAll | ||
7 | } from 'react-komposer'; | ||
8 | import { Loading } from '/imports/client/components/Loading'; | ||
9 | |||
10 | import { Orgs } from '/imports/collections/orgs/index'; | ||
11 | import { Users } from '/imports/collections/users/index'; | ||
12 | import { StudentDataView } from './StudentDataView'; | ||
13 | import { Students } from '/imports/collections/students/methods' | ||
14 | |||
15 | |||
16 | const meteorTick = (props, onData) => { | ||
17 | |||
18 | const handles = [ | ||
19 | Meteor.subscribe('users.current'), | ||
20 | Meteor.subscribe('orgs.current'), | ||
21 | Meteor.subscribe('users.forMyOrg') | ||
22 | ]; | ||
23 | |||
24 | if(_.every(handles, (handle) => (handle.ready()) )) { | ||
25 | const user = Users.current(); | ||
26 | const org = Orgs.current(); | ||
27 | const students = Users.find({"role":"STUDENT"}).fetch(); | ||
28 | console.log(students); | ||
29 | onData(null, { | ||
30 | data: { | ||
31 | user: user, | ||
32 | org: org, | ||
33 | students:students | ||
34 | }, | ||
35 | }); | ||
36 | } | ||
37 | |||
38 | return () => { | ||
39 | _.each(handles, (handle) => handle.stop() ); | ||
40 | }; | ||
41 | }; | ||
42 | |||
43 | |||
44 | const reduxTick = (props, onData) => { | ||
45 | onData(null, { | ||
46 | data: {} | ||
47 | }); | ||
48 | }; | ||
49 | |||
50 | |||
51 | export const StudentDataController = composeAll( | ||
52 | composeWithTracker(meteorTick, Loading), | ||
53 | compose(reduxTick, Loading), | ||
54 | )(StudentDataView); | ||
55 |
imports/client/views/org/app/module/navigation/AuthenticatedNavigation.js
1 | import React, { Component } from 'react'; | 1 | import React, { Component } from 'react'; |
2 | import { browserHistory } from 'react-router'; | 2 | import { browserHistory } from 'react-router'; |
3 | import { LinkContainer } from 'react-router-bootstrap'; | 3 | import { LinkContainer } from 'react-router-bootstrap'; |
4 | import { logout } from '/imports/client/app/utils/loginMethods'; | ||
4 | import { Navbar,Modal, Nav, NavItem, | 5 | import { Navbar,Modal, Nav, NavItem, |
5 | Glyphicon, | 6 | Glyphicon, |
6 | NavDropdown, MenuItem } from 'react-bootstrap'; | 7 | NavDropdown, MenuItem } from 'react-bootstrap'; |
7 | import { Meteor } from 'meteor/meteor'; | 8 | import { Meteor } from 'meteor/meteor'; |
8 | 9 | ||
9 | const handleLogout = () => Meteor.logout(() => browserHistory.push('/login')); | 10 | const handleLogout = () => Meteor.logout(() => browserHistory.push('/login')); |
10 | 11 | ||
11 | export class AuthenticatedNavigation extends Component { | 12 | export class AuthenticatedNavigation extends Component { |
12 | constructor(props) { | 13 | constructor(props) { |
13 | super(props); | 14 | super(props); |
14 | this.state = { | 15 | this.state = { |
15 | 16 | ||
16 | }; | 17 | }; |
17 | }; | 18 | }; |
18 | render(){ | 19 | render(){ |
19 | const {user} = this.props.data; | 20 | const {user, org} = this.props.data; |
20 | return( | 21 | return( |
21 | <Navbar inverse> | 22 | <Navbar inverse collapseOnSelect className="bg-crimson"> |
22 | <Navbar.Header> | 23 | <Navbar.Header> |
23 | <Navbar.Brand> | 24 | <Navbar.Brand> |
24 | <Glyphicon glyph="star" /> | 25 | <a href="#"><Glyphicon glyph="link" />{org.name} </a> |
25 | <a href="#">YOUNGDESK</a> | ||
26 | </Navbar.Brand> | 26 | </Navbar.Brand> |
27 | <Navbar.Toggle /> | ||
27 | </Navbar.Header> | 28 | </Navbar.Header> |
28 | <Nav> | 29 | <Navbar.Collapse> |
29 | <NavItem eventKey={1} href="#">Link</NavItem> | 30 | |
30 | <NavItem eventKey={2} href="#">Link</NavItem> | 31 | <Nav pullRight> |
31 | <NavDropdown eventKey={3} title="Dropdown" id="basic-nav-dropdown"> | 32 | <NavItem eventKey={1} href="#"> |
32 | <MenuItem eventKey={3.1}>Action</MenuItem> | 33 | Hello {user.getFullName()} |
33 | <MenuItem eventKey={3.2}>Another action</MenuItem> | 34 | </NavItem> |
34 | <MenuItem eventKey={3.3}>Something else here</MenuItem> | 35 | <NavItem eventKey={2} href="#"> |
35 | <MenuItem divider /> | 36 | <div |
36 | <MenuItem eventKey={3.4}>Separated link</MenuItem> | 37 | className = "appLayout-topItem" |
37 | </NavDropdown> | 38 | onClick = {() => logout()} |
39 | > | ||
40 | logout | ||
41 | </div> | ||
42 | </NavItem> | ||
38 | </Nav> | 43 | </Nav> |
39 | </Navbar> | 44 | </Navbar.Collapse> |
45 | </Navbar> | ||
40 | ); | 46 | ); |
41 | } | 47 | } |
42 | } | 48 | } |
imports/client/views/org/app/module/navigation/index.js
1 | // import { InviteSignupController } from '/imports/client/views/invite/signup/index' | 1 | // import { InviteSignupController } from '/imports/client/views/invite/signup/index' |
2 | import _ from 'lodash'; | 2 | import _ from 'lodash'; |
3 | import { | 3 | import { |
4 | composeWithTracker, | 4 | composeWithTracker, |
5 | compose, | 5 | compose, |
6 | composeAll | 6 | composeAll |
7 | } from 'react-komposer'; | 7 | } from 'react-komposer'; |
8 | import { Loading } from '/imports/client/components/Loading'; | 8 | import { Loading } from '/imports/client/components/Loading'; |
9 | 9 | ||
10 | import { Orgs } from '/imports/collections/orgs/index'; | 10 | import { Orgs } from '/imports/collections/orgs/index'; |
11 | import { Users } from '/imports/collections/users/index'; | 11 | import { Users } from '/imports/collections/users/index'; |
12 | 12 | ||
13 | import { AppNavigation } from './AppNavigation'; | 13 | import { AppNavigation } from './AppNavigation'; |
14 | 14 | ||
15 | const meteorTick = (props, onData) => { | 15 | const meteorTick = (props, onData) => { |
16 | 16 | ||
17 | const handles = [ | 17 | const handles = [ |
18 | Meteor.subscribe('users.current') | 18 | Meteor.subscribe('users.current'), |
19 | Meteor.subscribe('orgs.current') | ||
19 | ]; | 20 | ]; |
20 | 21 | ||
21 | if(_.every(handles, (handle) => (handle.ready()) )) { | 22 | if(_.every(handles, (handle) => (handle.ready()) )) { |
22 | const user = Users.current(); | 23 | const user = Users.current(); |
24 | const org = Orgs.current(); | ||
23 | onData(null, { | 25 | onData(null, { |
24 | data: { | 26 | data: { |
25 | user: user, | 27 | user: user, |
28 | org: org | ||
26 | }, | 29 | }, |
27 | }); | 30 | }); |
28 | } | 31 | } |
29 | 32 | ||
30 | return () => { | 33 | return () => { |
31 | _.each(handles, (handle) => handle.stop() ); | 34 | _.each(handles, (handle) => handle.stop() ); |
32 | }; | 35 | }; |
33 | }; | 36 | }; |
34 | 37 | ||
35 | 38 | ||
36 | const reduxTick = (props, onData) => { | 39 | const reduxTick = (props, onData) => { |
37 | onData(null, { | 40 | onData(null, { |
38 | data: {} | 41 | data: {} |
39 | }); | 42 | }); |
40 | }; | 43 | }; |
41 | 44 | ||
42 | 45 | ||
43 | export const AppNavigationController = composeAll( | 46 | export const AppNavigationController = composeAll( |
44 | composeWithTracker(meteorTick, Loading), | 47 | composeWithTracker(meteorTick, Loading), |
45 | compose(reduxTick, Loading), | 48 | compose(reduxTick, Loading), |
46 | )(AppNavigation); | 49 | )(AppNavigation); |
47 | 50 |
imports/collections/students/index.js
1 | // import { Users } from '/imports/collections/users/index'; | 1 | // import {Students } from '/imports/collections/students/methods' |
2 | // import { Users } from '/imports/collections/users/index'; | ||
3 | 2 | ||
4 | import _ from 'lodash'; | 3 | import _ from 'lodash'; |
5 | import { Meteor } from 'meteor/meteor'; | 4 | import { Meteor } from 'meteor/meteor'; |
6 | import { Mongo } from 'meteor/mongo'; | 5 | import { Mongo } from 'meteor/mongo'; |
7 | import { SimpleSchema } from 'meteor/aldeed:simple-schema'; | 6 | import { SimpleSchema } from 'meteor/aldeed:simple-schema'; |
8 | 7 | ||
9 | import { Orgs } from '/imports/collections/orgs/index'; | 8 | import { Orgs } from '/imports/collections/orgs/index'; |
9 | import { Users } from '/imports/collections/users/index'; | ||
10 | 10 | ||
11 | class User { | 11 | class Student { |
12 | |||
13 | constructor(doc) { | 12 | constructor(doc) { |
14 | _.assign(this, doc); | 13 | _.assign(this, doc); |
15 | }; | 14 | }; |
16 | 15 | ||
17 | isEmailVerified() { | 16 | getUserIds() { |
18 | return !! _.find(this.emails, x => x.verified); | 17 | return _.filter(_.map(this.users, 'userId')); |
19 | }; | ||
20 | |||
21 | isPhoneVerified() { | ||
22 | return !! _.find(this.phones, x => x.verified); | ||
23 | }; | ||
24 | |||
25 | isIdentityVerified() { | ||
26 | return !! _.find(this.identities, x => x.verified); | ||
27 | }; | ||
28 | |||
29 | getRole() { | ||
30 | const org = Orgs.findOne({_id: this.orgId}); | ||
31 | if(!org) return null; | ||
32 | const connection = _.find(org.users, {userId: this._id}); | ||
33 | if(!connection) return null; | ||
34 | return connection.role; | ||
35 | }; | ||
36 | |||
37 | getFullName() { | ||
38 | return `${this.firstName} ${this.lastName}`; | ||
39 | }; | ||
40 | getFirstName() { | ||
41 | return `${this.firstName}`; | ||
42 | }; | ||
43 | getLastName() { | ||
44 | return `${this.lastName}`; | ||
45 | }; | ||
46 | getEmail() { | ||
47 | return `${this.emails[0].address}`; | ||
48 | }; | ||
49 | getOrganization(){ | ||
50 | return `${this.orgId}`; | ||
51 | }; | ||
52 | |||
53 | getNameByUserId(userId){ | ||
54 | var user = Users.findOne({"_id":userId}); | ||
55 | return user.firstName + " " + user.lastName; | ||
56 | } | ||
57 | getAvatarUrl() { | ||
58 | let random = parseInt(this._id.substr(0, 4), 36); | ||
59 | random = '' + (random % 32); | ||
60 | while(random.length < 3) random = '0' + random; | ||
61 | return `/files/random/random${ random }.png`; | ||
62 | }; | 18 | }; |
63 | }; | 19 | }; |
64 | 20 | export { Student }; | |
65 | export { User }; | 21 | |
66 | 22 | class StudentsCollection extends Mongo.Collection { | |
67 | 23 | insert(item, callback) { | |
68 | const transform = function(doc) { | 24 | _.assign(item, { |
69 | return new User(doc); | 25 | createdAt: new Date().getTime(), |
26 | }); | ||
27 | return super.insert(item, callback); | ||
28 | }; | ||
70 | }; | 29 | }; |
71 | 30 | ||
72 | export const Users = { | 31 | export const Students = new StudentsCollection('Students', { |
73 | 32 | transform: (item) => { | |
74 | current: function() { | 33 | return new Student(item); |
75 | return Meteor.users.findOne({_id: Meteor.userId()}, _.extend({transform: transform})); | ||
76 | }, | 34 | }, |
35 | }); | ||
77 | 36 | ||
78 | find: function(selector, options) { | 37 | _.assign(Students, { |
79 | return Meteor.users.find(selector || {}, _.extend({transform: transform}, options)); | 38 | allStudents: () => { |
39 | const user = Users.current(); | ||
40 | if(!user) return null; | ||
41 | return Orgs.find({'users.userId': user._id}); | ||
80 | }, | 42 | }, |
81 | 43 | current: () => { | |
82 | findOne: function(selector, options) { | 44 | const user = Users.current(); |
83 | return Meteor.users.findOne(selector || {}, _.extend({transform: transform}, options)); | 45 | if(!user) return null; |
46 | return Orgs.findOne({_id: user.orgId}); | ||
47 | }, | ||
48 | currentOrgUsers: () => { | ||
49 | const OrgsArr = Orgs.current(); | ||
50 | if(!OrgsArr) return null; | ||
51 | return OrgsArr.users; | ||
84 | }, | 52 | }, |
85 | 53 | ||
86 | insert: _.bind(Meteor.users.insert, Meteor.users), | 54 | }); |
87 | update: _.bind(Meteor.users.update, Meteor.users), | ||
88 | remove: _.bind(Meteor.users.remove, Meteor.users), | ||
89 | allow: _.bind(Meteor.users.allow, Meteor.users), | ||
90 | deny: _.bind(Meteor.users.deny, Meteor.users), | ||
91 | attachSchema: _.bind(Meteor.users.attachSchema, Meteor.users), | ||
92 | |||
93 | }; | ||
94 | |||
95 | 55 | ||
96 | Users.deny({ | 56 | Students.deny({ |
97 | insert() { return true; }, | 57 | insert() { return true; }, |
98 | update() { return true; }, | 58 | update() { return true; }, |
99 | remove() { return true; }, | 59 | remove() { return true; }, |
100 | }); | 60 | }); |
101 | 61 | ||
102 | Users.roles = { | ||
103 | 'STUDENT': 'STUDENT', | ||
104 | 'TEACHER': 'TEACHER', | ||
105 | 'ADMIN': 'ADMIN', | ||
106 | 'PARENT': 'PARENT' | ||
107 | }; | ||
108 | 62 | ||
109 | 63 | Students.schema = new SimpleSchema({ | |
110 | Users.schema = new SimpleSchema({ | 64 | userId: { type: String }, |
111 | roles: { type: String }, | ||
112 | orgId: { type: String }, | 65 | orgId: { type: String }, |
113 | admissionId: { type: String, optional: true }, | 66 | admissionId: { type: String, optional: true }, |
114 | enrollmentDate: { type: String, optional: true }, | 67 | enrollmentDate: { type: String, optional: true }, |
115 | address: { type: String, optional: true }, | 68 | address: { type: String, optional: true }, |
116 | prefix: { type: String, optional: true }, | 69 | prefix: { type: String, optional: true }, |
117 | firstName: { type: String, optional: true }, | 70 | firstName: { type: String, optional: true }, |
118 | middlename: { type: String, optional: true }, | 71 | middlename: { type: String, optional: true }, |
119 | lastName: { type: String, optional: true }, | 72 | lastName: { type: String, optional: true }, |
120 | gender: { type: String, optional: true }, | 73 | gender: { type: String, optional: true }, |
121 | dob: { type: String, optional: true }, | 74 | dob: { type: String, optional: true }, |
122 | rollNo: { type: String, optional: true }, | 75 | rollNo: { type: String, optional: true }, |
123 | class: { type: String, optional: true }, | 76 | class: { type: String, optional: true }, |
124 | section: { type: String, optional: true }, | 77 | section: { type: String, optional: true }, |
125 | bloodGroup: { type: String, optional: true }, | 78 | bloodGroup: { type: String, optional: true }, |
126 | community: { type: String, optional: true }, | 79 | community: { type: String, optional: true }, |
127 | nationality: { type: String, optional: true }, | 80 | nationality: { type: String, optional: true }, |
128 | motherTongue: { type: String, optional: true }, | 81 | motherTongue: { type: String, optional: true }, |
129 | motherTongue: { type: String, optional: true }, | 82 | motherTongue: { type: String, optional: true }, |
130 | religion: { type: String, optional: true }, | 83 | religion: { type: String, optional: true }, |
131 | permanentAddress: { | 84 | permanentAddress: { |
132 | type: new SimpleSchema({ | 85 | type: new SimpleSchema({ |
133 | home: { type: String, optional: true }, | 86 | home: { type: String, optional: true }, |
134 | street: { type: String, optional: true }, | 87 | street: { type: String, optional: true }, |
135 | town: { type: String, optional: true }, | 88 | town: { type: String, optional: true }, |
136 | city: { type: String, optional: true }, | 89 | city: { type: String, optional: true }, |
137 | state: { type: String, optional: true }, | 90 | state: { type: String, optional: true }, |
138 | zip: { type: String, optional: true }, | 91 | zip: { type: String, optional: true }, |
139 | }), | 92 | }), |
93 | optional: true | ||
140 | }, | 94 | }, |
141 | parent: { | 95 | parent: { |
142 | type: [new SimpleSchema({ | 96 | type: [new SimpleSchema({ |
143 | id: { type: String, }, | 97 | id: { type: String, }, |
144 | relatinship: { type: Boolean, }, | 98 | relatinship: { type: Boolean, }, |
145 | })], | 99 | })], |
146 | minCount: 1, | 100 | optional: true |
147 | }, | ||
148 | emails: { | ||
149 | type: [new SimpleSchema({ | ||
150 | address: { type: String, }, | ||
151 | verified: { type: Boolean, }, | ||
152 | })], | ||
153 | }, | 101 | }, |
154 | prevInstitute: { | 102 | prevInstitute: { |
155 | type: [new SimpleSchema({ | 103 | type: [new SimpleSchema({ |
156 | name: { type: String, }, | 104 | name: { type: String, }, |
157 | fromYear: { type: Boolean, }, | 105 | fromYear: { type: Boolean, }, |
158 | toYear: { type: Boolean, }, | 106 | toYear: { type: Boolean, }, |
159 | ydId: { type: Boolean, }, | 107 | ydId: { type: Boolean, }, |
160 | })], | 108 | })], |
161 | optional: true | 109 | optional: true |
162 | }, | 110 | }, |
163 | 111 | ||
164 | phones: { | ||
165 | type: [new SimpleSchema({ | ||
166 | country: { type: String, }, | ||
167 | prefix: { type: String, }, | ||
168 | number: { type: String, }, | ||
169 | verified: { type: Boolean, }, | ||
170 | })], | ||
171 | optional: true | ||
172 | }, | ||
173 | |||
174 | services: { | 112 | services: { |
175 | type: Object, | 113 | type: Object, |
176 | optional: true, | 114 | optional: true, |
177 | blackbox: true, | 115 | blackbox: true, |
178 | }, | 116 | }, |
179 | 117 | ||
180 | isMetaUser: { type: Boolean, optional: true }, | 118 | isMetaUser: { type: Boolean, optional: true }, |
181 | 119 | ||
182 | createdAt: { type: Date, autoValue: function(){return new Date();}} | 120 | createdAt: { type: Date, autoValue: function(){return new Date();}} |
183 | 121 | ||
184 | }); | 122 | }); |
185 | 123 | ||
186 | Users.attachSchema(Users.schema); | 124 | Students.attachSchema(Students.schema); |
187 | 125 | ||
188 | Users.privateFields = { | 126 | Students.privateFields = { |
189 | orgId: 1, | 127 | orgId: 1, |
190 | address: 1, | 128 | address: 1, |
191 | 129 | ||
192 | firstName: 1, | 130 | firstName: 1, |
193 | lastName: 1, | 131 | lastName: 1, |
imports/collections/students/methods.js
File was created | 1 | // import { } from '/imports/collections/students/methods'; | |
2 | import _ from 'lodash'; | ||
3 | import { Meteor } from 'meteor/meteor'; | ||
4 | import { ValidatedMethod } from 'meteor/mdg:validated-method'; | ||
5 | import { SimpleSchema } from 'meteor/aldeed:simple-schema'; | ||
6 | import { DDPRateLimiter } from 'meteor/ddp-rate-limiter'; | ||
7 | import { Bert } from 'meteor/themeteorchef:bert'; | ||
8 | import { Users } from '/imports/collections/users/index'; | ||
9 | import { Students } from '/imports/collections/students/index'; | ||
10 | import { Orgs } from '/imports/collections/orgs/index'; | ||
11 | export const studentMethods = new ValidatedMethod({ | ||
12 | name: 'student.method', | ||
13 | |||
14 | validate: new SimpleSchema({ | ||
15 | itemId: { type: String }, | ||
16 | }).validator(), | ||
17 | |||
18 | run({itemId}) { | ||
19 | return {}; | ||
20 | }, | ||
21 | |||
22 | }); | ||
23 | |||
24 | export const addStudentManually = new ValidatedMethod({ | ||
25 | name: 'student.addManually', | ||
26 | |||
27 | validate: new SimpleSchema({ | ||
28 | firstName: { type: String }, | ||
29 | middleName: { type: String }, | ||
30 | lastName: { type: String }, | ||
31 | }).validator(), | ||
32 | |||
33 | run({firstName,middleName,lastName}) { | ||
34 | console.log(firstName); | ||
35 | console.log(middleName); | ||
36 | console.log(lastName); | ||
37 | const user = Users.findOne({_id: this.userId}); | ||
38 | orgId = user.orgId; | ||
39 | newUserId = Users.insert({ | ||
40 | username: firstName, | ||
41 | firstName: firstName, | ||
42 | middleName: middleName, | ||
43 | lastName: lastName, | ||
44 | orgId: orgId, | ||
45 | role: 'STUDENT' | ||
46 | }); | ||
47 | log(newUserId); | ||
48 | if(newUserId){ | ||
49 | Students.insert({ | ||
50 | userId: newUserId, | ||
51 | orgId: orgId, | ||
52 | }); | ||
53 | } | ||
54 | return {newUserId}; | ||
55 | }, | ||
56 | |||
57 | }); | ||
58 |
imports/collections/users/index.js
1 | // import { Users } from '/imports/collections/users/index'; | 1 | // import { Users } from '/imports/collections/users/index'; |
2 | // import { Users } from '/imports/collections/users/index'; | 2 | // import { Users } from '/imports/collections/users/index'; |
3 | 3 | ||
4 | import _ from 'lodash'; | 4 | import _ from 'lodash'; |
5 | import { Meteor } from 'meteor/meteor'; | 5 | import { Meteor } from 'meteor/meteor'; |
6 | import { Mongo } from 'meteor/mongo'; | 6 | import { Mongo } from 'meteor/mongo'; |
7 | import { SimpleSchema } from 'meteor/aldeed:simple-schema'; | 7 | import { SimpleSchema } from 'meteor/aldeed:simple-schema'; |
8 | 8 | ||
9 | import { Orgs } from '/imports/collections/orgs/index'; | 9 | import { Orgs } from '/imports/collections/orgs/index'; |
10 | 10 | ||
11 | class User { | 11 | class User { |
12 | 12 | ||
13 | constructor(doc) { | 13 | constructor(doc) { |
14 | _.assign(this, doc); | 14 | _.assign(this, doc); |
15 | }; | 15 | }; |
16 | 16 | ||
17 | isEmailVerified() { | 17 | isEmailVerified() { |
18 | return !! _.find(this.emails, x => x.verified); | 18 | return !! _.find(this.emails, x => x.verified); |
19 | }; | 19 | }; |
20 | 20 | ||
21 | isPhoneVerified() { | 21 | isPhoneVerified() { |
22 | return !! _.find(this.phones, x => x.verified); | 22 | return !! _.find(this.phones, x => x.verified); |
23 | }; | 23 | }; |
24 | 24 | ||
25 | isIdentityVerified() { | 25 | isIdentityVerified() { |
26 | return !! _.find(this.identities, x => x.verified); | 26 | return !! _.find(this.identities, x => x.verified); |
27 | }; | 27 | }; |
28 | 28 | ||
29 | getRole() { | 29 | getRole() { |
30 | const org = Orgs.findOne({_id: this.orgId}); | 30 | const org = Orgs.findOne({_id: this.orgId}); |
31 | if(!org) return null; | 31 | if(!org) return null; |
32 | const connection = _.find(org.users, {userId: this._id}); | 32 | const connection = _.find(org.users, {userId: this._id}); |
33 | if(!connection) return null; | 33 | if(!connection) return null; |
34 | return connection.role; | 34 | return connection.role; |
35 | }; | 35 | }; |
36 | 36 | ||
37 | getFullName() { | 37 | getFullName() { |
38 | return `${this.firstName} ${this.lastName}`; | 38 | return `${this.firstName} ${this.lastName}`; |
39 | }; | 39 | }; |
40 | getFirstName() { | 40 | getFirstName() { |
41 | return `${this.firstName}`; | 41 | return `${this.firstName}`; |
42 | }; | 42 | }; |
43 | getLastName() { | 43 | getLastName() { |
44 | return `${this.lastName}`; | 44 | return `${this.lastName}`; |
45 | }; | 45 | }; |
46 | getEmail() { | 46 | getEmail() { |
47 | return `${this.emails[0].address}`; | 47 | return `${this.emails[0].address}`; |
48 | }; | 48 | }; |
49 | getOrganization(){ | 49 | getOrganization(){ |
50 | return `${this.orgId}`; | 50 | return `${this.orgId}`; |
51 | }; | 51 | }; |
52 | 52 | ||
53 | getNameByUserId(userId){ | 53 | getNameByUserId(userId){ |
54 | var user = Users.findOne({"_id":userId}); | 54 | var user = Users.findOne({"_id":userId}); |
55 | return user.firstName + " " + user.lastName; | 55 | return user.firstName + " " + user.lastName; |
56 | } | 56 | } |
57 | getAvatarUrl() { | 57 | getAvatarUrl() { |
58 | let random = parseInt(this._id.substr(0, 4), 36); | 58 | let random = parseInt(this._id.substr(0, 4), 36); |
59 | random = '' + (random % 32); | 59 | random = '' + (random % 32); |
60 | while(random.length < 3) random = '0' + random; | 60 | while(random.length < 3) random = '0' + random; |
61 | return `/files/random/random${ random }.png`; | 61 | return `/files/random/random${ random }.png`; |
62 | }; | 62 | }; |
63 | }; | 63 | }; |
64 | 64 | ||
65 | export { User }; | 65 | export { User }; |
66 | 66 | ||
67 | 67 | ||
68 | const transform = function(doc) { | 68 | const transform = function(doc) { |
69 | return new User(doc); | 69 | return new User(doc); |
70 | }; | 70 | }; |
71 | 71 | ||
72 | export const Users = { | 72 | export const Users = { |
73 | 73 | ||
74 | current: function() { | 74 | current: function() { |
75 | return Meteor.users.findOne({_id: Meteor.userId()}, _.extend({transform: transform})); | 75 | return Meteor.users.findOne({_id: Meteor.userId()}, _.extend({transform: transform})); |
76 | }, | 76 | }, |
77 | 77 | ||
78 | find: function(selector, options) { | 78 | find: function(selector, options) { |
79 | return Meteor.users.find(selector || {}, _.extend({transform: transform}, options)); | 79 | return Meteor.users.find(selector || {}, _.extend({transform: transform}, options)); |
80 | }, | 80 | }, |
81 | 81 | ||
82 | findOne: function(selector, options) { | 82 | findOne: function(selector, options) { |
83 | return Meteor.users.findOne(selector || {}, _.extend({transform: transform}, options)); | 83 | return Meteor.users.findOne(selector || {}, _.extend({transform: transform}, options)); |
84 | }, | 84 | }, |
85 | 85 | ||
86 | insert: _.bind(Meteor.users.insert, Meteor.users), | 86 | insert: _.bind(Meteor.users.insert, Meteor.users), |
87 | update: _.bind(Meteor.users.update, Meteor.users), | 87 | update: _.bind(Meteor.users.update, Meteor.users), |
88 | remove: _.bind(Meteor.users.remove, Meteor.users), | 88 | remove: _.bind(Meteor.users.remove, Meteor.users), |
89 | allow: _.bind(Meteor.users.allow, Meteor.users), | 89 | allow: _.bind(Meteor.users.allow, Meteor.users), |
90 | deny: _.bind(Meteor.users.deny, Meteor.users), | 90 | deny: _.bind(Meteor.users.deny, Meteor.users), |
91 | attachSchema: _.bind(Meteor.users.attachSchema, Meteor.users), | 91 | attachSchema: _.bind(Meteor.users.attachSchema, Meteor.users), |
92 | 92 | ||
93 | }; | 93 | }; |
94 | 94 | ||
95 | 95 | ||
96 | Users.deny({ | 96 | Users.deny({ |
97 | insert() { return true; }, | 97 | insert() { return true; }, |
98 | update() { return true; }, | 98 | update() { return true; }, |
99 | remove() { return true; }, | 99 | remove() { return true; }, |
100 | }); | 100 | }); |
101 | 101 | ||
102 | Users.roles = { | 102 | Users.roles = { |
103 | 'STUDENT': 'STUDENT', | 103 | 'STUDENT': 'STUDENT', |
104 | 'TEACHER': 'TEACHER', | 104 | 'TEACHER': 'TEACHER', |
105 | 'ADMIN': 'ADMIN', | 105 | 'ADMIN': 'ADMIN', |
106 | 'PARENT': 'PARENT' | 106 | 'PARENT': 'PARENT' |
107 | }; | 107 | }; |
108 | 108 | ||
109 | 109 | ||
110 | Users.schema = new SimpleSchema({ | 110 | Users.schema = new SimpleSchema({ |
111 | role: { type: String }, | 111 | role: { type: String }, |
112 | orgId: { type: String }, | 112 | orgId: { type: String }, |
113 | username: { type: String, optional: true }, | ||
113 | prefix: { type: String, optional: true }, | 114 | prefix: { type: String, optional: true }, |
114 | firstName: { type: String, optional: true }, | 115 | firstName: { type: String, optional: true }, |
115 | middlename: { type: String, optional: true }, | 116 | middlename: { type: String, optional: true }, |
116 | lastName: { type: String, optional: true }, | 117 | lastName: { type: String, optional: true }, |
117 | gender: { type: String, optional: true }, | 118 | gender: { type: String, optional: true }, |
118 | dob: { type: String, optional: true }, | 119 | dob: { type: String, optional: true }, |
119 | emails: { | 120 | emails: { |
120 | type: [new SimpleSchema({ | 121 | type: [new SimpleSchema({ |
121 | address: { type: String, }, | 122 | address: { type: String, }, |
122 | verified: { type: Boolean, }, | 123 | verified: { type: Boolean, }, |
123 | })], | 124 | })], |
125 | optional: true | ||
124 | }, | 126 | }, |
125 | phones: { | 127 | phones: { |
126 | type: [new SimpleSchema({ | 128 | type: [new SimpleSchema({ |
127 | country: { type: String, }, | 129 | country: { type: String, }, |
128 | prefix: { type: String, }, | 130 | prefix: { type: String, }, |
129 | number: { type: String, }, | 131 | number: { type: String, }, |
130 | verified: { type: Boolean, }, | 132 | verified: { type: Boolean, }, |
131 | })], | 133 | })], |
132 | optional: true | 134 | optional: true |
133 | }, | 135 | }, |
134 | 136 | ||
135 | services: { | 137 | services: { |
136 | type: Object, | 138 | type: Object, |
137 | optional: true, | 139 | optional: true, |
138 | blackbox: true, | 140 | blackbox: true, |
139 | }, | 141 | }, |
140 | 142 | ||
141 | isMetaUser: { type: Boolean, optional: true }, | 143 | isMetaUser: { type: Boolean, optional: true }, |
142 | 144 | ||
143 | createdAt: { type: Date, autoValue: function(){return new Date();}} | 145 | createdAt: { type: Date, autoValue: function(){return new Date();}} |
144 | 146 | ||
145 | }); | 147 | }); |
146 | 148 | ||
147 | Users.attachSchema(Users.schema); | 149 | Users.attachSchema(Users.schema); |
148 | 150 | ||
149 | Users.privateFields = { | 151 | Users.privateFields = { |
150 | orgId: 1, | 152 | orgId: 1, |
151 | address: 1, | 153 | address: 1, |
152 | 154 | ||
153 | firstName: 1, | 155 | firstName: 1, |
154 | lastName: 1, | 156 | lastName: 1, |
155 | emails: 1, | 157 | emails: 1, |
156 | phones: 1, | 158 | phones: 1, |
157 | 159 | ||
158 | isMetaUser: 1, | 160 | isMetaUser: 1, |
159 | createdAt: 1, | 161 | createdAt: 1, |
160 | }; | 162 | }; |
161 | 163 | ||
162 | Users.publicFields = { | 164 | Users.publicFields = { |
163 | firstName: 1, | 165 | firstName: 1, |
164 | lastName: 1, | 166 | lastName: 1, |
165 | emails: 1, | 167 | emails: 1, |
166 | 168 | ||
167 | createdAt: 1, | 169 | createdAt: 1, |
168 | }; | 170 | }; |
169 | 171 |
imports/collections/users/publications.js
1 | import { Meteor } from 'meteor/meteor'; | 1 | import { Meteor } from 'meteor/meteor'; |
2 | import { check, Match } from 'meteor/check' | 2 | import { check, Match } from 'meteor/check' |
3 | import { Users } from '/imports/collections/users/index'; | 3 | import { Users } from '/imports/collections/users/index'; |
4 | import { Orgs } from '/imports/collections/orgs/index'; | 4 | import { Orgs } from '/imports/collections/orgs/index'; |
5 | 5 | ||
6 | 6 | ||
7 | 7 | ||
8 | Meteor.publish('users.current', function() { | 8 | Meteor.publish('users.current', function() { |
9 | return Users.find({ | 9 | return Users.find({ |
10 | _id: this.userId, | 10 | _id: this.userId, |
11 | }, { | 11 | }, { |
12 | fields: Users.privateFields, | 12 | fields: Users.privateFields, |
13 | }); | 13 | }); |
14 | }); | 14 | }); |
15 | Meteor.publish('users.forMyOrg', function() { | ||
16 | const user = Users.findOne({_id: this.userId}); | ||
17 | if(!user) return []; | ||
18 | const org = Orgs.findOne({_id: user.orgId}); | ||
19 | if(!org) return []; | ||
20 | |||
21 | return Users.find({ | ||
22 | orgId: user.orgId,role:"STUDENT" | ||
23 | }); | ||
24 | }); | ||
15 | 25 |
imports/server/collections.js
1 | import '/imports/collections/orgs/publications' | 1 | import '/imports/collections/orgs/publications' |
2 | import '/imports/collections/orgs/methods'; | 2 | import '/imports/collections/orgs/methods'; |
3 | 3 | ||
4 | import '/imports/collections/users/publications'; | 4 | import '/imports/collections/users/publications'; |
5 | |||
6 | import '/imports/collections/students/methods'; | ||
5 | 7 |
imports/server/fixtures.js
1 | import { Meteor } from 'meteor/meteor'; | 1 | import { Meteor } from 'meteor/meteor'; |
2 | import { Roles } from 'meteor/alanning:roles'; | 2 | import { Roles } from 'meteor/alanning:roles'; |
3 | import { Accounts } from 'meteor/accounts-base'; | 3 | import { Accounts } from 'meteor/accounts-base'; |
4 | import { Orgs } from '/imports/collections/orgs/index'; | ||
4 | 5 | ||
5 | if (!Meteor.isProduction) { | 6 | if (!Meteor.isProduction) { |
6 | const users = [{ | 7 | const users = [{ |
7 | email: 'admin@admin.com', | 8 | email: 'admin@admin.com', |
8 | password: 'password', | 9 | password: 'password', |
9 | profile: { | 10 | profile: { |
10 | name: { first: 'Carl', last: 'Winslow' }, | 11 | name: { first: 'Carl', last: 'Winslow' }, |
11 | }, | 12 | }, |
12 | roles: ['admin'], | 13 | roles: ['admin'], |
13 | }]; | 14 | }]; |
14 | 15 | ||
15 | users.forEach(({ email, password, profile, roles }) => { | 16 | users.forEach(({ email, password, profile, roles }) => { |
16 | const userExists = Meteor.users.findOne({ 'emails.address': email }); | 17 | const userExists = Meteor.users.findOne({ 'emails.address': email }); |
17 | 18 | ||
18 | if (!userExists) { | 19 | if (!userExists) { |
19 | const userId = Accounts.createUser({ email, password, profile }); | 20 | const userId = Accounts.createUser({ email, password, profile }); |
20 | Roles.addUsersToRoles(userId, roles); | 21 | Roles.addUsersToRoles(userId, roles); |
22 | const org = [{ | ||
23 | name: 'yd', | ||
24 | slug: 'yd', | ||
25 | "users.userId": userId | ||
26 | }]; | ||
27 | Orgs.inser | ||
21 | } | 28 | } |
22 | }); | 29 | }); |
23 | } | 30 | } |
24 | 31 |
package.json
1 | { | 1 | { |
2 | "name": "application-name", | 2 | "name": "application-name", |
3 | "version": "1.0.0", | 3 | "version": "1.0.0", |
4 | "description": "Application description.", | 4 | "description": "Application description.", |
5 | "scripts": { | 5 | "scripts": { |
6 | "start": "meteor --settings settings-development.json", | 6 | "start": "meteor --settings settings-development.json", |
7 | "test": "meteor test --driver-package practicalmeteor:mocha --port 5000", | 7 | "test": "meteor test --driver-package practicalmeteor:mocha --port 5000", |
8 | "chimp-watch": "chimp --ddp=http://localhost:3000 --watch --mocha --path=tests", | 8 | "chimp-watch": "chimp --ddp=http://localhost:3000 --watch --mocha --path=tests", |
9 | "chimp-test": "chimp --ddp=http://localhost:3000 --mocha --path=tests", | 9 | "chimp-test": "chimp --ddp=http://localhost:3000 --mocha --path=tests", |
10 | "staging": "meteor deploy staging.meteor.com --settings settings-development.json", | 10 | "staging": "meteor deploy staging.meteor.com --settings settings-development.json", |
11 | "production": "meteor deploy production.meteor.com --settings settings-production.json" | 11 | "production": "meteor deploy production.meteor.com --settings settings-production.json" |
12 | }, | 12 | }, |
13 | "devDependencies": { | 13 | "devDependencies": { |
14 | "chimp": "^0.41.2", | 14 | "chimp": "^0.41.2", |
15 | "eslint": "^3.8.1", | 15 | "eslint": "^3.8.1", |
16 | "eslint-config-airbnb": "^12.0.0", | 16 | "eslint-config-airbnb": "^12.0.0", |
17 | "eslint-plugin-import": "^1.16.0", | 17 | "eslint-plugin-import": "^1.16.0", |
18 | "eslint-plugin-jsx-a11y": "^2.2.3", | 18 | "eslint-plugin-jsx-a11y": "^2.2.3", |
19 | "eslint-plugin-meteor": "^4.0.1", | 19 | "eslint-plugin-meteor": "^4.0.1", |
20 | "eslint-plugin-react": "^6.4.1" | 20 | "eslint-plugin-react": "^6.4.1" |
21 | }, | 21 | }, |
22 | "eslintConfig": { | 22 | "eslintConfig": { |
23 | "parserOptions": { | 23 | "parserOptions": { |
24 | "ecmaFeatures": { | 24 | "ecmaFeatures": { |
25 | "jsx": true | 25 | "jsx": true |
26 | } | 26 | } |
27 | }, | 27 | }, |
28 | "plugins": [ | 28 | "plugins": [ |
29 | "meteor", | 29 | "meteor", |
30 | "react" | 30 | "react" |
31 | ], | 31 | ], |
32 | "extends": [ | 32 | "extends": [ |
33 | "airbnb/base", | 33 | "airbnb/base", |
34 | "plugin:meteor/guide", | 34 | "plugin:meteor/guide", |
35 | "plugin:react/recommended" | 35 | "plugin:react/recommended" |
36 | ], | 36 | ], |
37 | "env": { | 37 | "env": { |
38 | "browser": true | 38 | "browser": true |
39 | }, | 39 | }, |
40 | "globals": { | 40 | "globals": { |
41 | "server": false, | 41 | "server": false, |
42 | "browser": false, | 42 | "browser": false, |
43 | "expect": false | 43 | "expect": false |
44 | }, | 44 | }, |
45 | "rules": { | 45 | "rules": { |
46 | "import/no-unresolved": 0, | 46 | "import/no-unresolved": 0, |
47 | "import/no-extraneous-dependencies": 0, | 47 | "import/no-extraneous-dependencies": 0, |
48 | "import/extensions": 0, | 48 | "import/extensions": 0, |
49 | "no-underscore-dangle": [ | 49 | "no-underscore-dangle": [ |
50 | "error", | 50 | "error", |
51 | { | 51 | { |
52 | "allow": [ | 52 | "allow": [ |
53 | "_id", | 53 | "_id", |
54 | "_ensureIndex", | 54 | "_ensureIndex", |
55 | "_verifyEmailToken", | 55 | "_verifyEmailToken", |
56 | "_resetPasswordToken", | 56 | "_resetPasswordToken", |
57 | "_name" | 57 | "_name" |
58 | ] | 58 | ] |
59 | } | 59 | } |
60 | ], | 60 | ], |
61 | "class-methods-use-this": 0 | 61 | "class-methods-use-this": 0 |
62 | } | 62 | } |
63 | }, | 63 | }, |
64 | "dependencies": { | 64 | "dependencies": { |
65 | "babel-runtime": "^6.18.0", | 65 | "babel-runtime": "^6.18.0", |
66 | "bcrypt": "^0.8.7", | 66 | "bcrypt": "^0.8.7", |
67 | "bootstrap": "^3.3.7", | 67 | "bootstrap": "^3.3.7", |
68 | "jquery": "^2.2.4", | 68 | "jquery": "^2.2.4", |
69 | "jquery-validation": "^1.15.1", | 69 | "jquery-validation": "^1.15.1", |
70 | "react": "^15.4.2", | 70 | "react": "^15.4.2", |
71 | "react-addons-css-transition-group": "^15.4.2", | 71 | "react-addons-css-transition-group": "^15.4.2", |
72 | "react-addons-pure-render-mixin": "^15.3.2", | 72 | "react-addons-pure-render-mixin": "^15.3.2", |
73 | "react-addons-transition-group": "^15.4.2", | 73 | "react-addons-transition-group": "^15.4.2", |
74 | "react-bootstrap": "^0.30.5", | 74 | "react-bootstrap": "^0.30.8", |
75 | "react-bootstrap-date-picker": "^4.0.0", | ||
75 | "react-dom": "^15.4.2", | 76 | "react-dom": "^15.4.2", |
76 | "react-fontawesome": "^1.5.0", | 77 | "react-fontawesome": "^1.5.0", |
77 | "react-komposer": "^1.13.1", | 78 | "react-komposer": "^1.13.1", |
78 | "react-router": "^2.6.1", | 79 | "react-router": "^2.6.1", |
79 | "react-router-bootstrap": "^0.23.1", | 80 | "react-router-bootstrap": "^0.23.1", |
80 | "react-svg": "^2.1.19", | 81 | "react-svg": "^2.1.19", |
81 | "reactstrap": "^4.2.0", | 82 | "reactstrap": "^4.2.0", |
82 | "velocity-animate": "^1.4.3", | 83 | "velocity-animate": "^1.4.3", |
83 | "velocity-react": "^1.2.1" | 84 | "velocity-react": "^1.2.1" |
84 | } | 85 | } |
85 | } | 86 | } |
86 | 87 |
private/fixtures/Orgs-Orgs.csv
File was created | 1 | key,name,userKeys | |
2 | ADMIN,Admin org,ADMIN | ||
3 | BRISBANE,Brisbane Kangaroos,"ADAM,BOB,CHARLIE" | ||
4 | PERTH,Perth Sharks,"ADAM,DAVE" |
private/fixtures/Users-Users.csv
File was created | 1 | key,first,last,orgKey,email,isMeta | |
2 | ADMIN,Admin,Adminovich,ADMIN,admin@acme.dev,TRUE | ||
3 | ADMIN,Deepak,Jha,YD,deepak@youngdesk.com,TRUE | ||
4 |