From 31e0a1f081cf30212f6ff671f85a4d5443cfa0e5 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Fri, 21 Feb 2020 15:02:56 -0800 Subject: [PATCH] UI Work on chats --- bodyshop_translations.babel | 21 +++++ .../chat-conversation.component.jsx | 89 ++++++++++++++----- .../chat-open-button.component.jsx | 23 +++++ .../chat-overlay/chat-overlay.container.jsx | 9 +- .../jobs-list/jobs-list.component.jsx | 24 ++--- client/src/pages/jobs/jobs.page.jsx | 4 +- client/src/pages/manage/manage.page.jsx | 4 +- .../src/redux/messaging/messaging.actions.js | 10 +++ .../src/redux/messaging/messaging.reducer.js | 29 ++++-- client/src/translations/en_us/common.json | 3 +- client/src/translations/es/common.json | 3 +- client/src/translations/fr/common.json | 3 +- 12 files changed, 163 insertions(+), 59 deletions(-) create mode 100644 client/src/components/chat-open-button/chat-open-button.component.jsx diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index f538d5413..f7d5f1109 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -5346,6 +5346,27 @@ + + typeamessage + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + diff --git a/client/src/components/chat-conversation/chat-conversation.component.jsx b/client/src/components/chat-conversation/chat-conversation.component.jsx index 458e18c37..5d7f095c6 100644 --- a/client/src/components/chat-conversation/chat-conversation.component.jsx +++ b/client/src/components/chat-conversation/chat-conversation.component.jsx @@ -1,11 +1,16 @@ -import { Card } from "antd"; +import { Button, Card, Input, Icon } from "antd"; import React, { useEffect, useState } from "react"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import twilio from "twilio"; -import { toggleConversationVisible } from "../../redux/messaging/messaging.actions"; +import { + closeConversation, + toggleConversationVisible +} from "../../redux/messaging/messaging.actions"; import PhoneFormatter from "../../utils/PhoneFormatter"; import "./chat-conversation.styles.scss"; //https://bootsnipp.com/snippets/exR5v +import { MdSend } from "react-icons/md"; +import { useTranslation } from "react-i18next"; const client = twilio( "ACf1b1aaf0e04740828b49b6e58467d787", @@ -17,20 +22,24 @@ const mapStateToProps = createStructuredSelector({ }); const mapDispatchToProps = dispatch => ({ toggleConversationVisible: conversationId => - dispatch(toggleConversationVisible(conversationId)) + dispatch(toggleConversationVisible(conversationId)), + closeConversation: phone => dispatch(closeConversation(phone)) }); export default connect( mapStateToProps, mapDispatchToProps )(function ChatConversationComponent({ conversation, - toggleConversationVisible + toggleConversationVisible, + closeConversation }) { - const [conversations, setConversations] = useState([]); + const { t } = useTranslation(); + + const [messages, setMessages] = useState([]); useEffect(() => { client.messages.list({ limit: 20 }, (error, items) => { - setConversations( + setMessages( items.reduce((acc, value) => { acc.push({ sid: value.sid, @@ -42,34 +51,68 @@ export default connect( ); }); return () => {}; - }, [setConversations]); + }, [setMessages]); return (
+
toggleConversationVisible(conversation.phone)} + > + {conversation.phone} +
+ +
+ ) : null + } style={{ - width: conversation.open ? "400px" : "125px", + width: conversation.open ? "400px" : "175px", margin: "0px 10px" }} size="small" - onClick={() => toggleConversationVisible(conversation.id)} > {conversation.open ? ( -
- +
+
+
    + {messages.map(item => ( +
  • +

    {item.body}

    +
  • + ))} +
+
+ } + />
) : ( - {conversation.phone} +
+
toggleConversationVisible(conversation.phone)}> + {conversation.phone} +
+ +
)}
diff --git a/client/src/components/chat-open-button/chat-open-button.component.jsx b/client/src/components/chat-open-button/chat-open-button.component.jsx new file mode 100644 index 000000000..bce620e7b --- /dev/null +++ b/client/src/components/chat-open-button/chat-open-button.component.jsx @@ -0,0 +1,23 @@ +import React from "react"; +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +import { openConversation } from "../../redux/messaging/messaging.actions"; +import { Icon } from "antd"; +const mapStateToProps = createStructuredSelector({ + //currentUser: selectCurrentUser +}); +const mapDispatchToProps = dispatch => ({ + openConversation: phone => dispatch(openConversation(phone)) +}); +export default connect( + mapStateToProps, + mapDispatchToProps +)(function ChatOpenButton({ openConversation, phone }) { + return ( + openConversation(phone)} + /> + ); +}); diff --git a/client/src/components/chat-overlay/chat-overlay.container.jsx b/client/src/components/chat-overlay/chat-overlay.container.jsx index 9cbff086f..6302bad0d 100644 --- a/client/src/components/chat-overlay/chat-overlay.container.jsx +++ b/client/src/components/chat-overlay/chat-overlay.container.jsx @@ -1,4 +1,4 @@ -import { Affix, Button, Badge } from "antd"; +import { Affix, Badge } from "antd"; import React from "react"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; @@ -7,8 +7,8 @@ import { selectChatVisible, selectConversations } from "../../redux/messaging/messaging.selectors"; -import ChatOverlayComponent from "./chat-overlay.component"; import ChatConversationContainer from "../chat-conversation/chat-conversation.container"; +import ChatOverlayComponent from "./chat-overlay.component"; const mapStateToProps = createStructuredSelector({ chatVisible: selectChatVisible, @@ -26,9 +26,8 @@ export default connect( toggleChatVisible, conversations }) { - console.log("conversations", conversations); return ( - +
{conversations ? conversations.map((conversation, idx) => ( - + )) diff --git a/client/src/components/jobs-list/jobs-list.component.jsx b/client/src/components/jobs-list/jobs-list.component.jsx index d44fbcc06..1b1ddb888 100644 --- a/client/src/components/jobs-list/jobs-list.component.jsx +++ b/client/src/components/jobs-list/jobs-list.component.jsx @@ -1,11 +1,11 @@ -import { Input, Table, Icon, Button } from "antd"; +import { Button, Icon, Input, Table } from "antd"; import React, { useState } from "react"; import { useTranslation } from "react-i18next"; -import { Link } from "react-router-dom"; +import { Link, withRouter } from "react-router-dom"; +import CurrencyFormatter from "../../utils/CurrencyFormatter"; import PhoneFormatter from "../../utils/PhoneFormatter"; import { alphaSort } from "../../utils/sorters"; -import { withRouter } from "react-router-dom"; -import CurrencyFormatter from "../../utils/CurrencyFormatter"; +import StartChatButton from "../chat-open-button/chat-open-button.component"; export default withRouter(function JobsList({ searchTextState, @@ -78,13 +78,7 @@ export default withRouter(function JobsList({ return record.ownr_ph1 ? ( {record.ownr_ph1} - { - alert("SMSing will happen here."); - }} - /> + ) : ( t("general.labels.unknown") @@ -214,10 +208,10 @@ export default withRouter(function JobsList({ return (
{ setSearchText(e.target.value); }} @@ -226,10 +220,10 @@ export default withRouter(function JobsList({
); }} - size='small' + size="small" pagination={{ position: "top" }} columns={columns.map(item => ({ ...item }))} - rowKey='id' + rowKey="id" dataSource={jobs} rowSelection={{ selectedRowKeys: [selectedJob] }} onChange={handleTableChange} diff --git a/client/src/pages/jobs/jobs.page.jsx b/client/src/pages/jobs/jobs.page.jsx index f8339a99e..41b9cfa15 100644 --- a/client/src/pages/jobs/jobs.page.jsx +++ b/client/src/pages/jobs/jobs.page.jsx @@ -34,10 +34,8 @@ export default connect( const [selectedJob, setSelectedJob] = useState(hash ? hash.substr(1) : null); const searchTextState = useState(""); const searchText = searchTextState[0]; - if (error) return ; - //TODO Implement pagination for this. + if (error) return ; - console.log(typeof searchText); return (
- +
- + ); diff --git a/client/src/redux/messaging/messaging.actions.js b/client/src/redux/messaging/messaging.actions.js index a41cfe5d7..b89ab0ebb 100644 --- a/client/src/redux/messaging/messaging.actions.js +++ b/client/src/redux/messaging/messaging.actions.js @@ -9,3 +9,13 @@ export const toggleConversationVisible = conversationId => ({ type: MessagingActionTypes.TOGGLE_CONVERSATION_VISIBLE, payload: conversationId }); + +export const openConversation = phone => ({ + type: MessagingActionTypes.OPEN_CONVERSATION, + payload: phone +}); + +export const closeConversation = phone => ({ + type: MessagingActionTypes.CLOSE_CONVERSATION, + payload: phone +}); diff --git a/client/src/redux/messaging/messaging.reducer.js b/client/src/redux/messaging/messaging.reducer.js index 9d6a39cab..f9acdf08e 100644 --- a/client/src/redux/messaging/messaging.reducer.js +++ b/client/src/redux/messaging/messaging.reducer.js @@ -3,8 +3,8 @@ import MessagingActionTypes from "./messaging.types"; const INITIAL_STATE = { visible: false, conversations: [ - { id: 1, phone: "6049992002", open: false }, - { id: 2, phone: "6049992991", open: false } + { phone: "6049992002", open: false }, + { phone: "6049992991", open: false } ] }; @@ -21,20 +21,33 @@ const messagingReducer = (state = INITIAL_STATE, action) => { visible: true }; case MessagingActionTypes.OPEN_CONVERSATION: - return { - ...state, - conversations: [...state.conversations, action.payload] - }; + if (state.conversations.find(c => c.phone === action.payload)) + return { + ...state, + conversations: state.conversations.map(c => + c.phone === action.payload ? { ...c, open: true } : c + ) + }; + else + return { + ...state, + conversations: [ + ...state.conversations, + { phone: action.payload, open: true } + ] + }; case MessagingActionTypes.CLOSE_CONVERSATION: return { ...state, - conversations: state.conversations.filter(c => c !== action.paylod) + conversations: state.conversations.filter( + c => c.phone !== action.payload + ) }; case MessagingActionTypes.TOGGLE_CONVERSATION_VISIBLE: return { ...state, conversations: state.conversations.map(c => - c.id === action.payload ? { ...c, open: !c.open } : c + c.phone === action.payload ? { ...c, open: !c.open } : c ) }; default: diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 70cdc0132..28f268853 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -339,7 +339,8 @@ }, "messaging": { "labels": { - "messaging": "Messaging" + "messaging": "Messaging", + "typeamessage": "Send a message..." } }, "notes": { diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index 4ba9c8c48..5d0ad5457 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -339,7 +339,8 @@ }, "messaging": { "labels": { - "messaging": "Mensajería" + "messaging": "Mensajería", + "typeamessage": "Enviar un mensaje..." } }, "notes": { diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 7e5785c94..492d8e413 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -339,7 +339,8 @@ }, "messaging": { "labels": { - "messaging": "Messagerie" + "messaging": "Messagerie", + "typeamessage": "Envoyer un message..." } }, "notes": {