Skip to content

Commit

Permalink
Merge pull request #5 from lperson/conversations-query
Browse files Browse the repository at this point in the history
Conversations query
  • Loading branch information
lperson committed Jun 13, 2018
2 parents 6dcea51 + 3286278 commit 789ec60
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 117 deletions.
2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@
"test-coverage": "jest --coverage",
"test-coverage-bothbackends": "jest --config jest.config.sqlite.js && jest --coverage",
"clean": "rm -rf $OUTPUT_DIR",
"clean-node-modules": "rm -rf ./node_modules",
"lint": "eslint --fix --ext js --ext jsx src",
"preinstall": "npm run clean-node-modules",
"prod-build-client": "webpack --config ./webpack/config.js",
"prod-build-server": "babel ./src -d ./build/server --source-maps --copy-files",
"prod-build": "npm run clean && npm run prod-build-client && npm run prod-build-server",
Expand Down
183 changes: 71 additions & 112 deletions src/components/IncomingMessageList.jsx
Original file line number Diff line number Diff line change
@@ -1,77 +1,46 @@
import React, { Component } from 'react'
import type from 'prop-types'
import { Card, CardHeader, CardText } from 'material-ui/Card'
import Dialog from 'material-ui/Dialog';
import FlatButton from 'material-ui/FlatButton';
import ActionOpenInNew from 'material-ui/svg-icons/action/open-in-new';
import Dialog from 'material-ui/Dialog'
import FlatButton from 'material-ui/FlatButton'
import ActionOpenInNew from 'material-ui/svg-icons/action/open-in-new'
import loadData from '../containers/hoc/load-data'
import { withRouter } from 'react-router'
import gql from 'graphql-tag'
import LoadingIndicator from '../components/LoadingIndicator'
import DataTables from 'material-ui-datatables'

import { MESSAGE_STATUSES } from '../components/IncomingMessageFilter';
import { MESSAGE_STATUSES } from '../components/IncomingMessageFilter'

