diff --git a/.gitignore b/.gitignore index 7152fda..448da8b 100755 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ node_modules/ .DS_Store ui/node_modules/ ui/build/ +.env diff --git a/ui/src/App.css b/ui/src/App.css index b226b83..678751b 100755 --- a/ui/src/App.css +++ b/ui/src/App.css @@ -1,27 +1,33 @@ .app { width: 100%; + height: 100%; +} + +.cols{ + display: flex; + height: 100%; } .left-nav { width: 300px; - background-color: #444444; - color: #EEEEEE; + background-color: #2f4050; + color: #a7b1c2; font-size: 13px; + overflow: auto; } .logo { height: 80px; width: 100%; - padding-left: 60px; - padding-right: 60px; - padding-top: 22px; - padding-bottom: 22px; - background-color: #333333; + background-color: #2B3C4B; + display: flex; + align-items: center; + justify-content: center; } .left-nav-item { padding-left: 30px; - margin-top: 30px; + margin-top: 20px; margin-bottom: 20px; } @@ -31,10 +37,13 @@ margin-right: 10px; } +.topic-select.form-control:not([size]):not([multiple]){ + height: 32px; +} + .filter { - padding-left: 0px; - text-align: center; - font-style: italic; + padding-left: 30px; + margin-bottom: 10px; } .filter-control { @@ -45,14 +54,33 @@ color: #000000; } +.timestamp-control .DayPickerInput{ + width: 100%; +} + +.timestamp-control input{ + display: block; + width: 90%; + padding: .375rem .75rem; + font-size: 13px; + line-height: 1.5; + color: #495057; + background-color: #fff; + background-clip: padding-box; + border: 1px solid #ced4da; + border-radius: .25rem; + transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out; +} + .apply-button { margin-left: 30px; } .main-content { - background-color: #F5F5F5; - padding: 20px; + background-color: #f3f3f4; width: calc(100% - 300px); + display: flex; + flex-direction: column; } .error-msg { @@ -60,6 +88,15 @@ margin-bottom: 30px; } +.page-heading{ + font-size: 24px; + padding-top: 20px; + padding-bottom: 10px; + padding-left: 25px; + font-weight: 100; + background-color: #fff; +} + .message-input { width: 75%; margin-right: 20px; @@ -72,18 +109,14 @@ } .view-button { - vertical-align: top; + vertical-align: bottom; } .get-button { - vertical-align: top; + vertical-align: bottom; margin-left: 10px; } -.send-message-button { - vertical-align: text-bottom; -} - .loading-img-container { position: fixed; top: 0; @@ -101,3 +134,107 @@ left: 50%; z-index: 99999; } + +.btn{ + font-size: 12px; +} + +.form-control{ + font-size: 13px; + margin-top: 5px; +} + +.form-control:focus { + border-color: #ced4da; + box-shadow: none; +} + +.page-body{ + padding: 25px 25px 70px; + position: relative; + height: calc(100% - 66px); +} + +.table{ + background-color: #fff; + table-layout: fixed; +} + +.table thead th{ + border: none; +} + +.table thead th:nth-child(1){ + width: 200px; +} + +.table thead th:nth-child(3){ + width: 220px; +} + +.table thead th:nth-child(4){ + width: 200px; +} + +.table td{ + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.table td:nth-child(2){ + cursor: pointer; +} + +.table tr{ + border-bottom: 1px solid #e7eaec; +} + +.btn-primary, +.btn-primary:not(:disabled):not(.disabled):active{ + background-color: #f8ac59; + border-color: #f8ac59; +} + +.btn-primary:hover{ + background-color: #f7a54a; + border-color: #f7a54a; +} + +.btn-primary:focus{ + box-shadow: none; +} + +.send-message-button, +.send-message-button:not(:disabled):not(.disabled):active { + vertical-align: text-bottom; + background-color: #23c6c8; + border-color: #23c6c8; +} + +.send-message-button:hover { + background-color: #21b9bb; + border-color: #21b9bb; +} + +.selected-topic{ + color: #676a6c; +} + +.msg-input-group{ + display: flex; + align-items: center; + position: absolute; + bottom: 25px; + left: 25px; + right: 20px; +} + +.msg-input-group .form-control{ + margin-top: 1px; +} + +.table-wrap{ + height: 100%; + overflow: auto; +} diff --git a/ui/src/App.js b/ui/src/App.js index 370e63f..1645420 100755 --- a/ui/src/App.js +++ b/ui/src/App.js @@ -1,5 +1,4 @@ import React, { Component } from 'react'; -import logo from './logo.png'; import loadingImg from './loading.gif'; import './App.css'; import DayPickerInput from 'react-day-picker/DayPickerInput'; @@ -8,6 +7,8 @@ import API from './services/API'; import _ from 'lodash'; import config from './config/config'; import moment from 'moment'; +import logo from './logo.png'; +import Modal from './Modal'; const defaultMsgCount = Number(config.DEFAULT_MESSAGE_COUNT); @@ -56,7 +57,9 @@ class App extends Component { this.applyFilters = this.applyFilters.bind(this); this.changeExtraMsgCount = this.changeExtraMsgCount.bind(this); this.sendMessage = this.sendMessage.bind(this); + this.handleKeyPress = this.handleKeyPress.bind(this); this.setupWS = this.setupWS.bind(this); + this.toggleMsgModal = this.toggleMsgModal.bind(this); } setupWS() { @@ -104,12 +107,22 @@ class App extends Component { componentDidMount() { const that = this; + this.setState({ + loading: true, + }); + // get topics API.getAllTopics((err, topics) => { if (err) { - that.setState({ errorMsg: err }); + that.setState({ + errorMsg: err, + loading: false, + }); } else { - that.setState({ topics }); + that.setState({ + topics, + loading: false, + }); } }); @@ -262,25 +275,50 @@ class App extends Component { this.setState({ errorMsg: 'Message can not be empty.' }); return; } + if (!this.state.selectedTopic) { this.setState({ errorMsg: 'Please select topic to view.' }); return; } + if (this.state.loading) { + return; + } + this.setState({ loading: true, errorMsg: null }); const that = this; API.sendMessageToKafka(this.state.selectedTopic, this.state.messageToSend, (err) => { if (err) { that.setState({ loading: false, errorMsg: err }); } else { - that.setState({ loading: false }); + that.setState({ loading: false, messageToSend: '' }); } }); } + handleKeyPress(e) { + if (e.key === 'Enter') { + this.sendMessage(); + } + } + + toggleMsgModal(msg){ + if(msg){ + if(msg.payload){ + this.setState({ + selectedMsg: msg + }) + } + }else{ + this.setState({ + selectedMsg: null + }) + } + } + render() { const { topics, tempOriginator, tempPayload, tempMimetype, tempStartDate, tempEndDate, - messageToSend, extraMsgCount, errorMsg, loading } = this.state; + messageToSend, extraMsgCount, errorMsg, loading, selectedTopic, selectedMsg} = this.state; const filteredMessages = this.filter(); return ( @@ -288,9 +326,11 @@ class App extends Component { { loading &&
Loading...
} -
+
- logo +
+ logo +
Topic:
this.setState({ tempOriginator: e.target.value })}> + onChange={(e) => this.setState({ tempOriginator: e.target.value })} />
Payload:
this.setState({ tempPayload: e.target.value })}> + onChange={(e) => this.setState({ tempPayload: e.target.value })} />
Mime-Type:
this.setState({ tempMimetype: e.target.value })}> + onChange={(e) => this.setState({ tempMimetype: e.target.value })} />
Timestamp Start:
this.handleDayChange('start', day)} /> + onDayChange={(day) => this.handleDayChange('start', day)} className="form-control"/>
@@ -338,44 +378,57 @@ class App extends Component {
Specify Additional Offset (Offset of 20 already applied. This will get added to it.)
this.changeExtraMsgCount(Number(e.target.value))}> + onChange={(e) => this.changeExtraMsgCount(Number(e.target.value))} />
- { errorMsg &&
- {errorMsg} -
} - - - - - - - - - - - - { _.map(filteredMessages, (msg, ind) => ( - - - - - - - - )) } - -
TopicOriginatorPayloadTimestampMime-Type
{msg.topic || ''}{msg.originator || ''}{msg.payload ? JSON.stringify(msg.payload) : ''}{msg.timestamp || ''}{msg['mime-type'] || ''}
-
- Send message in selected topic:
- - +
+ Topic{selectedTopic ? (: {selectedTopic}) : ''} +
+
+ { errorMsg &&
+ {errorMsg} +
} +
+ + + + + + + + + + + { _.map(filteredMessages, (msg, ind) => ( + + + + + + + )) } + +
OriginatorPayloadTimestampMime-Type
{msg.originator || ''}this.toggleMsgModal(msg)}>{msg.payload ? JSON.stringify(msg.payload) : ''}{msg.timestamp || ''}{msg['mime-type'] || ''}
+
+
+ this.setState({ messageToSend: e.target.value })} + onKeyPress={this.handleKeyPress} + /> + +
+
- + { + selectedMsg&& + this.toggleMsgModal()}/> + }
); } diff --git a/ui/src/Modal.css b/ui/src/Modal.css new file mode 100644 index 0000000..47bcd8f --- /dev/null +++ b/ui/src/Modal.css @@ -0,0 +1,50 @@ +.modal-wrap{ + z-index: 10; + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; +} + +.modal-back{ + background-color: rgba(0, 0, 0, .15); + position: absolute; + top: 0; + bottom: 0; + right: 0; + left: 0; +} + +.modal-bd{ + position: absolute; + background-color: #fff; + max-width: 600px; + top: 80px; + left: 0; + right: 0; + margin: 0 auto; + z-index: 10; + border-radius: 4px; + padding: 40px 20px 20px; +} + +.modal-bd-content{ + max-height: 540px; + overflow: auto; + height: 100%; +} + +.modal-close{ + font-weight: 100; + font-size: 30px; + line-height: 30px; + position: absolute; + cursor: pointer; + top: 10px; + right: 10px; +} + +.text-content{ + word-break: break-all; +} \ No newline at end of file diff --git a/ui/src/Modal.js b/ui/src/Modal.js new file mode 100644 index 0000000..1440997 --- /dev/null +++ b/ui/src/Modal.js @@ -0,0 +1,29 @@ +import React from 'react'; +import _ from 'lodash'; +import './Modal.css'; + +export default function Modal({message, onClose}){ + return ( +
+
+
+
×
+
+ { + _.isObject(message.payload) ? + ( + +
+                  {
+                    JSON.stringify(message.payload, null, 2)
+                  }
+                
+
+ ) : + (
{message.payload}
) + } +
+
+
+ ) +} \ No newline at end of file diff --git a/ui/src/index.css b/ui/src/index.css index b4cc725..c21b446 100755 --- a/ui/src/index.css +++ b/ui/src/index.css @@ -1,5 +1,15 @@ -body { +html, body{ margin: 0; padding: 0; + height: 100%; +} + +body { font-family: sans-serif; + font-size: 13px; + min-width: 1280px; } + +#root{ + height: 100%; +} \ No newline at end of file diff --git a/ui/src/logo.png b/ui/src/logo.png old mode 100755 new mode 100644