Skip to content
This repository has been archived by the owner on Jul 17, 2020. It is now read-only.

Added Multiple High-Level Functionalities #416

Open
wants to merge 9 commits into
base: staging
Choose a base branch
from
2 changes: 2 additions & 0 deletions client/Router.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import Packing from './modules/food/components/Packing'
import Settings from './modules/settings/SettingsRouter'
import Users from './modules/users/UserRouter'
import Volunteers from './modules/volunteer/VolunteerRouter'
import VolunteerScheduling from './modules/volunteerScheduling/volunteerSchedulingRouter'

import requireRole from './components/router/requireRole'
import SwitchWithNotFound from './components/router/SwitchWithNotFound'
Expand All @@ -42,6 +43,7 @@ const Router = ({history}) =>
<Route path="/customers" component={Customers} />
<Route path="/donors" component={Donors} />
<Route path="/drivers" component={Drivers} />
<Route path="/volunteerScheduling" component={VolunteerScheduling} />
<Route path="/inventory" exact component={canInventory(Inventory)} />
<Route path="/packing" exact component={canPack(Packing)} />
<Route path="/schedule" exact component={canSchedule(Schedule)} />
Expand Down
1 change: 1 addition & 0 deletions client/modules/core/components/sidebar/SidebarMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const SidebarMenu = () =>
<MenuItem title="Customers" path="/customers/list" />
<MenuItem title="Volunteers" path="/volunteers/list" />
<MenuItem title="Donors" path="/donors/list" />
<MenuItem title="Volunteer Scheduling" path="/volunteerScheduling/list" />

<MenuItem title="User Accounts" path="/users/list" />

Expand Down
34 changes: 32 additions & 2 deletions client/modules/customer/components/CustomerList.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@ import React, {Component} from 'react'
import {connect} from 'react-redux'
import {Link} from 'react-router-dom'
import {BootstrapTable, TableHeaderColumn, SizePerPageDropDown} from 'react-bootstrap-table'
import { Button, Modal } from 'react-bootstrap'
import 'react-bootstrap-table/dist/react-bootstrap-table.min.css'

import {fieldTypes} from '../../../../common/constants'
import selectors from '../../../store/selectors'
import {fieldsByType} from '../../../lib/questionnaire-helpers'
import {loadCustomers} from '../reducer'
import {loadCustomers, massUpload} from '../reducer'
import {loadQuestionnaires} from '../../questionnaire/reducers/api'

import {Box, BoxBody, BoxHeader} from '../../../components/box'
import ClientStatusLabel from '../../../components/ClientStatusLabel'
import {Page, PageBody} from '../../../components/page'

import MassImportsModal from './MassImportsModal'