function prepareDataTableData(organization) {
const assignments = getAssignmentsFromOrganization(organization)
const tableData = []
for (const assignment of assignments) {
for (const contact of assignment.contacts) {
tableData.push({
campaignTitle: assignment.campaign.title,
texter: assignment.texter.displayName,
to: contact.firstName + ' ' + contact.lastName,
status: contact.messageStatus,
messages: contact.messages
})
function prepareDataTableData(conversations) {
const tableData = conversations.map(conversation => {
return {
campaignTitle: conversation.campaign.title,
texter: conversation.texter.displayName,
to: conversation.contact.firstName + ' ' + conversation.contact.lastName,
status: conversation.contact.messageStatus,
messages: conversation.contact.messages
}
}
})
return tableData
}

function getAssignmentsFromOrganization(organization) {
const assignments = []
for (const campaign of organization.campaigns) {
for (const assignment of campaign.assignments) {
assignments.push(assignment)
}
}
return assignments
}

function getContactsFromOrganization(organization) {
const assignments = getAssignmentsFromOrganization(organization)
return assignments.reduce((accumulator, assignment) => {
if (!assignment.contacts) {
return accumulator
}
return assignment.contacts.reduce((contactAccumulator, contact) => {
contactAccumulator.push(contact)
return contactAccumulator
}, accumulator)
}, [])
}

function prepareSelectedRowsData(organization, rowsSelected) {
const contacts = getContactsFromOrganization(organization)
function prepareSelectedRowsData(conversations, rowsSelected) {
let thingToIterate = []
if (rowsSelected === 'all') {
thingToIterate = []
for (let i = 0; i < contacts.length; i++) {
for (let i = 0; i < conversations.length; i++) {
thingToIterate.push(i)
}
} else if (rowsSelected !== 'none') {
thingToIterate = rowsSelected
}

return thingToIterate.reduce((returnData, index) => {
const contact = contacts[index]
const conversation= conversations[index]
returnData.push({
campaignId: contact.campaign.id,
campaignContactId: contact.id,
messageIds: contact.messages.map(message => {
return message.id
})
campaignId: conversation.campaign.id,
campaignContactId: conversation.contact.id,
})
return returnData
}, [])
Expand All @@ -84,23 +53,23 @@ export class IncomingMessageList extends Component {
data: [],
page: 1,
rowSize: 10,
activeConversation: undefined,
activeConversation: undefined
}

this.prepareTableColumns = this.prepareTableColumns.bind(this);
this.prepareTableColumns = this.prepareTableColumns.bind(this)
this.handleNextPageClick = this.handleNextPageClick.bind(this)
this.handlePreviousPageClick = this.handlePreviousPageClick.bind(this)
this.handleRowSizeChanged = this.handleRowSizeChanged.bind(this)
this.handleOpenConversation = this.handleOpenConversation.bind(this);
this.handleCloseConversation = this.handleCloseConversation.bind(this);

this.handleOpenConversation = this.handleOpenConversation.bind(this)
this.handleCloseConversation = this.handleCloseConversation.bind(this)
}

componentWillReceiveProps(nextProps) {
if (nextProps.organization.loading) {
if (nextProps.conversations.loading) {
this.setState({ data: [], count: 0, page: 1 })
} else {
const assignments = prepareDataTableData(nextProps.organization.organization)
const assignments = prepareDataTableData(nextProps.conversations.conversations)
this.setState({ data: assignments, count: assignments.length, page: 1 })
}
}
Expand Down Expand Up @@ -128,38 +97,38 @@ export class IncomingMessageList extends Component {
key: 'latestMessage',
label: 'Latest Message',
render: (columnKey, row) => {
let lastMessage = null;
let lastMessage = null
let lastMessageEl = <p>No Messages</p>
if (row.messages.length > 0) {
if (row.messages && row.messages.length > 0) {
lastMessage = row.messages[row.messages.length - 1]
lastMessageEl = (
<p>
<span style={{'color': lastMessage.isFromContact ? 'blue' : 'black'}}>
<span style={{ color: lastMessage.isFromContact ? 'blue' : 'black' }}>
<b>{lastMessage.isFromContact ? 'Contact:' : 'Texter:'} </b>
</span>
{lastMessage.text}
</p>
);
)
}
return lastMessageEl;
return lastMessageEl
}
},
{
key: 'viewConversation',
label: 'View Conversation',
render: (columnKey, row) => {
if (row.messages.length > 0) {
if (row.messages && row.messages.length > 0) {
return (
<FlatButton
onClick={event => {
event.stopPropagation();
this.handleOpenConversation(row);
event.stopPropagation()
this.handleOpenConversation(row)
}}
icon={<ActionOpenInNew />}
/>
);
)
}
return '';
return ''
}
}
]
Expand All @@ -182,20 +151,20 @@ export class IncomingMessageList extends Component {
}

handleOpenConversation(contact) {
this.setState({activeConversation: contact});
};
this.setState({ activeConversation: contact })
}

handleCloseConversation() {
this.setState({activeConversation: undefined});
};
this.setState({ activeConversation: undefined })
}

render() {
const sliceStart = (this.state.page - 1) * this.state.rowSize,
sliceEnd = (this.state.page - 1) * this.state.rowSize + this.state.rowSize;
const tableData = this.state.data.slice(sliceStart, sliceEnd);
sliceEnd = (this.state.page - 1) * this.state.rowSize + this.state.rowSize
const tableData = this.state.data.slice(sliceStart, sliceEnd)
return (
<div>
{this.props.organization.loading ? (
{this.props.conversations.loading ? (
<LoadingIndicator />
) : (
<DataTables
Expand All @@ -218,7 +187,7 @@ export class IncomingMessageList extends Component {
) {
this.props.onConversationSelected(
rowsSelected,
prepareSelectedRowsData(this.props.organization.organization, rowsSelected)
prepareSelectedRowsData(this.props.conversations.conversations, rowsSelected)
)
}
}}
Expand All @@ -231,23 +200,23 @@ export class IncomingMessageList extends Component {
autoScrollBodyContent={true}
onRequestClose={this.handleCloseConversation}
>
{this.state.activeConversation !== undefined &&
{this.state.activeConversation !== undefined && (
<div>
{this.state.activeConversation.messages.map((message, index) => {
const isFromContact = message.isFromContact;
const isFromContact = message.isFromContact
const style = {
'color': isFromContact ? 'blue' : 'black',
'textAlign': isFromContact ? 'left' : 'right',
};
color: isFromContact ? 'blue' : 'black',
textAlign: isFromContact ? 'left' : 'right'
}

return (
<p key={index} style={style}>
{message.text}
</p>
);
)
})}
</div>
}
)}
</Dialog>
</div>
)
Expand All @@ -263,46 +232,36 @@ IncomingMessageList.propTypes = {
}

const mapQueriesToProps = ({ ownProps }) => ({
organization: {
conversations: {
query: gql`
query Q(
$organizationId: String!
$contactsFilter: ContactsFilter
$campaignsFilter: CampaignsFilter
$utc: String
) {
organization(id: $organizationId, utc: $utc) {
id
campaigns(campaignsFilter: $campaignsFilter) {
conversations(
organizationId: $organizationId
campaignsFilter: $campaignsFilter
contactsFilter: $contactsFilter
utc: $utc
) {
texter {
id
displayName
}
contact {
id
firstName
lastName
messageStatus
}
campaign {
id
title
assignments {
campaign {
id
title
}
texter {
id
displayName
}
contacts(contactsFilter: $contactsFilter) {
id
campaign {
id
}
firstName
lastName
messageStatus
messages {
id
createdAt
userNumber
contactNumber
text
isFromContact
}
}
}
}
assignment {
id
}
}
}
Expand Down
62 changes: 62 additions & 0 deletions src/server/api/conversations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import _ from 'lodash'
import User from '../models/user'
import CampaignContact from '../models/campaign-contact'

export const schema = `
input ConversationFilter {
assignmentsFilter: AssignmentsFilter
campaignsFilter: CampaignsFilter
contactsFilter: ContactsFilter
}
type Conversation {
texter: User!
contact: CampaignContact!
campaign: Campaign!
assignment: Assignment
}
`

function coerceQueryFieldsToResolverFields(instance, fieldsToRemove) {
const fields = _.remove(Object.keys(instance), el => {
return !(fieldsToRemove.includes(el))
})
return _.pick(instance, fields)
}

export const resolvers = {
Conversation: {
texter: instance => {
const texterFields = coerceQueryFieldsToResolverFields(instance, [
'u_id',
'u_first_name',
'u_last_name'
])
texterFields.id = instance.u_id
texterFields.last_name = instance.u_first_name
texterFields.first_name = instance.u_last_name
return texterFields
},
contact: instance => {
const contactFields = coerceQueryFieldsToResolverFields(instance, [
'cc_id',
'cc_first_name',
'cc_last_name'
])
contactFields.id = instance.cc_id
contactFields.first_name = instance.cc_first_name
contactFields.last_name = instance.cc_last_name
return contactFields
},
campaign: instance => {
const campaignFields = coerceQueryFieldsToResolverFields(instance, ['cmp_id'])
campaignFields.id = instance.cmp_id
return campaignFields
},
assignment: instance => {
const assignmentFields = coerceQueryFieldsToResolverFields(instance, ['ass_id'])
assignmentFields.id = instance.ass_id
return assignmentFields
}
}
}
Loading

0 comments on commit 789ec60

Please sign in to comment.