index.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. var React = require('react');
  2. var ReactDOM = require('react-dom');
  3. var Remarkable = require('remarkable');
  4. var Button = require('react-bootstrap').Button;
  5. var ButtonToolbar = require('react-bootstrap').ButtonToolbar;
  6. var FormControl = require('react-bootstrap').FormControl;
  7. var FormGroup = require('react-bootstrap').FormGroup;
  8. var Panel = require('react-bootstrap').Panel;
  9. var Modal = require('react-bootstrap').Modal;
  10. var DatePicker = require('react-bootstrap-date-picker');
  11. var FontAwesome = require('react-fontawesome');
  12. var dateFormat = require('dateformat');
  13. var $ = require('jquery');
  14. const API_URL = "https://todo.tankernn.eu/php/api.php";
  15. const priorityNames = {1: "danger", 2: "warning", 3: "info", 4: "success"};
  16. function dateToString(date) {
  17. return dateFormat(date, "yyyy-mm-dd");
  18. }
  19. var TodoForm = React.createClass({
  20. getInitialState: function() {
  21. return {showModal: false, title: this.props.title, text: this.props.text, deadline: this.props.deadline, priority: this.props.priority};
  22. },
  23. getDefaultProps: function() {
  24. return {edit: false};
  25. },
  26. closeModal() {
  27. this.setState({ showModal: false });
  28. },
  29. openModal() {
  30. this.setState({ showModal: true });
  31. },
  32. handleTitleChange: function(e) {
  33. this.setState({title: e.target.value});
  34. },
  35. handleDateChange: function(dateString) {
  36. this.setState({deadline: dateString});
  37. console.log(dateToString(dateString));
  38. },
  39. handlePriorityChange: function(e) {
  40. this.setState({priority: e.target.value});
  41. },
  42. handleTextChange: function(e) {
  43. this.setState({text: e.target.value});
  44. },
  45. handleSubmit: function(e) {
  46. e.preventDefault();
  47. var title = this.state.title.trim();
  48. var text = this.state.text.trim();
  49. var priority = this.state.priority;
  50. var deadline = dateToString(this.state.deadline);
  51. console.log(deadline);
  52. if (!title || !text || !deadline) {
  53. return;
  54. }
  55. this.props.onCommentSubmit({a: (this.props.edit ? 'edit' : 'add'), id: -1, title: title, text: text, deadline: deadline, priority: priority});
  56. this.closeModal();
  57. if (!this.props.edit)
  58. this.setState(this.getInitialState());
  59. },
  60. render: function() {
  61. return (
  62. <div>
  63. <Button bsStyle="primary" onClick={this.openModal}><FontAwesome name='edit' /> {this.props.edit ? "Edit" : "Add"}</Button>
  64. <Modal show={this.state.showModal} onHide={this.closeModal}>
  65. <form name="todoForm" onSubmit={this.handleSubmit}>
  66. <Modal.Header closeButton>
  67. <Modal.Title>TODO-Form</Modal.Title>
  68. </Modal.Header>
  69. <Modal.Body>
  70. <FormControl
  71. type="text"
  72. placeholder="Title"
  73. value={this.state.title}
  74. onChange={this.handleTitleChange}
  75. />
  76. <FormControl componentClass="select" value={this.state.priority} onChange={this.handlePriorityChange}>
  77. <option value={1}>First priority</option>
  78. <option value={2}>Second priority</option>
  79. <option value={3}>Not very important</option>
  80. <option value={4}>Only if you are bored</option>
  81. </FormControl>
  82. <DatePicker
  83. value={this.state.deadline}
  84. onChange={this.handleDateChange}
  85. />
  86. <br />
  87. <FormControl
  88. componentClass="textarea"
  89. value={this.state.text}
  90. onChange={this.handleTextChange} />
  91. </Modal.Body>
  92. <Modal.Footer>
  93. <ButtonToolbar>
  94. <Button bsStyle="primary" type="submit"><FontAwesome name='save' /> Save</Button>
  95. <Button onClick={this.closeModal}><FontAwesome name='ban' /> Cancel</Button>
  96. </ButtonToolbar>
  97. </Modal.Footer>
  98. </form>
  99. </Modal>
  100. </div>
  101. );
  102. }
  103. });
  104. var Item = React.createClass({
  105. rawMarkup: function() {
  106. var md = new Remarkable();
  107. var rawMarkup = md.render(this.props.children.toString());
  108. return { __html: rawMarkup };
  109. },
  110. handleEditSubmit: function(data) {
  111. data.a = 'edit';
  112. data.id = this.props.id;
  113. this.props.handleEditSubmit(data);
  114. },
  115. handleDeleteClick: function() {
  116. console.log("Deleting " + this.props.id);
  117. this.props.handleDeleteClick(this.props.id);
  118. },
  119. render: function() {
  120. var md = new Remarkable();
  121. var daysLeft = Math.ceil((new Date(this.props.deadline) - new Date()) / (1000 * 60 * 60 * 24));
  122. if (isNaN(daysLeft)) {
  123. daysLeft = "No deadline.";
  124. } else if (daysLeft > 1) {
  125. daysLeft += " days left.";
  126. } else if (daysLeft == 1) {
  127. daysLeft = "One day left.";
  128. } else if (daysLeft == 0) {
  129. daysLeft = "Deadline today!";
  130. } else {
  131. daysLeft = "Should have been done " + Math.abs(daysLeft) + " day(s) ago."
  132. }
  133. var deadline = new Date(this.props.deadline);
  134. if (isNaN(deadline))
  135. deadline = new Date();
  136. var todoForm = <TodoForm
  137. title={this.props.title}
  138. text={this.props.children.toString()}
  139. deadline={deadline.toISOString()}
  140. priority={this.props.priority}
  141. onCommentSubmit={this.handleEditSubmit}
  142. edit={true}
  143. />;
  144. return (
  145. <Panel
  146. header={<header><span className="deadline">{daysLeft}</span><h3>{this.props.title}</h3></header>}
  147. footer={
  148. <ButtonToolbar>
  149. {todoForm}
  150. <Button bsStyle="danger" onClick={this.handleDeleteClick}><FontAwesome name='trash' /> Delete</Button>
  151. </ButtonToolbar>
  152. }
  153. bsStyle={priorityNames[this.props.priority]}
  154. >
  155. <span dangerouslySetInnerHTML={this.rawMarkup()} />
  156. </Panel>
  157. );
  158. }
  159. });
  160. var TodoList = React.createClass({
  161. render: function() {
  162. console.log(this.props.list);
  163. var onDeleteClick = this.props.onDeleteClick;
  164. var handleEditSubmit = this.props.handleEditSubmit;
  165. var itemList = this.props.list.map(function(item) {
  166. return (
  167. <Item handleDeleteClick={onDeleteClick} handleEditSubmit={handleEditSubmit} priority={item.priority} title={item.title} id={item.id} key={item.id} deadline={item.deadline}>
  168. {item.description}
  169. </Item>
  170. );
  171. });
  172. return (
  173. <div className="list">
  174. {itemList}
  175. </div>
  176. );
  177. }
  178. });
  179. var App = React.createClass({
  180. getInitialState: function() {
  181. return {list: [], result: 0};
  182. },
  183. updateList: function() {
  184. $.ajax({
  185. url: this.props.url,
  186. dataType: 'json',
  187. type: 'GET',
  188. success: function(data) {
  189. // Not logged in
  190. if (data.result == 1) {
  191. document.location.href = "https://login.tankernn.eu/";
  192. }
  193. // Success
  194. this.setState({list: data.list, result: data.result});
  195. }.bind(this),
  196. error: function(xhr, status, err) {
  197. console.error(this.props.url, status, err.toString());
  198. }.bind(this)
  199. });
  200. },
  201. componentDidMount: function() {
  202. this.updateList();
  203. this.timer = setInterval(this.updateList, 5000);
  204. },
  205. componentWillUnmount: function() {
  206. clearInterval(this.timer);
  207. },
  208. handleCommentSubmit: function(comment) {
  209. $.ajax({
  210. url: this.props.url,
  211. dataType: 'json',
  212. cache: false,
  213. type: 'POST',
  214. data: comment,
  215. success: function(data) {
  216. if (data.result != 0) {
  217. console.log("Error in API: " + data.result);
  218. }
  219. if (data.hasOwnProperty("message")) {
  220. console.log("API message: " + data.message);
  221. }
  222. this.setState({list: data.list, result: data.result});
  223. }.bind(this),
  224. error: function(xhr, status, err) {
  225. console.error(this.props.url, status, err.toString());
  226. this.setState({result: 1});
  227. }.bind(this)
  228. });
  229. },
  230. handleDelete: function(id) {
  231. $.ajax({
  232. url: this.props.url,
  233. dataType: 'json',
  234. cache: false,
  235. type: 'POST',
  236. data: {a: 'rm', id: id},
  237. success: function(data) {
  238. this.setState({list: data.list, result: data.result});
  239. }.bind(this),
  240. error: function(xhr, status, err) {
  241. console.error(this.props.url, status, err.toString());
  242. }.bind(this)
  243. });
  244. },
  245. render: function() {
  246. return (
  247. <main>
  248. <h1>Tankernn TODO list</h1>
  249. <TodoForm title={""} text={""} deadline={new Date().toISOString()} priority={1} onCommentSubmit={this.handleCommentSubmit} />
  250. <TodoList onDeleteClick={this.handleDelete} handleEditSubmit={this.handleCommentSubmit} list={this.state.list} />
  251. </main>
  252. );
  253. }
  254. });
  255. ReactDOM.render(<App url={API_URL} />, document.getElementById('content'));