const mapStateToProps = state => ({
customers: selectors.customer.getAll(state),
loading: selectors.customer.loading(state) ||
Expand All @@ -24,15 +27,31 @@ const mapStateToProps = state => ({

const mapDispatchToProps = dispatch => ({
loadCustomers: () => dispatch(loadCustomers()),
loadQuestionnaires: () => dispatch(loadQuestionnaires())
loadQuestionnaires: () => dispatch(loadQuestionnaires()),
massUpload: docs => dispatch(massUpload(docs))
})

class CustomerList extends Component {
constructor(props) {
super(props)
this.state = {
showImportsModal: false
}
}

componentWillMount() {
this.props.loadCustomers()
this.props.loadQuestionnaires()
}

openModal = () => {
this.setState({showImportsModal: true})
}

closeModal = () => {
this.setState({showImportsModal: false})
}

getStatusLabel = (_, customer) => <ClientStatusLabel client={customer} />

getActionButtons = (_, customer) =>
Expand Down Expand Up @@ -70,6 +89,17 @@ class CustomerList extends Component {
loading={loading}
error={loadError}
>

<Button onClick={this.openModal}>Mass Imports</Button>

<Modal show={this.state.showImportsModal} onHide={this.closeModal}>
<MassImportsModal
customers={this.props.customers}
closeModal={this.closeModal}
massUpload={this.props.massUpload}
/>
</Modal>

<BootstrapTable
data={this.formatData()}
keyField="_id"
Expand Down
191 changes: 191 additions & 0 deletions client/modules/customer/components/MassImportsModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import React from 'react'
import { Button } from 'react-bootstrap'
import { Box, BoxHeader, BoxBody } from '../../../components/box'

import CSVReader from 'react-csv-reader'

export default class MassImportsModal extends React.Component {

constructor(props) {
super(props)
this.state = {
validInput: false,
documents: []
}
}

handleInput = data => {
/* Error Checking for Empty File */
if(data.length == 0 || data.length == 1 || data.length == 2 || data[0][0] == "") {
this.printErrorMessage("Empty")
}
/* Error Checking for Header Format */
else if (!this.validateHeaders(data[0])) {
this.printErrorMessage("Format")
}
else {
document.getElementById("error").innerHTML = ""
var info = []

const customers = this.props.customers

for(var i = 1; i < data.length-1; i++) {
if(!this.validateRow(data[i])) {
this.printErrorMessage("Row")
}
else {
var firstName = data[i][0]
var lastName = data[i][1]
var email = data[i][2]
var birthday = new Date(data[i][3])

if(isNaN(birthday.getTime())) {
continue
}


var street = data[i][4]
var city = data[i][5]
var state = data[i][6]
var zip = data[i][7]

// Don't add this entry if it's a duplicate
if(this.isDuplicate(firstName, lastName, email, customers) == true || this.isDuplicate(firstName, lastName, email, info) == true) {
continue
}


/*
** The meta field was taken from the Questionnaire model. I'm not sure what it is,
** but it's a required field.
*/
var fields = []
fields.push({meta: "c523b236-cf40-47fc-94d4-1f5ae9ccd3c1", value: street})
fields.push({meta: "d2cd8ff9-d70a-4316-a970-5c9a11dc0076", value: city})
fields.push({meta: "d4b7113c-9943-4858-893c-1b2512533f46", value: state})
fields.push({meta: "ce470f63-9c6f-401f-8c85-c9fef022be90", value: zip})
fields.push({meta: "8a537855-657a-476f-98cc-3f8c6313532d", value: birthday})

info.push({firstName: firstName, lastName: lastName, email: email, fields: fields})
}
}
if(info.length > 262) {
this.printErrorMessage("Size")
}
else {
this.setState({validInput: true, documents: info})
}
}
}

// Determines if customer is a duplicate
// I use email as well in the edge case that there are 2 people
// with the same name
isDuplicate = (firstName, lastName, email, customers) => {
for(var i = 0; i < customers.length; i++) {
if(customers[i].firstName.toLowerCase() == firstName.toLowerCase() &&
customers[i].lastName.toLowerCase() == lastName.toLowerCase() &&
customers[i].email.toLowerCase() == email.toLowerCase()) {

return true
}
}
return false
}

printErrorMessage = error => {
document.getElementById("error").style.color = "red"
if(error == "Empty") {
document.getElementById("error").innerHTML = "Error: Empty CSV Input File."
}
else if(error == "Format") {
document.getElementById("error").innerHTML = "Error: Invalid Input File Format. Please Use CSV Template."
}
else if(error == "Size") {
document.getElementById("error").innerHTML = "Error: Too Many Entries. 262 is Maximum Number of Entries for One Upload."
}
else if(error == "Row") {
document.getElementById("error").innerHTML = "Warning: File Contains Some Invalid Rows. Rows will be Ignored."
}
this.setState({validInput: false})
}

/* Validates header length and values */
validateHeaders = headers => {
if(headers.length != 8) {
return false
}
else {
var expected = ["First Name", "Last Name", "Email", "Birthday", "Street", "City/Town", "State/Province", "ZIP"]
for(var i = 0; i < headers.length; i++) {
if(headers[i] != expected[i]) {
return false
}
}
}
return true
}

/* Validates that a row has the correct informaiton and no blanks for required fields */
validateRow = row => {
if(row.length != 8) {
return false
}
else {
for(var i = 0; i < row.length; i++) {
if(row[i] == "") {
return false
}
}
}
return true
}


importData = () => {
var docs = this.state.documents
const customer = {
firstName: 'test',
lastName: 'test',
email: '[email protected]'
}
this.props.massUpload({
...customer,
docs: docs
})
this.props.closeModal()
location.reload()
}


handleError = () => {
document.getElementById("error").innerHTML = "Weird error"
}

render = () => {
return (
<Box>
<BoxHeader>
Customer Mass Imports
</BoxHeader>
<BoxBody>
<div id="error"></div>
<CSVReader
cssClass="csv-reader-input"
label="Select a CSV file to import"
onFileLoaded={this.handleInput}
onError={this.handleError}
/>
<div className="pull-right btn-toolbar">
<Button onClick={this.props.closeModal}>Cancel</Button>
<Button className={this.state.validInput && 'btn-success'}
onClick={this.importData}
disabled={!this.state.validInput}>
Import Data
</Button>
</div>
</BoxBody>
</Box>
)
}
}
Loading