├── mobile ├── .watchmanconfig ├── .gitignore ├── app.json ├── images │ ├── REWmEe.jpg │ └── blur-bg.jpg ├── .babelrc ├── App.test.js ├── package.json ├── components │ ├── MainContent.js │ └── LoginForm.js ├── App.js ├── .flowconfig └── README.md ├── src ├── server │ ├── .babelrc │ ├── graphql │ │ ├── staff.js │ │ ├── conversation_messages.js │ │ ├── conversation.js │ │ ├── entities.js │ │ └── schema.js │ ├── jwtConfig.js │ ├── views │ │ ├── index.pug │ │ ├── error.pug │ │ └── layout.pug │ ├── models-knex │ │ ├── entities.js │ │ ├── conversations.js │ │ ├── conversation_messages.js │ │ └── conversation_participants.js │ ├── public │ │ └── stylesheets │ │ │ ├── style.sass │ │ │ ├── style.css │ │ │ └── style.css.map │ ├── db │ │ └── knex.js │ ├── routes │ │ ├── graphql.js │ │ ├── customer.js │ │ ├── index1.js │ │ ├── index2.js │ │ ├── test.js │ │ ├── department.js │ │ ├── authentication.js │ │ ├── staff.js │ │ └── index.js │ ├── bookshelf.js │ ├── .sequelizerc │ ├── utils │ │ ├── chattoken.js │ │ ├── chatnew.js │ │ └── chat.js │ ├── sequelize │ │ ├── models │ │ │ ├── entities.js │ │ │ ├── conversations.js │ │ │ ├── conversations_messages.js │ │ │ ├── conversations_participants.js │ │ │ └── index.js │ │ ├── config │ │ │ └── config.json │ │ ├── migrations │ │ │ ├── 20170924185022-create-conversations.js │ │ │ ├── 20170924190713-create-conversations-participants.js │ │ │ ├── 20170924185717-create-conversations-messages.js │ │ │ └── 20170924182938-create-entities.js │ │ └── seeders │ │ │ └── 20170924202122-demo-entities.js │ ├── migrations_temp │ │ ├── 20170831101624_increment_table.js │ │ └── 20170913112234_leave_application.js │ ├── seeds │ │ ├── 01-department.js │ │ ├── 03-customer.js │ │ └── 02-staff.js │ ├── seeds1 │ │ └── 04-conversations.js │ ├── knexfile.js │ ├── .eslintrc.json │ ├── migrations │ │ ├── 20170831073944_entities.js │ │ └── 20170910074905_conversations.js │ ├── package.json │ ├── app.js │ └── bin │ │ └── start ├── images │ ├── REWmEe.jpg │ └── blur-bg.jpg ├── actions │ ├── config.js │ ├── leaveapplication.js │ ├── types.js │ ├── staffInformation.js │ ├── department.js │ └── userAuthentication.js ├── rootReducer.js ├── App.test.js ├── components │ ├── customers │ │ ├── CustomerNew.js │ │ ├── CustomerList.js │ │ └── Customer.js │ ├── humanresource │ │ ├── PayAdvice.js │ │ ├── NewDepartmentForm.js │ │ ├── NewLeaveApplication.js │ │ ├── AllowanceAndBenefits.js │ │ ├── LeaveApplicationDetails.js │ │ ├── DepartmentDetails.js │ │ ├── LeaveApplication.js │ │ ├── Department.js │ │ ├── NewStaffForm.js │ │ ├── StaffList.js │ │ └── StaffDetails.js │ ├── users │ │ ├── ChangePassword.js │ │ ├── UserRoles.js │ │ └── LoginForm.js │ ├── helpers │ │ ├── Pagination.js │ │ ├── PageNotFound.js │ │ └── ComposeEmail.js │ ├── navigations │ │ ├── RightMenu.js │ │ ├── Accordion.js │ │ ├── MainContent.js │ │ ├── TopNavigation.js │ │ └── LeftMenu.js │ ├── dashboard │ │ └── Graph.js │ ├── Dashboard.js │ ├── communications │ │ └── MainChat.js │ └── tests │ │ └── TryOverlay.js ├── utils │ ├── setAuthorizationToken.js │ └── validateLogin.js ├── AppInit.js ├── App.css ├── reducers │ ├── auth.js │ └── humanresource.js ├── index.scss ├── index.js ├── App.js ├── logo.svg ├── index.css ├── registerServiceWorker.js └── pagination.css ├── .gitignore ├── public ├── REWmEe.jpg ├── favicon.ico ├── manifest.json ├── index.html └── flexboxgrid.min.css ├── .yo-rc.json ├── package.json └── README.md /mobile/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /mobile/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .expo/ 3 | npm-debug.* 4 | -------------------------------------------------------------------------------- /src/server/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-0"] 3 | } 4 | -------------------------------------------------------------------------------- /mobile/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "sdkVersion": "20.0.0" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/server/graphql/staff.js: -------------------------------------------------------------------------------- 1 | const Staff = ` 2 | type Staff { 3 | 4 | } 5 | ` 6 | -------------------------------------------------------------------------------- /src/server/jwtConfig.js: -------------------------------------------------------------------------------- 1 | export default { 2 | jwtSecret: 'justputyourownsecretkey' 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | mobile/node_modules 4 | src/server/node_modules 5 | 6 | -------------------------------------------------------------------------------- /public/REWmEe.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/passionate-dev219/ERP_System/HEAD/public/REWmEe.jpg -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/passionate-dev219/ERP_System/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/images/REWmEe.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/passionate-dev219/ERP_System/HEAD/src/images/REWmEe.jpg -------------------------------------------------------------------------------- /src/images/blur-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/passionate-dev219/ERP_System/HEAD/src/images/blur-bg.jpg -------------------------------------------------------------------------------- /mobile/images/REWmEe.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/passionate-dev219/ERP_System/HEAD/mobile/images/REWmEe.jpg -------------------------------------------------------------------------------- /src/server/views/index.pug: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= title 5 | p Welcome to #{title} 6 | -------------------------------------------------------------------------------- /mobile/images/blur-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/passionate-dev219/ERP_System/HEAD/mobile/images/blur-bg.jpg -------------------------------------------------------------------------------- /src/server/views/error.pug: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= message 5 | h2= error.status 6 | pre #{error.stack} 7 | -------------------------------------------------------------------------------- /.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "generator-express-es6": { 3 | "promptValues": { 4 | "viewEngine": "pug", 5 | "cssPreprocessor": "sass" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /src/server/models-knex/entities.js: -------------------------------------------------------------------------------- 1 | import bookshelf from '../bookshelf'; 2 | 3 | export default bookshelf.Model.extend({ 4 | tableName: 'entities' 5 | }) 6 | -------------------------------------------------------------------------------- /src/server/public/stylesheets/style.sass: -------------------------------------------------------------------------------- 1 | body 2 | padding: 50px 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif 4 | 5 | a 6 | color: #00B7FF 7 | -------------------------------------------------------------------------------- /src/server/models-knex/conversations.js: -------------------------------------------------------------------------------- 1 | import bookshelf from '../bookshelf'; 2 | 3 | export default bookshelf.Model.extend({ 4 | tableName: 'conversations' 5 | }) 6 | -------------------------------------------------------------------------------- /mobile/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["babel-preset-expo"], 3 | "env": { 4 | "development": { 5 | "plugins": ["transform-react-jsx-source"] 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/server/views/layout.pug: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title= title 5 | link(rel='stylesheet', href='/stylesheets/style.css') 6 | body 7 | block content 8 | -------------------------------------------------------------------------------- /src/server/models-knex/conversation_messages.js: -------------------------------------------------------------------------------- 1 | import bookshelf from '../bookshelf'; 2 | 3 | export default bookshelf.Model.extend({ 4 | tableName: 'conversation_messages' 5 | }) 6 | -------------------------------------------------------------------------------- /src/server/db/knex.js: -------------------------------------------------------------------------------- 1 | var environment = process.env.NODE_ENV || 'development'; 2 | var config = require('../knexfile.js')[environment]; 3 | 4 | module.exports = require('knex')(config); 5 | -------------------------------------------------------------------------------- /src/server/models-knex/conversation_participants.js: -------------------------------------------------------------------------------- 1 | import bookshelf from '../bookshelf'; 2 | 3 | export default bookshelf.Model.extend({ 4 | tableName: 'conversation_participants' 5 | }) 6 | -------------------------------------------------------------------------------- /src/actions/config.js: -------------------------------------------------------------------------------- 1 | let port = window.location.port; 2 | 3 | if (port !== '') 4 | port = ':' + 3003; 5 | 6 | export const apiServer = window.location.protocol + '//' + window.location.hostname + port; 7 | -------------------------------------------------------------------------------- /src/server/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; } 4 | 5 | a { 6 | color: #00B7FF; } 7 | 8 | /*# sourceMappingURL=style.css.map */ -------------------------------------------------------------------------------- /src/actions/leaveapplication.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | const apiServer = 'http://localhost:3003'; 3 | 4 | function getLeaveApplicationDetails(leavenum) { 5 | return axios.get(apiServer + `/api/leave/$leavenum`) 6 | } 7 | -------------------------------------------------------------------------------- /src/rootReducer.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import auth from './reducers/auth'; 3 | import humanresource from './reducers/humanresource'; 4 | 5 | export default combineReducers({ 6 | auth, humanresource 7 | }) 8 | -------------------------------------------------------------------------------- /src/server/routes/graphql.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | // import { 3 | // graphiqlExpress, 4 | // } from 'graphql-server-express'; 5 | 6 | const router = express.Router(); 7 | 8 | 9 | 10 | export default router; 11 | -------------------------------------------------------------------------------- /src/server/routes/customer.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | 3 | const router = express.Router(); 4 | 5 | router.get('/api/list', (req, res) => { 6 | res.status(200).json({'title': 'taitel'}) 7 | }); 8 | 9 | export default router; 10 | -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | }); 9 | -------------------------------------------------------------------------------- /src/server/bookshelf.js: -------------------------------------------------------------------------------- 1 | import knex from 'knex'; 2 | import bookshelf from 'bookshelf'; 3 | import knexConfig from './knexfile'; 4 | 5 | var bookshelf1 = bookshelf(knex(knexConfig.development)); 6 | bookshelf1.plugin('pagination'); 7 | export default bookshelf1; 8 | -------------------------------------------------------------------------------- /src/actions/types.js: -------------------------------------------------------------------------------- 1 | export const SET_CURRENT_USER = 'SET_CURRENT_USER'; 2 | export const SET_DISPLAYED_STAFF = 'SET_DISPLAYED_STAFF'; 3 | export const SET_DISPLAYED_DEPARTMENT = 'SET_DISPLAYED_DEPARTMENT'; 4 | export const SET_SELECTED_STAFF = 'SET_SELECTED_STAFF'; 5 | -------------------------------------------------------------------------------- /src/components/customers/CustomerNew.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | export default class CustomerNew extends Component { 4 | render() { 5 | return( 6 |
7 | New Customer 8 |
9 | ) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /mobile/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import App from './App'; 3 | 4 | import renderer from 'react-test-renderer'; 5 | 6 | it('renders without crashing', () => { 7 | const rendered = renderer.create().toJSON(); 8 | expect(rendered).toBeTruthy(); 9 | }); 10 | -------------------------------------------------------------------------------- /src/server/public/stylesheets/style.css.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "file": "style.css", 4 | "sources": [ 5 | "style.sass" 6 | ], 7 | "mappings": "AAAA,AAAA,IAAI,CAAC;EACH,OAAO,EAAE,IAAK;EACd,IAAI,EAAE,kDAAmD,GAAG;;AAE9D,AAAA,CAAC,CAAC;EACA,KAAK,EAAE,OAAQ,GAAG", 8 | "names": [] 9 | } -------------------------------------------------------------------------------- /src/components/humanresource/PayAdvice.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | class PayAdvice extends Component { 4 | render() { 5 | return( 6 |
7 | Pay Advice 8 |
9 | ) 10 | } 11 | } 12 | 13 | export default PayAdvice; 14 | -------------------------------------------------------------------------------- /src/components/humanresource/NewDepartmentForm.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | class NewDepartmentForm extends Component { 4 | render() { 5 | return( 6 |
7 | New Department 8 |
9 | ) 10 | } 11 | } 12 | 13 | export default NewDepartmentForm; 14 | -------------------------------------------------------------------------------- /src/components/humanresource/NewLeaveApplication.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | class NewLeaveApplication extends Component { 4 | render() { 5 | return( 6 |
7 | New Leave 8 |
9 | ) 10 | } 11 | } 12 | 13 | export default NewLeaveApplication; 14 | -------------------------------------------------------------------------------- /src/components/humanresource/AllowanceAndBenefits.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | class AllowanceAndBenefits extends Component { 4 | render() { 5 | return( 6 |
7 | Allowance And Benefits 8 |
9 | ) 10 | } 11 | } 12 | 13 | export default AllowanceAndBenefits; 14 | -------------------------------------------------------------------------------- /src/components/humanresource/LeaveApplicationDetails.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | class LeaveApplicationDetails extends Component { 4 | render() { 5 | return( 6 |
7 | Leave Details 8 |
9 | ) 10 | } 11 | } 12 | 13 | export default LeaveApplicationDetails; 14 | -------------------------------------------------------------------------------- /src/server/.sequelizerc: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | "config": path.resolve('./sequelize/config', 'config.json'), 5 | "models-path": path.resolve('./sequelize/models'), 6 | "seeders-path": path.resolve('./sequelize/seeders'), 7 | "migrations-path": path.resolve('./sequelize/migrations') 8 | }; 9 | -------------------------------------------------------------------------------- /src/server/utils/chattoken.js: -------------------------------------------------------------------------------- 1 | import jwt from 'jsonwebtoken'; 2 | import jwtConfig from '../jwtConfig'; 3 | 4 | export default (jwtData) => { 5 | if (jwtData) { 6 | return jwt.verify(jwtData, jwtConfig.jwtSecret); 7 | } else { 8 | return { 9 | stats: false, 10 | errors: 'Invalid token' 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/utils/setAuthorizationToken.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | // import isEmpty from 'lodash'; 3 | 4 | export default function setAuthorizationToken(token) { 5 | if (token) { 6 | axios.defaults.headers.common['Authorization'] = `Bearer ${token}`; 7 | } else { 8 | delete axios.defaults.headers.common['Authorization']; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/server/sequelize/models/entities.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = (sequelize, DataTypes) => { 3 | var Entities = sequelize.define('entities', { 4 | id: DataTypes.INTEGER 5 | }, { 6 | classMethods: { 7 | associate: function(models) { 8 | // associations can be defined here 9 | } 10 | } 11 | }); 12 | return Entities; 13 | }; 14 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /src/server/sequelize/models/conversations.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = (sequelize, DataTypes) => { 3 | var conversations = sequelize.define('conversations', { 4 | conversationtype: DataTypes.INTEGER 5 | }, { 6 | classMethods: { 7 | associate: function(models) { 8 | // associations can be defined here 9 | } 10 | } 11 | }); 12 | return conversations; 13 | }; 14 | -------------------------------------------------------------------------------- /src/AppInit.js: -------------------------------------------------------------------------------- 1 | import jwtDecode from 'jwt-decode'; 2 | import { setCurrentUser } from './actions/userAuthentication'; 3 | import { setDisplayedStaff } from './actions/staffInformation'; 4 | 5 | if (localStorage.jwtToken) { 6 | setAuthorizationToken(localStorage.jwtToken); 7 | store.dispatch(setCurrentUser(jwtDecode(localStorage.jwtToken))); 8 | store.dispatch(setDisplayedStaff(localStorage.staffList)); 9 | } 10 | -------------------------------------------------------------------------------- /src/server/graphql/conversation_messages.js: -------------------------------------------------------------------------------- 1 | import Conversation from './conversation'; 2 | 3 | const typeDefs = ` 4 | type Conversation_Messages { 5 | id: ID! 6 | entity_ID: Conversation! 7 | message: String 8 | created_at: Date! 9 | updated_at: Date! 10 | } 11 | 12 | type Query { 13 | conversation_messages: [Conversation_Messages] 14 | } 15 | `; 16 | 17 | export default typeDefs; 18 | -------------------------------------------------------------------------------- /src/server/sequelize/models/conversations_messages.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = (sequelize, DataTypes) => { 3 | var conversations_messages = sequelize.define('conversation_messages', { 4 | conversation_id: DataTypes.INTEGER 5 | }, { 6 | classMethods: { 7 | associate: function(models) { 8 | // associations can be defined here 9 | } 10 | } 11 | }); 12 | return conversations_messages; 13 | }; 14 | -------------------------------------------------------------------------------- /src/server/migrations_temp/20170831101624_increment_table.js: -------------------------------------------------------------------------------- 1 | 2 | exports.up = function(knex, Promise) { 3 | return knex.schema.createTable('increment_table', (table) => { 4 | table.increments(); 5 | table.string('the_prefix'); 6 | table.string('entity_name'); 7 | table.integer('current_number'); 8 | }) 9 | }; 10 | 11 | exports.down = function(knex, Promise) { 12 | return knex.schema.dropTable('increment_table'); 13 | }; 14 | -------------------------------------------------------------------------------- /src/server/sequelize/models/conversations_participants.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = (sequelize, DataTypes) => { 3 | var conversations_participants = sequelize.define('conversation_participants', { 4 | conversation_id: DataTypes.INTEGER 5 | }, { 6 | classMethods: { 7 | associate: function(models) { 8 | // associations can be defined here 9 | } 10 | } 11 | }); 12 | return conversations_participants; 13 | }; 14 | -------------------------------------------------------------------------------- /src/utils/validateLogin.js: -------------------------------------------------------------------------------- 1 | const validator = require('validator'); 2 | 3 | function validateLogin(email = '', password = '') { 4 | let errors; 5 | 6 | if (email === '') { 7 | errors.email = 'Email cannot be empty'; 8 | } 9 | if (!validator.isEmail(email)) { 10 | errors.email = 'Invalid email format'; 11 | } 12 | if (password === '') { 13 | errors.password = 'Password cannot be empty'; 14 | } 15 | 16 | return errors; 17 | } 18 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | 2 | .App { 3 | text-align: center; 4 | } 5 | 6 | .App-logo { 7 | animation: App-logo-spin infinite 20s linear; 8 | height: 80px; 9 | } 10 | 11 | .App-header { 12 | background-color: #222; 13 | height: 150px; 14 | padding: 20px; 15 | color: white; 16 | } 17 | 18 | .App-intro { 19 | font-size: large; 20 | } 21 | 22 | @keyframes App-logo-spin { 23 | from { transform: rotate(0deg); } 24 | to { transform: rotate(360deg); } 25 | } 26 | -------------------------------------------------------------------------------- /src/components/customers/CustomerList.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { 3 | // Button, Dialog, Intent, Hotkey, Hotkeys, HotkeysTarget 4 | } from "@blueprintjs/core"; 5 | 6 | export default class CustomerList extends Component { 7 | render() { 8 | return( 9 |
10 | Customer List 11 |
12 | 13 |
14 |
15 | ) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/components/users/ChangePassword.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | class ChangePassword extends Component { 4 | state = { 5 | dialogTitle: 'Change Password', 6 | isOpen: false 7 | } 8 | 9 | onChange = (e) => { 10 | this.setState({ 11 | [e.target.name]: e.target.value 12 | }) 13 | } 14 | render() { 15 | return( 16 |
17 | 18 |
19 | ) 20 | } 21 | } 22 | 23 | export default ChangePassword; 24 | -------------------------------------------------------------------------------- /src/reducers/auth.js: -------------------------------------------------------------------------------- 1 | import { SET_CURRENT_USER } from '../actions/types'; 2 | import isEmpty from 'lodash/isEmpty'; 3 | 4 | const initialState = { 5 | isAuthenticated: false, 6 | user: {} 7 | }; 8 | 9 | export default (state = initialState, action = {}) => { 10 | switch (action.type) { 11 | case SET_CURRENT_USER: 12 | return { 13 | isAuthenticated: !isEmpty(action.user), 14 | user: action.user 15 | } 16 | default:return state; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/components/customers/Customer.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { Switch, Route } from 'react-router-dom'; 3 | import CustomerList from './CustomerList'; 4 | import CustomerNew from './CustomerNew'; 5 | 6 | export default class Customer extends Component { 7 | render() { 8 | return ( 9 |
10 | 11 | 12 | 13 | 14 |
15 | ) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/server/routes/index1.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import schema from '../graphql/schema'; 3 | import bodyParser from 'body-parser'; 4 | import { graphqlExpress, graphiqlExpress } from 'apollo-server-express'; 5 | 6 | const router = express.Router(); 7 | 8 | /* GET index page. */ 9 | router.get('/', (req, res, next) => { 10 | res.render('index', { 11 | title: 'Express' 12 | }); 13 | }); 14 | 15 | router.use('/graphql', bodyParser.json(), graphqlExpress({ schema })); 16 | router.use('/graphiql', graphiqlExpress({ endpointURL: '/graphql' })); 17 | 18 | export default router; 19 | -------------------------------------------------------------------------------- /src/server/sequelize/config/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "development": { 3 | "username": "iqbal", 4 | "password": "iqbal007", 5 | "database": "bisnew", 6 | "host": "127.0.0.1", 7 | "dialect": "postgres" 8 | }, 9 | "test": { 10 | "username": "root", 11 | "password": null, 12 | "database": "database_test", 13 | "host": "127.0.0.1", 14 | "dialect": "postgres" 15 | }, 16 | "production": { 17 | "username": "root", 18 | "password": null, 19 | "database": "database_production", 20 | "host": "127.0.0.1", 21 | "dialect": "postgres" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/reducers/humanresource.js: -------------------------------------------------------------------------------- 1 | import { 2 | SET_DISPLAYED_STAFF, 3 | SET_SELECTED_STAFF, 4 | SET_DISPLAYED_DEPARTMENT 5 | } 6 | from '../actions/types'; 7 | 8 | export default (state = [], action = {}) => { 9 | switch (action.type) { 10 | case SET_DISPLAYED_STAFF: 11 | return { 12 | staffList: action.staffList 13 | } 14 | case SET_SELECTED_STAFF: 15 | return { 16 | selectedStaff: action.staff 17 | } 18 | case SET_DISPLAYED_DEPARTMENT: 19 | return { 20 | departmentList: action.departmentList 21 | } 22 | default: return state; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/server/seeds/01-department.js: -------------------------------------------------------------------------------- 1 | const faker = require('faker'); 2 | const bcrypt = require('bcrypt'); 3 | 4 | var createDepartment = (knex, id) => { 5 | return knex('entities').insert({ 6 | fullname: faker.commerce.department(), 7 | email: faker.internet.email(), 8 | isdepartment: true 9 | }) 10 | } 11 | 12 | exports.seed = function(knex, Promise) { 13 | return knex('entities').del() 14 | .then(function () { 15 | let records = [] 16 | 17 | for (i = 0; i < 7; i++) { 18 | records.push(createDepartment(knex, i)); 19 | } 20 | return Promise.all(records); 21 | }) 22 | }; 23 | -------------------------------------------------------------------------------- /src/components/users/UserRoles.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | class UserRoles extends Component { 4 | render() { 5 | return( 6 |
7 | User Roles 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
TypeNumber of Users
17 |
18 |
19 | ) 20 | } 21 | } 22 | 23 | export default UserRoles; 24 | -------------------------------------------------------------------------------- /src/server/seeds1/04-conversations.js: -------------------------------------------------------------------------------- 1 | var getStaff = (knex) => { 2 | return knex('entities') 3 | .select('id') 4 | .pluck('id') 5 | .where('isstaff', true) 6 | .then(staffList => { 7 | return staffList 8 | }) 9 | .catch(err => { 10 | console.log('error: ' + err); 11 | }) 12 | } 13 | 14 | var createConversation = (knex) => { 15 | return knex('conversations').insert({}) 16 | .returning('id') 17 | .then(id => { 18 | 19 | }) 20 | } 21 | 22 | exports.seed = function(knex, Promise) { 23 | // Deletes ALL existing entries 24 | let staffList = getStaff(knex); 25 | console.log(staffList); 26 | }; 27 | -------------------------------------------------------------------------------- /src/server/graphql/conversation.js: -------------------------------------------------------------------------------- 1 | import Entities from './entities'; 2 | 3 | const Conversations = ` 4 | type Conversations { 5 | conversation_type: Int! 6 | } 7 | 8 | type ConversationMessages { 9 | id: Int! 10 | conversation_id: Conversations 11 | message: String 12 | entity_id: Entities 13 | } 14 | 15 | type Participants { 16 | id: Int! 17 | conversation_id: Conversations 18 | entity_id: Entities 19 | isadmin: Boolean 20 | } 21 | 22 | type Query { 23 | conversation(conversation_type: Int!): Conversations 24 | getConversation: String 25 | } 26 | ` 27 | 28 | export default () => [Conversations, Entities]; 29 | -------------------------------------------------------------------------------- /src/server/graphql/entities.js: -------------------------------------------------------------------------------- 1 | const Entities = ` 2 | scalar Date 3 | 4 | type Entities { 5 | id: Int! 6 | parent_id: Int 7 | email: String 8 | fullname: String 9 | firstname: String 10 | lastname: String 11 | designation: String 12 | department_id: Int 13 | address1: String 14 | address2: String 15 | postcode: String 16 | city: String 17 | state: String 18 | country: String 19 | isstaff: Boolean 20 | isdepartment: Boolean 21 | iscustomer: Boolean 22 | issupllier: Boolean 23 | isfixedasset: Boolean 24 | isonline: Boolean 25 | isallowedtologin: Boolean 26 | createdAt: Date 27 | updatedAt: Date 28 | } 29 | ` 30 | export default Entities; 31 | -------------------------------------------------------------------------------- /src/actions/staffInformation.js: -------------------------------------------------------------------------------- 1 | import { SET_DISPLAYED_STAFF } from './types'; 2 | import axios from 'axios'; 3 | import { apiServer } from './config'; 4 | 5 | export function putDisplayedStaff(staffList) { 6 | return { 7 | type: SET_DISPLAYED_STAFF, 8 | staffList 9 | }; 10 | } 11 | 12 | export function setDisplayedStaff(pageNum = 0) { 13 | return dispatch => { 14 | return axios.get(apiServer + '/api/staff/' + pageNum) 15 | .then((response) => { 16 | localStorage.setItem("staffList", response.data) 17 | dispatch(putDisplayedStaff(response.data)); 18 | }) 19 | } 20 | } 21 | 22 | export function getStaffById(staffId) { 23 | return axios.get(apiServer + '/api/staffdetails/' + staffId) 24 | } 25 | -------------------------------------------------------------------------------- /src/server/routes/index2.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import isEmpty from 'lodash/isEmpty'; 3 | import Validator from 'validator'; 4 | import knex from '../db/knex'; 5 | import bcrypt from 'bcrypt'; 6 | import Entities from '../models-knex/entities'; 7 | import jwt from 'jsonwebtoken'; 8 | import jwtConfig from '../jwtConfig'; 9 | const router = express.Router(); 10 | const staff = require('./staff')(router); 11 | const department = require('./department')(router); 12 | const test = require('./test')(router); 13 | const authentication = require('./authentication')(router); 14 | 15 | router.get('/', (req, res, next) => { 16 | res.status(200).json({ 17 | 'string': 'It works!' 18 | }) 19 | }); 20 | 21 | export default router; 22 | -------------------------------------------------------------------------------- /src/actions/department.js: -------------------------------------------------------------------------------- 1 | import { SET_DISPLAYED_DEPARTMENT} from './types'; 2 | import axios from 'axios'; 3 | import { apiServer } from './config'; 4 | 5 | export function putDisplayedDepartment(departmentList) { 6 | return { 7 | type: SET_DISPLAYED_DEPARTMENT, 8 | departmentList 9 | }; 10 | } 11 | 12 | export function setDisplayedDepartment(pageNum = 1) { 13 | // console.log('here'); 14 | return dispatch => { 15 | return axios.get(apiServer + '/api/department/' + pageNum) 16 | .then((response) => { 17 | localStorage.setItem("departmentList", response.data) 18 | dispatch(putDisplayedDepartment(response.data)); 19 | }) 20 | } 21 | } 22 | 23 | export function getDepartmentById(departmentId) { 24 | return axios.get(apiServer + '/api/departmentdetails/' + departmentId) 25 | } 26 | -------------------------------------------------------------------------------- /src/server/routes/test.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import isEmpty from 'lodash/isEmpty'; 3 | import Validator from 'validator'; 4 | import knex from '../db/knex'; 5 | import bcrypt from 'bcrypt'; 6 | // import Entities from '../models-knex/entities'; 7 | import jwt from 'jsonwebtoken'; 8 | import jwtConfig from '../jwtConfig'; 9 | 10 | module.exports = (router) => { 11 | router.get('/api/test/cm', (req, res, next) => { 12 | knex('conversation_messages') 13 | // .table('conversation_participants') 14 | .distinct('conversation_id') 15 | .max("id") 16 | .select(knex.raw('jsonb_agg(entity_id) as entities, jsonb_agg(message) as messages')) 17 | .groupBy("conversation_id") 18 | .debug() 19 | .then(results => { 20 | res.status(200).json(results); 21 | }) 22 | }) 23 | } 24 | -------------------------------------------------------------------------------- /src/server/seeds/03-customer.js: -------------------------------------------------------------------------------- 1 | const faker = require('faker'); 2 | const bcrypt = require('bcrypt'); 3 | 4 | var createCustomer = (knex, id) => { 5 | return knex('entities').insert({ 6 | fullname: faker.company.companyName(), 7 | password_digest: bcrypt.hashSync('password', 10), 8 | email: faker.internet.email(), 9 | address1: faker.address.streetName(), 10 | address2: faker.address.streetAddress(), 11 | postcode: faker.address.zipCode(), 12 | city: faker.address.city(), 13 | state: faker.address.state(), 14 | country: faker.address.country(), 15 | iscustomer: true 16 | }) 17 | } 18 | 19 | exports.seed = function(knex, Promise) { 20 | let records = []; 21 | 22 | for (let i = 0; i < 100; i++) { 23 | records.push(createCustomer(knex, i)) 24 | } 25 | 26 | return Promise.all(records) 27 | }; 28 | -------------------------------------------------------------------------------- /src/components/helpers/Pagination.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { NavLink } from 'react-router-dom'; 3 | 4 | class Pagination extends Component { 5 | render() { 6 | const { theElements } = this.props 7 | return( 8 |
9 | { 10 | theElements.map((element, key) => { 11 | return ( 12 | 15 |
this.props.reRender(key+1)} style={{width:'100%', minHeight:'100%'}}> 16 | {element.display} 17 |
18 |
19 | ) 20 | }) 21 | } 22 |
23 | ) 24 | } 25 | } 26 | 27 | export default Pagination; 28 | -------------------------------------------------------------------------------- /src/server/sequelize/migrations/20170924185022-create-conversations.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | up: (queryInterface, Sequelize) => { 4 | return queryInterface.createTable('conversations', { 5 | id: { 6 | allowNull: false, 7 | autoIncrement: true, 8 | primaryKey: true, 9 | type: Sequelize.INTEGER 10 | }, 11 | conversationtype: { 12 | type: Sequelize.INTEGER 13 | }, 14 | createdAt: { 15 | allowNull: false, 16 | type: Sequelize.DATE, 17 | defaultValue: Sequelize.fn('NOW') 18 | }, 19 | updatedAt: { 20 | allowNull: false, 21 | type: Sequelize.DATE, 22 | defaultValue: Sequelize.fn('NOW') 23 | } 24 | }); 25 | }, 26 | down: (queryInterface, Sequelize) => { 27 | return queryInterface.dropTable('conversations'); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /mobile/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mobile", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "babel-plugin-import": "^1.4.0", 7 | "jest-expo": "~20.0.0", 8 | "react-native-scripts": "1.3.1", 9 | "react-test-renderer": "16.0.0-alpha.12" 10 | }, 11 | "main": "./node_modules/react-native-scripts/build/bin/crna-entry.js", 12 | "scripts": { 13 | "start": "react-native-scripts start", 14 | "eject": "react-native-scripts eject", 15 | "android": "react-native-scripts android", 16 | "ios": "react-native-scripts ios", 17 | "test": "node node_modules/jest/bin/jest.js --watch" 18 | }, 19 | "jest": { 20 | "preset": "jest-expo" 21 | }, 22 | "dependencies": { 23 | "antd-mobile": "^1.6.5", 24 | "expo": "^20.0.0", 25 | "react": "16.0.0-alpha.12", 26 | "react-native": "^0.47.0", 27 | "react-native-elements": "^0.16.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/server/migrations_temp/20170913112234_leave_application.js: -------------------------------------------------------------------------------- 1 | 2 | exports.up = function(knex, Promise) { 3 | return knex.schema.createTable('leave_application', table => { 4 | table.increments(); 5 | table.integer('applicant_id') 6 | .unsigned() 7 | .references('entities.id'); 8 | table.integer('leave_type').unsigned(); 9 | table.string('applicant_remarks'); 10 | table.string('date_from').notNullable().defaultTo(knex.raw('now()')); 11 | table.string('date_to').notNullable().defaultTo(knex.raw('now()')); 12 | table.integer('reviewed_by') 13 | .unsigned() 14 | .references('entities_id'); 15 | table.integer('approval_status'); 16 | table.string('approval_remarks'); 17 | table.timestamp('created_at').notNullable().defaultTo(knex.raw('now()')); 18 | table.timestamp('updated_at').notNullable().defaultTo(knex.raw('now()')); 19 | }) 20 | }; 21 | 22 | exports.down = function(knex, Promise) { 23 | return knex.schema.dropTable('leave_application'); 24 | }; 25 | -------------------------------------------------------------------------------- /mobile/components/MainContent.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { StyleSheet, Text, View, Image } from 'react-native'; 3 | 4 | class MainContent extends Component { 5 | render() { 6 | return( 7 | 8 | Open up App.js to start working on your app! 9 | Changes you make will automatically reload. 10 | Shake your phone to open the developer menu. 11 | {/* 36 | 37 | ) 38 | } 39 | 40 | showProfileMenu() { 41 | const compassMenu = ( 42 | 43 | 44 | 45 | 46 | ); 47 | return ( 48 | 51 | 52 | 53 | ) 54 | } 55 | 56 | enterSearchMode() { 57 | alert('You press enter!'); 58 | } 59 | 60 | checkEnterKey(e) { 61 | if (e.keyCode === 13) { 62 | this.enterSearchMode(); 63 | } 64 | } 65 | 66 | render() { 67 | return ( 68 |
69 | 96 |
97 | ) 98 | } 99 | } 100 | 101 | TopNavigation.propTypes = { 102 | logout: PropTypes.func.isRequired, 103 | reRender: PropTypes.func.isRequired 104 | } 105 | 106 | export default connect((state) => { return {} }, {logout})(TopNavigation); 107 | -------------------------------------------------------------------------------- /src/components/humanresource/NewStaffForm.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | class NewStaffForm extends Component { 4 | render() { 5 | return( 6 |
7 |

Personel Details

8 |
9 |
10 |
11 |
First Name
12 |
13 | 15 |
16 |
17 |
18 |
Last Name
19 |
20 | 22 |
23 |
24 |
25 |
Staff ID
26 |
27 | 29 |
30 |
31 |
32 |
Designation
33 |
34 | 36 |
37 |
38 |
39 |
Email
40 |
41 | 43 |
44 |
45 |
46 |
Department
47 |
48 | 51 |
52 |
53 |
54 |
55 |
56 |
Street Address
57 |
58 | 60 |
61 |
62 |
63 |
Street Address 2
64 |
65 | 67 |
68 |
69 |
70 |
Postcode
71 |
72 | 74 |
75 |
76 |
77 |
City/Province
78 |
79 | 81 |
82 |
83 |
84 |
State
85 |
86 | 88 |
89 |
90 |
91 |
Country
92 |
93 | 95 |
96 |
97 |
98 |
99 |
100 | ) 101 | } 102 | } 103 | 104 | export default NewStaffForm; 105 | -------------------------------------------------------------------------------- /src/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | // In production, we register a service worker to serve assets from local cache. 2 | 3 | // This lets the app load faster on subsequent visits in production, and gives 4 | // it offline capabilities. However, it also means that developers (and users) 5 | // will only see deployed updates on the "N+1" visit to a page, since previously 6 | // cached resources are updated in the background. 7 | 8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. 9 | // This link also includes instructions on opting out of this behavior. 10 | 11 | const isLocalhost = Boolean( 12 | window.location.hostname === 'localhost' || 13 | // [::1] is the IPv6 localhost address. 14 | window.location.hostname === '[::1]' || 15 | // 127.0.0.1/8 is considered localhost for IPv4. 16 | window.location.hostname.match( 17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 18 | ) 19 | ); 20 | 21 | export default function register() { 22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 23 | // The URL constructor is available in all browsers that support SW. 24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location); 25 | if (publicUrl.origin !== window.location.origin) { 26 | // Our service worker won't work if PUBLIC_URL is on a different origin 27 | // from what our page is served on. This might happen if a CDN is used to 28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 29 | return; 30 | } 31 | 32 | window.addEventListener('load', () => { 33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 34 | 35 | if (!isLocalhost) { 36 | // Is not local host. Just register service worker 37 | registerValidSW(swUrl); 38 | } else { 39 | // This is running on localhost. Lets check if a service worker still exists or not. 40 | checkValidServiceWorker(swUrl); 41 | } 42 | }); 43 | } 44 | } 45 | 46 | function registerValidSW(swUrl) { 47 | navigator.serviceWorker 48 | .register(swUrl) 49 | .then(registration => { 50 | registration.onupdatefound = () => { 51 | const installingWorker = registration.installing; 52 | installingWorker.onstatechange = () => { 53 | if (installingWorker.state === 'installed') { 54 | if (navigator.serviceWorker.controller) { 55 | // At this point, the old content will have been purged and 56 | // the fresh content will have been added to the cache. 57 | // It's the perfect time to display a "New content is 58 | // available; please refresh." message in your web app. 59 | console.log('New content is available; please refresh.'); 60 | } else { 61 | // At this point, everything has been precached. 62 | // It's the perfect time to display a 63 | // "Content is cached for offline use." message. 64 | console.log('Content is cached for offline use.'); 65 | } 66 | } 67 | }; 68 | }; 69 | }) 70 | .catch(error => { 71 | console.error('Error during service worker registration:', error); 72 | }); 73 | } 74 | 75 | function checkValidServiceWorker(swUrl) { 76 | // Check if the service worker can be found. If it can't reload the page. 77 | fetch(swUrl) 78 | .then(response => { 79 | // Ensure service worker exists, and that we really are getting a JS file. 80 | if ( 81 | response.status === 404 || 82 | response.headers.get('content-type').indexOf('javascript') === -1 83 | ) { 84 | // No service worker found. Probably a different app. Reload the page. 85 | navigator.serviceWorker.ready.then(registration => { 86 | registration.unregister().then(() => { 87 | window.location.reload(); 88 | }); 89 | }); 90 | } else { 91 | // Service worker found. Proceed as normal. 92 | registerValidSW(swUrl); 93 | } 94 | }) 95 | .catch(() => { 96 | console.log( 97 | 'No internet connection found. App is running in offline mode.' 98 | ); 99 | }); 100 | } 101 | 102 | export function unregister() { 103 | if ('serviceWorker' in navigator) { 104 | navigator.serviceWorker.ready.then(registration => { 105 | registration.unregister(); 106 | }); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/server/routes/index.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import isEmpty from 'lodash/isEmpty'; 3 | import Validator from 'validator'; 4 | import knex from 'knex'; 5 | import bcrypt from 'bcrypt'; 6 | import Entities from '../models/user'; 7 | import jwt from 'jsonwebtoken'; 8 | import jwtConfig from '../jwtConfig'; 9 | 10 | const router = express.Router(); 11 | 12 | /* GET index page. */ 13 | router.get('/', (req, res, next) => { 14 | res.render('index', { 15 | title: 'Express' 16 | }); 17 | }); 18 | 19 | router.get('/knex', (req, res, next) => { 20 | knex('entities') 21 | .select("fullname") 22 | .then(entity => { 23 | res.status(200).json(entity) 24 | }) 25 | }) 26 | 27 | export default router; 28 | 29 | function validateInput(data) { 30 | let errors = {}; 31 | 32 | if (Validator.isEmpty(data.email)) { 33 | errors.email = 'Username is required'; 34 | } 35 | 36 | if (!Validator.isEmail(data.email)) { 37 | errors.email = 'Username should be in email format'; 38 | } 39 | 40 | if (Validator.isEmpty(data.password)) { 41 | errors.password = 'Password is required'; 42 | } 43 | 44 | return { 45 | errors, isValid: isEmpty(errors) 46 | } 47 | } 48 | 49 | router.post('/api/login', (req, res, next) => { 50 | const { errors, isValid } = validateInput(req.body); 51 | if (!isValid) { 52 | res.status(400).json(errors); 53 | } else { 54 | const { email, password } = req.body; 55 | 56 | setTimeout(() => { 57 | Entities 58 | .where({email: email}) 59 | .fetch() 60 | .tap((staff) => { 61 | if (staff != null) { 62 | if (bcrypt.compareSync(password, staff.get('password_digest'))) { 63 | const token = jwt.sign({ id: staff.get('id'), email:email }, jwtConfig.jwtSecret); 64 | res.status(200).json({'statuscode':'200', token}); 65 | } else { 66 | res.status(303).json({'errors':'Invalid credentials'}) 67 | } 68 | } 69 | else { 70 | res.status(302).json({'errors': 'Invalid credentials'}) 71 | } 72 | }) 73 | }, 1000); 74 | 75 | } 76 | 77 | }); 78 | 79 | router.post('/api/staff/new', (req, res, next) => { 80 | const { email, password } = req.body; 81 | new Entities({ 82 | email, 83 | password_digest: bcrypt.hashSync(password, 13) 84 | }).save() 85 | .then((saved) => { 86 | res.status(200).json(saved); 87 | }) 88 | .catch((errors) => { 89 | res.status(300).json(errors); 90 | }) 91 | }); 92 | 93 | router.get('/api/department', (req, res, next) => { 94 | Entities.where('isdepartment', true) 95 | .fetchPage({ 96 | pageSize: 9, 97 | page: 1 98 | }) 99 | .then((results) => { 100 | res.status(200).send(results) 101 | }) 102 | .catch((errors) => { 103 | res.status(401).send(errors) 104 | }) 105 | }) 106 | 107 | router.get('/api/departmentdetails/:departmentId', (req, res, next) => { 108 | Entities.where({ 109 | isdepartment: true, 110 | id: req.params.departmentId 111 | }).fetch() 112 | .then((results) => { 113 | res.status(200).send(results) 114 | }) 115 | .catch((err) => { 116 | res.status(401).send(err) 117 | }) 118 | }) 119 | 120 | router.get('/api/department/:pageNumber', (req, res, next) => { 121 | Entities.where('isdepartment', true) 122 | .fetchPage({ 123 | pageSize: 9, 124 | page: req.params.pageNumber ? req.params.pageNumber : 1 125 | }) 126 | .then((results) => { 127 | res.status(200).send(results) 128 | }) 129 | .catch((errors) => { 130 | res.status(401).send(errors) 131 | }) 132 | }) 133 | 134 | router.get('/api/staff', (req, res, next) => { 135 | Entities 136 | .where('isstaff', 'true') 137 | .fetchPage({ 138 | pageSize: 9, 139 | page: 1 140 | }) 141 | .then((results) => { 142 | res.status(200).send(results) 143 | }) 144 | .catch((errors) => { 145 | res.status(401).send(errors) 146 | }) 147 | }); 148 | 149 | router.get('/api/staff/:pageNumber', (req, res, next) => { 150 | Entities.where('isstaff', true).fetchPage({ 151 | pageSize: 12, 152 | page: req.params.pageNumber ? req.params.pageNumber : 1 153 | }) 154 | .then((results) => { 155 | var theResults = { 156 | results, 157 | pagination: results.pagination 158 | } 159 | res.status(200).json(theResults) 160 | }) 161 | .catch((errors) => { 162 | res.status(401).send(errors) 163 | }) 164 | }); 165 | 166 | router.get('/api/staffdetails/:id', (req, res, next) => { 167 | Entities.where('id', req.params.id).fetch() 168 | .then((results) => { 169 | res.status(200).json( 170 | results 171 | ) 172 | }) 173 | .catch((errors) => { 174 | res.status(401).json(errors) 175 | }) 176 | }) 177 | 178 | router.get('/chat', (req, res, next) => { 179 | res.status(200).json({ 180 | response: 'Here i am' 181 | }) 182 | }) 183 | -------------------------------------------------------------------------------- /src/components/humanresource/StaffList.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import PropTypes from 'prop-types'; 4 | import { setDisplayedStaff, getStaffById } from '../../actions/staffInformation'; 5 | import { Dialog, Hotkey, Hotkeys, HotkeysTarget 6 | // Button, Intent, 7 | } from "@blueprintjs/core"; 8 | import StaffDetails from './StaffDetails'; 9 | import NewStaffForm from './NewStaffForm'; 10 | import Pagination from '../helpers/Pagination' 11 | 12 | class StaffList extends Component { 13 | constructor(props) { 14 | super(props) 15 | this.props.setDisplayedStaff() 16 | } 17 | state = { 18 | isOpen: false, 19 | dialogTitle: 'New Staff', 20 | selectedStaffId: 0, 21 | selectedStaff: {}, 22 | pageNum: 1 23 | } 24 | 25 | showStaffDetails = (staffId) => { 26 | const { id, fullname } = this.props.humanresource.staffList.staffList.find(staff => staff.id === staffId); 27 | getStaffById(staffId) 28 | .then(response => { 29 | if (response.data.staff) 30 | { 31 | this.setState({ 32 | dialogTitle: 'Staff Details Information - ' + fullname, 33 | selectedStaffId: id, 34 | isOpen: true, 35 | selectedStaff: response.data.staff[0] 36 | }) 37 | } 38 | }) 39 | } 40 | 41 | newStaff = () => { 42 | this.setState({ 43 | dialogTitle: 'New Staff', 44 | isOpen: true, 45 | selectedStaffId: 0 46 | }) 47 | } 48 | 49 | toggleOverlay = () => { 50 | this.setState({ isOpen: this.state.isOpen ? false : true }) 51 | } 52 | 53 | componentWillReceiveProps(nextProps) { 54 | if ( 55 | !(this.props.humanresource.staffList) || 56 | JSON.stringify(this.props.humanresource.staffList.staffList[0].id) !== JSON.stringify(nextProps.humanresource.staffList.staffList[0].id) 57 | ) { 58 | this.render() 59 | } 60 | } 61 | 62 | renderHotkeys() { 63 | return 64 | 70 | 71 | } 72 | 73 | reRender(fromChild) { 74 | this.props.setDisplayedStaff(fromChild); 75 | } 76 | 77 | render() { 78 | const { staffList = {} } = this.props.humanresource; 79 | const { countStaff = 0 } = staffList; 80 | const pageCount = Math.round(countStaff / 12); 81 | 82 | const theElements = [] 83 | for(let i = 0; i < pageCount; i++) { 84 | theElements.push({linkTo: '/hr/page/' + (i+1), display: (i+1)}) 85 | } 86 | 87 | return( 88 |
89 | Staff List 90 |
91 | { 92 | staffList.staffList ? 93 | staffList.staffList.map((staff, key) => { 94 | return ( 95 |
this.showStaffDetails(staff.id)} className="pt-card pt-elevation-1 pt-interactive transparentThis grid-30 card-padding" style={{margin:'10px 10px 0px 10px'}}> 96 |
97 | 98 |
99 |
100 |
101 | {staff.fullname.slice(0, 15)} 102 |
103 |
{staff.designation.slice(0, 15)}
104 |
105 |
106 | ) 107 | }) 108 | : 109 | '' 110 | } 111 |
112 |
113 | 114 |
115 | 116 | 123 |
124 | { 125 | this.state.selectedStaffId ? 126 | : 127 | 128 | } 129 |
130 |
131 |
132 | ) 133 | } 134 | } 135 | 136 | StaffList.PropTypes = { 137 | setDisplayedStaff: PropTypes.func.isRequired, 138 | humanresource: PropTypes.object.isRequired 139 | } 140 | 141 | function mapStateToProps(state) { 142 | return { 143 | humanresource: state.humanresource 144 | } 145 | } 146 | 147 | HotkeysTarget(StaffList) 148 | export default connect(mapStateToProps, { setDisplayedStaff })(StaffList); 149 | -------------------------------------------------------------------------------- /src/components/tests/TryOverlay.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Palantir Technologies, Inc. All rights reserved. 3 | * Licensed under the BSD-3 License as modified (the “License”); you may obtain a copy 4 | * of the license at https://github.com/palantir/blueprint/blob/master/LICENSE 5 | * and https://github.com/palantir/blueprint/blob/master/PATENTS 6 | */ 7 | 8 | import * as classNames from "classnames"; 9 | import * as React from "react"; 10 | 11 | import { 12 | Button, 13 | Classes, 14 | IBackdropProps, 15 | Intent, 16 | IOverlayableProps, 17 | Overlay, 18 | Switch, 19 | } from "@blueprintjs/core"; 20 | import { BaseExample, handleBooleanChange } from "@blueprintjs/docs"; 21 | 22 | 23 | const OVERLAY_EXAMPLE_CLASS = "docs-overlay-example-transition"; 24 | 25 | export interface IOverlayExampleState extends IOverlayableProps, IBackdropProps { 26 | isOpen?: boolean; 27 | } 28 | 29 | export class OverlayExample extends BaseExample { 30 | state: IOverlayExampleState = { 31 | autoFocus: true, 32 | canEscapeKeyClose: true, 33 | canOutsideClickClose: true, 34 | enforceFocus: true, 35 | hasBackdrop: true, 36 | inline: false, 37 | isOpen: false, 38 | }; 39 | 40 | button: HTMLButtonElement; 41 | refHandlers = { 42 | button: (ref: HTMLButtonElement) => this.button = ref, 43 | }; 44 | 45 | handleAutoFocusChange = handleBooleanChange((autoFocus) => this.setState({ autoFocus })); 46 | handleBackdropChange = handleBooleanChange((hasBackdrop) => this.setState({ hasBackdrop })); 47 | handleEnforceFocusChange = handleBooleanChange((enforceFocus) => this.setState({ enforceFocus })); 48 | handleEscapeKeyChange = handleBooleanChange((canEscapeKeyClose) => this.setState({ canEscapeKeyClose })); 49 | handleInlineChange = handleBooleanChange((inline) => this.setState({ inline })); 50 | handleOutsideClickChange = handleBooleanChange((val) => this.setState({ canOutsideClickClose: val })); 51 | 52 | renderExample() { 53 | const classes = classNames( 54 | Classes.CARD, 55 | Classes.ELEVATION_4, 56 | OVERLAY_EXAMPLE_CLASS, 57 | this.props.themeName, 58 | ); 59 | 60 | return ( 61 |
62 | 65 | 66 |
67 |

I'm an Overlay!

68 |

69 | This is a simple container with some inline styles to position it on the screen. 70 | Its CSS transitions are customized for this example only to demonstrate how 71 | easily custom transitions can be implemented. 72 |

73 |

74 | Click the right button below to transfer focus to the "Show overlay" trigger 75 | button outside of this overlay. If persistent focus is enabled, focus will 76 | be constrained to the overlay. Use the tab key to move to the 77 | next focusable element to illustrate this effect. 78 |

79 |
80 | 81 | 82 |
83 |
84 |
85 | ); 86 | } 87 | 88 | renderOptions() { 89 | const { hasBackdrop, inline } = this.state; 90 | return [ 91 | [ 92 | , 98 | , 104 | , 110 | ], [ 111 | , 117 | , 123 | , 129 | ], 130 | ]; 131 | } 132 | 133 | handleOpen = () => this.setState({ isOpen: true }); 134 | handleClose = () => this.setState({ isOpen: false }); 135 | 136 | focusButton = () => this.button.focus(); 137 | } 138 | -------------------------------------------------------------------------------- /src/pagination.css: -------------------------------------------------------------------------------- 1 | .rc-pagination { 2 | font-size: 12px; 3 | font-family: 'Arial'; 4 | user-select: none; 5 | padding: 0; 6 | text-align: center; 7 | } 8 | .rc-pagination-total-text { 9 | /*float: left;*/ 10 | height: 30px; 11 | line-height: 30px; 12 | list-style: none; 13 | padding: 0; 14 | margin: 0 8px 0 0; 15 | } 16 | .rc-pagination:after { 17 | content: " "; 18 | display: block; 19 | height: 0; 20 | clear: both; 21 | overflow: hidden; 22 | visibility: hidden; 23 | } 24 | .rc-pagination-item { 25 | cursor: pointer; 26 | border-radius: 6px; 27 | min-width: 28px; 28 | height: 28px; 29 | line-height: 28px; 30 | text-align: center; 31 | list-style: none; 32 | float: left; 33 | border: 1px solid #d9d9d9; 34 | background-color: rgba(0, 0, 0, 0.1); 35 | margin-right: 8px; 36 | } 37 | .rc-pagination-item a { 38 | text-decoration: none; 39 | color: #FFF; 40 | } 41 | .rc-pagination-item:hover { 42 | border-color: #2db7f5; 43 | } 44 | .rc-pagination-item:hover a { 45 | color: #2db7f5; 46 | } 47 | .rc-pagination-item-active { 48 | background-color: rgba(0, 0, 0, 0.2); 49 | border-color: #2db7f5; 50 | } 51 | .rc-pagination-item-active a { 52 | color: #fff; 53 | } 54 | .rc-pagination-item-active:hover a { 55 | color: #fff; 56 | } 57 | .rc-pagination-jump-prev:after, 58 | .rc-pagination-jump-next:after { 59 | content: "•••"; 60 | display: block; 61 | letter-spacing: 2px; 62 | color: #ccc; 63 | font-size: 12px; 64 | margin-top: 1px; 65 | } 66 | .rc-pagination-jump-prev:hover:after, 67 | .rc-pagination-jump-next:hover:after { 68 | color: #2db7f5; 69 | } 70 | .rc-pagination-jump-prev:hover:after { 71 | content: "«"; 72 | } 73 | .rc-pagination-jump-next:hover:after { 74 | content: "»"; 75 | } 76 | .rc-pagination-prev, 77 | .rc-pagination-jump-prev, 78 | .rc-pagination-jump-next { 79 | margin-right: 8px; 80 | } 81 | .rc-pagination-prev, 82 | .rc-pagination-next, 83 | .rc-pagination-jump-prev, 84 | .rc-pagination-jump-next { 85 | cursor: pointer; 86 | color: #fff; 87 | font-size: 10px; 88 | border-radius: 6px; 89 | list-style: none; 90 | min-width: 28px; 91 | height: 28px; 92 | line-height: 28px; 93 | float: left; 94 | text-align: center; 95 | } 96 | .rc-pagination-prev a:after { 97 | content: "‹"; 98 | display: block; 99 | } 100 | .rc-pagination-next a:after { 101 | content: "›"; 102 | display: block; 103 | } 104 | .rc-pagination-prev, 105 | .rc-pagination-next { 106 | border: 1px solid #d9d9d9; 107 | font-size: 18px; 108 | } 109 | .rc-pagination-prev a, 110 | .rc-pagination-next a { 111 | color: #fff; 112 | } 113 | .rc-pagination-prev a:after, 114 | .rc-pagination-next a:after { 115 | margin-top: -1px; 116 | } 117 | .rc-pagination-disabled { 118 | cursor: not-allowed; 119 | } 120 | .rc-pagination-disabled a { 121 | color: #ccc; 122 | } 123 | .rc-pagination-options { 124 | float: left; 125 | margin-left: 15px; 126 | } 127 | .rc-pagination-options-size-changer { 128 | float: left; 129 | width: 80px; 130 | } 131 | .rc-pagination-options-quick-jumper { 132 | float: left; 133 | margin-left: 16px; 134 | height: 28px; 135 | line-height: 28px; 136 | } 137 | .rc-pagination-options-quick-jumper input { 138 | margin: 0 8px; 139 | box-sizing: border-box; 140 | background-color: #fff; 141 | border-radius: 6px; 142 | border: 1px solid #d9d9d9; 143 | outline: none; 144 | padding: 3px 12px; 145 | width: 50px; 146 | height: 28px; 147 | } 148 | .rc-pagination-options-quick-jumper input:hover { 149 | border-color: #2db7f5; 150 | } 151 | .rc-pagination-options-quick-jumper button { 152 | display: inline-block; 153 | margin: 0 8px; 154 | font-weight: 500; 155 | text-align: center; 156 | touch-action: manipulation; 157 | cursor: pointer; 158 | background-image: none; 159 | border: 1px solid transparent; 160 | white-space: nowrap; 161 | padding: 0 15px; 162 | font-size: 12px; 163 | border-radius: 6px; 164 | height: 28px; 165 | user-select: none; 166 | transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); 167 | position: relative; 168 | color: rgba(0, 0, 0, 0.65); 169 | background-color: #fff; 170 | border-color: #d9d9d9; 171 | } 172 | .rc-pagination-options-quick-jumper button:hover, 173 | .rc-pagination-options-quick-jumper button:active, 174 | .rc-pagination-options-quick-jumper button:focus { 175 | color: #2db7f5; 176 | background-color: #fff; 177 | border-color: #2db7f5; 178 | } 179 | .rc-pagination-simple .rc-pagination-prev, 180 | .rc-pagination-simple .rc-pagination-next { 181 | border: none; 182 | height: 24px; 183 | line-height: 24px; 184 | margin: 0; 185 | font-size: 18px; 186 | } 187 | .rc-pagination-simple .rc-pagination-simple-pager { 188 | float: left; 189 | margin-right: 8px; 190 | list-style: none; 191 | } 192 | .rc-pagination-simple .rc-pagination-simple-pager .rc-pagination-slash { 193 | margin: 0 10px; 194 | } 195 | .rc-pagination-simple .rc-pagination-simple-pager input { 196 | margin: 0 8px; 197 | box-sizing: border-box; 198 | background-color: #fff; 199 | border-radius: 6px; 200 | border: 1px solid #d9d9d9; 201 | outline: none; 202 | padding: 5px 8px; 203 | min-height: 20px; 204 | } 205 | .rc-pagination-simple .rc-pagination-simple-pager input:hover { 206 | border-color: #2db7f5; 207 | } 208 | .rc-pagination-simple .rc-pagination-simple-pager button { 209 | display: inline-block; 210 | margin: 0 8px; 211 | font-weight: 500; 212 | text-align: center; 213 | touch-action: manipulation; 214 | cursor: pointer; 215 | background-image: none; 216 | border: 1px solid transparent; 217 | white-space: nowrap; 218 | padding: 0 8px; 219 | font-size: 12px; 220 | border-radius: 6px; 221 | height: 26px; 222 | user-select: none; 223 | transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); 224 | position: relative; 225 | color: rgba(0, 0, 0, 0.65); 226 | background-color: #fff; 227 | border-color: #d9d9d9; 228 | } 229 | .rc-pagination-simple .rc-pagination-simple-pager button:hover, 230 | .rc-pagination-simple .rc-pagination-simple-pager button:active, 231 | .rc-pagination-simple .rc-pagination-simple-pager button:focus { 232 | color: #2db7f5; 233 | background-color: #fff; 234 | border-color: #2db7f5; 235 | } 236 | @media only screen and (max-width: 1024px) { 237 | .rc-pagination-item-after-jump-prev, 238 | .rc-pagination-item-before-jump-next { 239 | display: none; 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /src/components/users/LoginForm.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { userLoginRequest } from '../../actions/userAuthentication'; 4 | // import { setDisplayedStaff } from '../../actions/staffInformation'; 5 | import { Button, Intent, Dialog } from "@blueprintjs/core"; 6 | import classNames from 'classnames'; 7 | import PropTypes from 'prop-types'; 8 | 9 | class LoginForm extends Component { 10 | state = { 11 | email: '', 12 | password: '', 13 | isLoading: false, 14 | errors: [], 15 | disabledSubmit: false 16 | } 17 | 18 | style = { 19 | loginBody: { 20 | margin: 'auto', 21 | width: '400px', 22 | height: '300px', 23 | textAlign: 'center', 24 | transform: 'translate('+0+'%, '+30+'%)' 25 | }, 26 | centeringAlignment: { 27 | margin: 'auto', 28 | transform: 'translate('+0+'%, '+5+'%)' 29 | } 30 | } 31 | 32 | onChange = (e) => { 33 | if (!!this.state.errors[e.target.name]) { 34 | let errors = Object.assign({}, this.state.errors); 35 | delete errors[e.target.name]; 36 | this.setState({ 37 | [e.target.name]: e.target.value, 38 | errors 39 | }); 40 | } else { 41 | this.setState({ 42 | [e.target.name]: e.target.value 43 | }) 44 | } 45 | } 46 | 47 | handleSubmit = (e) => { 48 | e.preventDefault(); 49 | this.setState({ disabledSubmit: true }); 50 | let errors = {} 51 | 52 | if (this.state.email === '') errors.email = "Email can't be empty"; 53 | if (this.state.password === '') errors.password = "Password can't be empty"; 54 | 55 | this.setState({ errors }); 56 | 57 | this.props.userLoginRequest({ 58 | email: this.state.email, 59 | password: this.state.password 60 | }).then((response) => { 61 | // this.props.setDisplayedStaff(); 62 | // console.log(response); 63 | if (!response.retStatus) { 64 | this.setState({ errors: response.errors}) 65 | } 66 | }) 67 | this.setState({ disabledSubmit: false }); 68 | } 69 | 70 | render() { 71 | return( 72 |
73 |
74 |
75 |
76 |
77 |
78 | MyERP 79 |
80 |
81 | For demo purposes please use Ezekiel.Botsford73@hotmail.com as email and password as password. 82 |
83 |
84 |
85 | 95 |
96 |
97 | { 98 | this.state.errors.email ?
Email is required!
: "" 99 | } 100 |
101 | 102 |
 
103 | 104 |
105 | 114 |
115 |
116 | { 117 | this.state.errors.password ?
Password is required!
: "" 118 | } 119 |
120 |
121 | { 122 | this.state.errors.errors ?
{this.state.errors.errors}
: '' 123 | } 124 |
125 |
 
126 |
127 |
131 |
132 |
133 |
134 |
135 | this.setState({isLoading:false})} 139 | title="Dialog header" 140 | > 141 |
142 | Some content 143 |
144 |
145 |
146 |
153 |
154 |
155 |
156 | ) 157 | } 158 | } 159 | 160 | LoginForm.propTypes = { 161 | userLoginRequest: PropTypes.func.isRequired, 162 | reRender: PropTypes.func.isRequired 163 | } 164 | 165 | export default connect((state) => { return {}}, { userLoginRequest })(LoginForm); 166 | -------------------------------------------------------------------------------- /src/components/navigations/LeftMenu.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { NavLink } from 'react-router-dom'; 3 | import Accordion from 'react-responsive-accordion'; 4 | import Draggable from 'react-draggable'; 5 | import ComposeEmail from '../helpers/ComposeEmail' 6 | // import classNames from 'classnames'; 7 | 8 | class LeftMenu extends Component { 9 | state = { 10 | displayEmail: 'none' 11 | } 12 | eventLogger = (e: MouseEvent, data: Object) => { 13 | console.log('Event: ', e); 14 | console.log('Data: ', data); 15 | }; 16 | render() { 17 | return( 18 |
19 | 20 |
21 |
    22 | 23 |
  • 24 | 25 | Staff List 26 |
  • 27 |
    28 | 29 |
  • 30 | 31 | Department 32 |
  • 33 |
    34 | 35 |
  • 36 | 37 | Leave Application 38 |
  • 39 |
    40 | 41 |
  • 42 | 43 | Allowance & Benefits 44 |
  • 45 |
    46 | 47 |
  • 48 | 49 | Pay Advice 50 |
  • 51 |
    52 |
53 |
54 |
55 |
    56 |
  • 57 | 58 | 59 | Customer List 60 | 61 |
  • 62 |
  • 63 | 64 | 65 | Quotation 66 | 67 |
  • 68 |
  • 69 | 70 | 71 | Purchase Order 72 | 73 |
  • 74 |
  • 75 | 76 | 77 | Delivery Order 78 | 79 |
  • 80 |
  • 81 | 82 | 83 | Invoice 84 | 85 |
  • 86 |
  • 87 | 88 | 89 | Pay Advice 90 | 91 |
  • 92 |
93 |
94 |
95 |
    96 |
  • 97 | 98 | 99 | Supplier List 100 | 101 |
  • 102 |
103 |
104 |
105 |
    106 |
  • 107 | 108 | 109 | Dashboard 110 | 111 |
  • 112 |
  • 113 | 114 | 115 | Cash Statement 116 | 117 |
  • 118 |
  • 119 | 120 | 121 | Balance Sheet 122 | 123 |
  • 124 |
  • 125 | 126 | 127 | Profit Loss Statement 128 | 129 |
  • 130 |
131 |
132 |
133 |
134 |
    135 |
  • 136 | Email 137 |
  • 138 |
  • this.setState({ displayEmail: 'block'})}> 139 | 140 | Compose 141 |
  • 142 |
  • 143 | 144 | Inbox 145 |
  • 146 |
  • 147 | 148 | Sent 149 |
  • 150 |
151 |
152 | 159 |
160 |
Compose New Email 161 |
162 | this.setState({displayEmail: 'none'}) } /> 163 |
164 |
165 |
166 | ) 167 | } 168 | } 169 | 170 | export default LeftMenu; 171 | -------------------------------------------------------------------------------- /src/components/humanresource/StaffDetails.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Tab, Tabs, TabList, TabPanel } from 'react-tabs'; 3 | import { connect } from 'react-redux'; 4 | 5 | class PersonnelDetails extends Component { 6 | handleOnChange() { 7 | 8 | } 9 | 10 | render(){ 11 | var { staffDetails } = this.props 12 | const labelWidth = 'grid-40' 13 | const fieldWidth = 'grid-60' 14 | return ( 15 |
16 |
17 |
18 |
First Name
19 |
20 | 25 |
26 |
27 |
28 |
Last Name
29 |
30 | 33 |
34 |
35 |
36 |
Staff ID
37 |
38 | 41 |
42 |
43 |
44 |
Designation
45 |
46 | 49 |
50 |
51 |
52 |
Department
53 |
54 | 57 |
58 |
59 |
60 |
Email
61 |
62 | 65 |
66 |
67 |
68 |
69 |
70 |
Street Address 1
71 |
72 | 77 |
78 |
79 |
80 |
Street Address 2
81 |
82 | 87 |
88 |
89 |
90 |
City
91 |
92 | 97 |
98 |
99 |
100 |
State
101 |
102 | 107 |
108 |
109 |
110 |
Country
111 |
112 | 117 |
118 |
119 |
120 |
121 | ) 122 | } 123 | } 124 | 125 | class AllowanceAndBenefits extends Component { 126 | render() { 127 | return ( 128 |
129 | Allowance And Benefits 130 |
131 | ) 132 | } 133 | } 134 | 135 | class StaffDetails extends Component { 136 | componentWillMount() { 137 | const { selectedStaff } = this.props 138 | this.state = { 139 | staffDetails: selectedStaff, 140 | selectedStaffId: selectedStaff.id 141 | } 142 | } 143 | 144 | handleSubmit() {} 145 | 146 | render() { 147 | return( 148 |
149 |
150 | 151 | 152 | Personnel Details 153 | Allowance & Benefits 154 | Administration 155 | Financial 156 | 157 |
158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | asd 169 | 170 |
171 |
172 |
173 |
174 | ) 175 | } 176 | } 177 | 178 | export default connect(null)(StaffDetails); 179 | -------------------------------------------------------------------------------- /public/flexboxgrid.min.css: -------------------------------------------------------------------------------- 1 | .container,.container-fluid{margin-right:auto;margin-left:auto}.container-fluid{padding-right:2rem;padding-left:2rem}.row{box-sizing:border-box;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:0;-ms-flex:0 1 auto;flex:0 1 auto;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-.5rem;margin-left:-.5rem}.row.reverse{-webkit-box-orient:horizontal;-webkit-box-direction:reverse;-ms-flex-direction:row-reverse;flex-direction:row-reverse}.col.reverse{-webkit-box-orient:vertical;-webkit-box-direction:reverse;-ms-flex-direction:column-reverse;flex-direction:column-reverse}.col-xs,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-offset-0,.col-xs-offset-1,.col-xs-offset-10,.col-xs-offset-11,.col-xs-offset-12,.col-xs-offset-2,.col-xs-offset-3,.col-xs-offset-4,.col-xs-offset-5,.col-xs-offset-6,.col-xs-offset-7,.col-xs-offset-8,.col-xs-offset-9{box-sizing:border-box;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;padding-right:.5rem;padding-left:.5rem}.col-xs{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-preferred-size:0;flex-basis:0;max-width:100%}.col-xs-1{-ms-flex-preferred-size:8.33333333%;flex-basis:8.33333333%;max-width:8.33333333%}.col-xs-2{-ms-flex-preferred-size:16.66666667%;flex-basis:16.66666667%;max-width:16.66666667%}.col-xs-3{-ms-flex-preferred-size:25%;flex-basis:25%;max-width:25%}.col-xs-4{-ms-flex-preferred-size:33.33333333%;flex-basis:33.33333333%;max-width:33.33333333%}.col-xs-5{-ms-flex-preferred-size:41.66666667%;flex-basis:41.66666667%;max-width:41.66666667%}.col-xs-6{-ms-flex-preferred-size:50%;flex-basis:50%;max-width:50%}.col-xs-7{-ms-flex-preferred-size:58.33333333%;flex-basis:58.33333333%;max-width:58.33333333%}.col-xs-8{-ms-flex-preferred-size:66.66666667%;flex-basis:66.66666667%;max-width:66.66666667%}.col-xs-9{-ms-flex-preferred-size:75%;flex-basis:75%;max-width:75%}.col-xs-10{-ms-flex-preferred-size:83.33333333%;flex-basis:83.33333333%;max-width:83.33333333%}.col-xs-11{-ms-flex-preferred-size:91.66666667%;flex-basis:91.66666667%;max-width:91.66666667%}.col-xs-12{-ms-flex-preferred-size:100%;flex-basis:100%;max-width:100%}.col-xs-offset-0{margin-left:0}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-11{margin-left:91.66666667%}.start-xs{-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;text-align:start}.center-xs{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;text-align:center}.end-xs{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;text-align:end}.top-xs{-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start}.middle-xs{-webkit-box-align:center;-ms-flex-align:center;align-items:center}.bottom-xs{-webkit-box-align:end;-ms-flex-align:end;align-items:flex-end}.around-xs{-ms-flex-pack:distribute;justify-content:space-around}.between-xs{-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.first-xs{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.last-xs{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}@media only screen and (min-width:48em){.container{width:49rem}.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-offset-0,.col-sm-offset-1,.col-sm-offset-10,.col-sm-offset-11,.col-sm-offset-12,.col-sm-offset-2,.col-sm-offset-3,.col-sm-offset-4,.col-sm-offset-5,.col-sm-offset-6,.col-sm-offset-7,.col-sm-offset-8,.col-sm-offset-9{box-sizing:border-box;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;padding-right:.5rem;padding-left:.5rem}.col-sm{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-preferred-size:0;flex-basis:0;max-width:100%}.col-sm-1{-ms-flex-preferred-size:8.33333333%;flex-basis:8.33333333%;max-width:8.33333333%}.col-sm-2{-ms-flex-preferred-size:16.66666667%;flex-basis:16.66666667%;max-width:16.66666667%}.col-sm-3{-ms-flex-preferred-size:25%;flex-basis:25%;max-width:25%}.col-sm-4{-ms-flex-preferred-size:33.33333333%;flex-basis:33.33333333%;max-width:33.33333333%}.col-sm-5{-ms-flex-preferred-size:41.66666667%;flex-basis:41.66666667%;max-width:41.66666667%}.col-sm-6{-ms-flex-preferred-size:50%;flex-basis:50%;max-width:50%}.col-sm-7{-ms-flex-preferred-size:58.33333333%;flex-basis:58.33333333%;max-width:58.33333333%}.col-sm-8{-ms-flex-preferred-size:66.66666667%;flex-basis:66.66666667%;max-width:66.66666667%}.col-sm-9{-ms-flex-preferred-size:75%;flex-basis:75%;max-width:75%}.col-sm-10{-ms-flex-preferred-size:83.33333333%;flex-basis:83.33333333%;max-width:83.33333333%}.col-sm-11{-ms-flex-preferred-size:91.66666667%;flex-basis:91.66666667%;max-width:91.66666667%}.col-sm-12{-ms-flex-preferred-size:100%;flex-basis:100%;max-width:100%}.col-sm-offset-0{margin-left:0}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-11{margin-left:91.66666667%}.start-sm{-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;text-align:start}.center-sm{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;text-align:center}.end-sm{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;text-align:end}.top-sm{-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start}.middle-sm{-webkit-box-align:center;-ms-flex-align:center;align-items:center}.bottom-sm{-webkit-box-align:end;-ms-flex-align:end;align-items:flex-end}.around-sm{-ms-flex-pack:distribute;justify-content:space-around}.between-sm{-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.first-sm{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.last-sm{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}}@media only screen and (min-width:64em){.container{width:65rem}.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-offset-0,.col-md-offset-1,.col-md-offset-10,.col-md-offset-11,.col-md-offset-12,.col-md-offset-2,.col-md-offset-3,.col-md-offset-4,.col-md-offset-5,.col-md-offset-6,.col-md-offset-7,.col-md-offset-8,.col-md-offset-9{box-sizing:border-box;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;padding-right:.5rem;padding-left:.5rem}.col-md{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-preferred-size:0;flex-basis:0;max-width:100%}.col-md-1{-ms-flex-preferred-size:8.33333333%;flex-basis:8.33333333%;max-width:8.33333333%}.col-md-2{-ms-flex-preferred-size:16.66666667%;flex-basis:16.66666667%;max-width:16.66666667%}.col-md-3{-ms-flex-preferred-size:25%;flex-basis:25%;max-width:25%}.col-md-4{-ms-flex-preferred-size:33.33333333%;flex-basis:33.33333333%;max-width:33.33333333%}.col-md-5{-ms-flex-preferred-size:41.66666667%;flex-basis:41.66666667%;max-width:41.66666667%}.col-md-6{-ms-flex-preferred-size:50%;flex-basis:50%;max-width:50%}.col-md-7{-ms-flex-preferred-size:58.33333333%;flex-basis:58.33333333%;max-width:58.33333333%}.col-md-8{-ms-flex-preferred-size:66.66666667%;flex-basis:66.66666667%;max-width:66.66666667%}.col-md-9{-ms-flex-preferred-size:75%;flex-basis:75%;max-width:75%}.col-md-10{-ms-flex-preferred-size:83.33333333%;flex-basis:83.33333333%;max-width:83.33333333%}.col-md-11{-ms-flex-preferred-size:91.66666667%;flex-basis:91.66666667%;max-width:91.66666667%}.col-md-12{-ms-flex-preferred-size:100%;flex-basis:100%;max-width:100%}.col-md-offset-0{margin-left:0}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-3{margin-left:25%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-6{margin-left:50%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-9{margin-left:75%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-11{margin-left:91.66666667%}.start-md{-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;text-align:start}.center-md{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;text-align:center}.end-md{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;text-align:end}.top-md{-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start}.middle-md{-webkit-box-align:center;-ms-flex-align:center;align-items:center}.bottom-md{-webkit-box-align:end;-ms-flex-align:end;align-items:flex-end}.around-md{-ms-flex-pack:distribute;justify-content:space-around}.between-md{-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.first-md{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.last-md{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}}@media only screen and (min-width:75em){.container{width:76rem}.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-offset-0,.col-lg-offset-1,.col-lg-offset-10,.col-lg-offset-11,.col-lg-offset-12,.col-lg-offset-2,.col-lg-offset-3,.col-lg-offset-4,.col-lg-offset-5,.col-lg-offset-6,.col-lg-offset-7,.col-lg-offset-8,.col-lg-offset-9{box-sizing:border-box;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;padding-right:.5rem;padding-left:.5rem}.col-lg{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-preferred-size:0;flex-basis:0;max-width:100%}.col-lg-1{-ms-flex-preferred-size:8.33333333%;flex-basis:8.33333333%;max-width:8.33333333%}.col-lg-2{-ms-flex-preferred-size:16.66666667%;flex-basis:16.66666667%;max-width:16.66666667%}.col-lg-3{-ms-flex-preferred-size:25%;flex-basis:25%;max-width:25%}.col-lg-4{-ms-flex-preferred-size:33.33333333%;flex-basis:33.33333333%;max-width:33.33333333%}.col-lg-5{-ms-flex-preferred-size:41.66666667%;flex-basis:41.66666667%;max-width:41.66666667%}.col-lg-6{-ms-flex-preferred-size:50%;flex-basis:50%;max-width:50%}.col-lg-7{-ms-flex-preferred-size:58.33333333%;flex-basis:58.33333333%;max-width:58.33333333%}.col-lg-8{-ms-flex-preferred-size:66.66666667%;flex-basis:66.66666667%;max-width:66.66666667%}.col-lg-9{-ms-flex-preferred-size:75%;flex-basis:75%;max-width:75%}.col-lg-10{-ms-flex-preferred-size:83.33333333%;flex-basis:83.33333333%;max-width:83.33333333%}.col-lg-11{-ms-flex-preferred-size:91.66666667%;flex-basis:91.66666667%;max-width:91.66666667%}.col-lg-12{-ms-flex-preferred-size:100%;flex-basis:100%;max-width:100%}.col-lg-offset-0{margin-left:0}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-11{margin-left:91.66666667%}.start-lg{-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;text-align:start}.center-lg{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;text-align:center}.end-lg{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;text-align:end}.top-lg{-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start}.middle-lg{-webkit-box-align:center;-ms-flex-align:center;align-items:center}.bottom-lg{-webkit-box-align:end;-ms-flex-align:end;align-items:flex-end}.around-lg{-ms-flex-pack:distribute;justify-content:space-around}.between-lg{-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.first-lg{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.last-lg{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}} -------------------------------------------------------------------------------- /mobile/README.md: -------------------------------------------------------------------------------- 1 | This project was bootstrapped with [Create React Native App](https://github.com/react-community/create-react-native-app). 2 | 3 | Below you'll find information about performing common tasks. The most recent version of this guide is available [here](https://github.com/react-community/create-react-native-app/blob/master/react-native-scripts/template/README.md). 4 | 5 | ## Table of Contents 6 | 7 | * [Updating to New Releases](#updating-to-new-releases) 8 | * [Available Scripts](#available-scripts) 9 | * [npm start](#npm-start) 10 | * [npm test](#npm-test) 11 | * [npm run ios](#npm-run-ios) 12 | * [npm run android](#npm-run-android) 13 | * [npm run eject](#npm-run-eject) 14 | * [Writing and Running Tests](#writing-and-running-tests) 15 | * [Environment Variables](#environment-variables) 16 | * [Configuring Packager IP Address](#configuring-packager-ip-address) 17 | * [Adding Flow](#adding-flow) 18 | * [Customizing App Display Name and Icon](#customizing-app-display-name-and-icon) 19 | * [Sharing and Deployment](#sharing-and-deployment) 20 | * [Publishing to Expo's React Native Community](#publishing-to-expos-react-native-community) 21 | * [Building an Expo "standalone" app](#building-an-expo-standalone-app) 22 | * [Ejecting from Create React Native App](#ejecting-from-create-react-native-app) 23 | * [Build Dependencies (Xcode & Android Studio)](#build-dependencies-xcode-android-studio) 24 | * [Should I Use ExpoKit?](#should-i-use-expokit) 25 | * [Troubleshooting](#troubleshooting) 26 | * [Networking](#networking) 27 | * [iOS Simulator won't open](#ios-simulator-wont-open) 28 | * [QR Code does not scan](#qr-code-does-not-scan) 29 | 30 | ## Updating to New Releases 31 | 32 | You should only need to update the global installation of `create-react-native-app` very rarely, ideally never. 33 | 34 | Updating the `react-native-scripts` dependency of your app should be as simple as bumping the version number in `package.json` and reinstalling your project's dependencies. 35 | 36 | Upgrading to a new version of React Native requires updating the `react-native`, `react`, and `expo` package versions, and setting the correct `sdkVersion` in `app.json`. See the [versioning guide](https://github.com/react-community/create-react-native-app/blob/master/VERSIONS.md) for up-to-date information about package version compatibility. 37 | 38 | ## Available Scripts 39 | 40 | If Yarn was installed when the project was initialized, then dependencies will have been installed via Yarn, and you should probably use it to run these commands as well. Unlike dependency installation, command running syntax is identical for Yarn and NPM at the time of this writing. 41 | 42 | ### `npm start` 43 | 44 | Runs your app in development mode. 45 | 46 | Open it in the [Expo app](https://expo.io) on your phone to view it. It will reload if you save edits to your files, and you will see build errors and logs in the terminal. 47 | 48 | Sometimes you may need to reset or clear the React Native packager's cache. To do so, you can pass the `--reset-cache` flag to the start script: 49 | 50 | ``` 51 | npm start -- --reset-cache 52 | # or 53 | yarn start -- --reset-cache 54 | ``` 55 | 56 | #### `npm test` 57 | 58 | Runs the [jest](https://github.com/facebook/jest) test runner on your tests. 59 | 60 | #### `npm run ios` 61 | 62 | Like `npm start`, but also attempts to open your app in the iOS Simulator if you're on a Mac and have it installed. 63 | 64 | #### `npm run android` 65 | 66 | Like `npm start`, but also attempts to open your app on a connected Android device or emulator. Requires an installation of Android build tools (see [React Native docs](https://facebook.github.io/react-native/docs/getting-started.html) for detailed setup). We also recommend installing Genymotion as your Android emulator. Once you've finished setting up the native build environment, there are two options for making the right copy of `adb` available to Create React Native App: 67 | 68 | ##### Using Android Studio's `adb` 69 | 70 | 1. Make sure that you can run adb from your terminal. 71 | 2. Open Genymotion and navigate to `Settings -> ADB`. Select “Use custom Android SDK tools” and update with your [Android SDK directory](https://stackoverflow.com/questions/25176594/android-sdk-location). 72 | 73 | ##### Using Genymotion's `adb` 74 | 75 | 1. Find Genymotion’s copy of adb. On macOS for example, this is normally `/Applications/Genymotion.app/Contents/MacOS/tools/`. 76 | 2. Add the Genymotion tools directory to your path (instructions for [Mac](http://osxdaily.com/2014/08/14/add-new-path-to-path-command-line/), [Linux](http://www.computerhope.com/issues/ch001647.htm), and [Windows](https://www.howtogeek.com/118594/how-to-edit-your-system-path-for-easy-command-line-access/)). 77 | 3. Make sure that you can run adb from your terminal. 78 | 79 | #### `npm run eject` 80 | 81 | This will start the process of "ejecting" from Create React Native App's build scripts. You'll be asked a couple of questions about how you'd like to build your project. 82 | 83 | **Warning:** Running eject is a permanent action (aside from whatever version control system you use). An ejected app will require you to have an [Xcode and/or Android Studio environment](https://facebook.github.io/react-native/docs/getting-started.html) set up. 84 | 85 | ## Customizing App Display Name and Icon 86 | 87 | You can edit `app.json` to include [configuration keys](https://docs.expo.io/versions/latest/guides/configuration.html) under the `expo` key. 88 | 89 | To change your app's display name, set the `expo.name` key in `app.json` to an appropriate string. 90 | 91 | To set an app icon, set the `expo.icon` key in `app.json` to be either a local path or a URL. It's recommended that you use a 512x512 png file with transparency. 92 | 93 | ## Writing and Running Tests 94 | 95 | This project is set up to use [jest](https://facebook.github.io/jest/) for tests. You can configure whatever testing strategy you like, but jest works out of the box. Create test files in directories called `__tests__` or with the `.test` extension to have the files loaded by jest. See the [the template project](https://github.com/react-community/create-react-native-app/blob/master/react-native-scripts/template/App.test.js) for an example test. The [jest documentation](https://facebook.github.io/jest/docs/getting-started.html) is also a wonderful resource, as is the [React Native testing tutorial](https://facebook.github.io/jest/docs/tutorial-react-native.html). 96 | 97 | ## Environment Variables 98 | 99 | You can configure some of Create React Native App's behavior using environment variables. 100 | 101 | ### Configuring Packager IP Address 102 | 103 | When starting your project, you'll see something like this for your project URL: 104 | 105 | ``` 106 | exp://192.168.0.2:19000 107 | ``` 108 | 109 | The "manifest" at that URL tells the Expo app how to retrieve and load your app's JavaScript bundle, so even if you load it in the app via a URL like `exp://localhost:19000`, the Expo client app will still try to retrieve your app at the IP address that the start script provides. 110 | 111 | In some cases, this is less than ideal. This might be the case if you need to run your project inside of a virtual machine and you have to access the packager via a different IP address than the one which prints by default. In order to override the IP address or hostname that is detected by Create React Native App, you can specify your own hostname via the `REACT_NATIVE_PACKAGER_HOSTNAME` environment variable: 112 | 113 | Mac and Linux: 114 | 115 | ``` 116 | REACT_NATIVE_PACKAGER_HOSTNAME='my-custom-ip-address-or-hostname' npm start 117 | ``` 118 | 119 | Windows: 120 | ``` 121 | set REACT_NATIVE_PACKAGER_HOSTNAME='my-custom-ip-address-or-hostname' 122 | npm start 123 | ``` 124 | 125 | The above example would cause the development server to listen on `exp://my-custom-ip-address-or-hostname:19000`. 126 | 127 | ## Adding Flow 128 | 129 | Flow is a static type checker that helps you write code with fewer bugs. Check out this [introduction to using static types in JavaScript](https://medium.com/@preethikasireddy/why-use-static-types-in-javascript-part-1-8382da1e0adb) if you are new to this concept. 130 | 131 | React Native works with [Flow](http://flowtype.org/) out of the box, as long as your Flow version matches the one used in the version of React Native. 132 | 133 | To add a local dependency to the correct Flow version to a Create React Native App project, follow these steps: 134 | 135 | 1. Find the Flow `[version]` at the bottom of the included [.flowconfig](.flowconfig) 136 | 2. Run `npm install --save-dev flow-bin@x.y.z` (or `yarn add --dev flow-bin@x.y.z`), where `x.y.z` is the .flowconfig version number. 137 | 3. Add `"flow": "flow"` to the `scripts` section of your `package.json`. 138 | 4. Add `// @flow` to any files you want to type check (for example, to `App.js`). 139 | 140 | Now you can run `npm run flow` (or `yarn flow`) to check the files for type errors. 141 | You can optionally use a [plugin for your IDE or editor](https://flow.org/en/docs/editors/) for a better integrated experience. 142 | 143 | To learn more about Flow, check out [its documentation](https://flow.org/). 144 | 145 | ## Sharing and Deployment 146 | 147 | Create React Native App does a lot of work to make app setup and development simple and straightforward, but it's very difficult to do the same for deploying to Apple's App Store or Google's Play Store without relying on a hosted service. 148 | 149 | ### Publishing to Expo's React Native Community 150 | 151 | Expo provides free hosting for the JS-only apps created by CRNA, allowing you to share your app through the Expo client app. This requires registration for an Expo account. 152 | 153 | Install the `exp` command-line tool, and run the publish command: 154 | 155 | ``` 156 | $ npm i -g exp 157 | $ exp publish 158 | ``` 159 | 160 | ### Building an Expo "standalone" app 161 | 162 | You can also use a service like [Expo's standalone builds](https://docs.expo.io/versions/latest/guides/building-standalone-apps.html) if you want to get an IPA/APK for distribution without having to build the native code yourself. 163 | 164 | ### Ejecting from Create React Native App 165 | 166 | If you want to build and deploy your app yourself, you'll need to eject from CRNA and use Xcode and Android Studio. 167 | 168 | This is usually as simple as running `npm run eject` in your project, which will walk you through the process. Make sure to install `react-native-cli` and follow the [native code getting started guide for React Native](https://facebook.github.io/react-native/docs/getting-started.html). 169 | 170 | #### Should I Use ExpoKit? 171 | 172 | If you have made use of Expo APIs while working on your project, then those API calls will stop working if you eject to a regular React Native project. If you want to continue using those APIs, you can eject to "React Native + ExpoKit" which will still allow you to build your own native code and continue using the Expo APIs. See the [ejecting guide](https://github.com/react-community/create-react-native-app/blob/master/EJECTING.md) for more details about this option. 173 | 174 | ## Troubleshooting 175 | 176 | ### Networking 177 | 178 | If you're unable to load your app on your phone due to a network timeout or a refused connection, a good first step is to verify that your phone and computer are on the same network and that they can reach each other. Create React Native App needs access to ports 19000 and 19001 so ensure that your network and firewall settings allow access from your device to your computer on both of these ports. 179 | 180 | Try opening a web browser on your phone and opening the URL that the packager script prints, replacing `exp://` with `http://`. So, for example, if underneath the QR code in your terminal you see: 181 | 182 | ``` 183 | exp://192.168.0.1:19000 184 | ``` 185 | 186 | Try opening Safari or Chrome on your phone and loading 187 | 188 | ``` 189 | http://192.168.0.1:19000 190 | ``` 191 | 192 | and 193 | 194 | ``` 195 | http://192.168.0.1:19001 196 | ``` 197 | 198 | If this works, but you're still unable to load your app by scanning the QR code, please open an issue on the [Create React Native App repository](https://github.com/react-community/create-react-native-app) with details about these steps and any other error messages you may have received. 199 | 200 | If you're not able to load the `http` URL in your phone's web browser, try using the tethering/mobile hotspot feature on your phone (beware of data usage, though), connecting your computer to that WiFi network, and restarting the packager. 201 | 202 | ### iOS Simulator won't open 203 | 204 | If you're on a Mac, there are a few errors that users sometimes see when attempting to `npm run ios`: 205 | 206 | * "non-zero exit code: 107" 207 | * "You may need to install Xcode" but it is already installed 208 | * and others 209 | 210 | There are a few steps you may want to take to troubleshoot these kinds of errors: 211 | 212 | 1. Make sure Xcode is installed and open it to accept the license agreement if it prompts you. You can install it from the Mac App Store. 213 | 2. Open Xcode's Preferences, the Locations tab, and make sure that the `Command Line Tools` menu option is set to something. Sometimes when the CLI tools are first installed by Homebrew this option is left blank, which can prevent Apple utilities from finding the simulator. Make sure to re-run `npm/yarn run ios` after doing so. 214 | 3. If that doesn't work, open the Simulator, and under the app menu select `Reset Contents and Settings...`. After that has finished, quit the Simulator, and re-run `npm/yarn run ios`. 215 | 216 | ### QR Code does not scan 217 | 218 | If you're not able to scan the QR code, make sure your phone's camera is focusing correctly, and also make sure that the contrast on the two colors in your terminal is high enough. For example, WebStorm's default themes may [not have enough contrast](https://github.com/react-community/create-react-native-app/issues/49) for terminal QR codes to be scannable with the system barcode scanners that the Expo app uses. 219 | 220 | If this causes problems for you, you may want to try changing your terminal's color theme to have more contrast, or running Create React Native App from a different terminal. You can also manually enter the URL printed by the packager script in the Expo app's search bar to load it manually. 221 | --------------------------------------------------------------------------------