RecordList.jsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. var DomainStore = require('../stores').Domains;
  2. var React = require('react');
  3. var deepMerge = require('../util').deepMerge;
  4. var TextName = React.createClass({
  5. getName: function() {
  6. return {
  7. name: this.refs.name.getDOMNode().value.trim()
  8. }
  9. },
  10. clear: function() {
  11. this.refs.name.getDOMNode().value = "";
  12. },
  13. render: function() {
  14. return (
  15. <td><input type="text" ref="name" defaultValue={this.props.record.name} /></td>
  16. )
  17. }
  18. });
  19. var SRVName = React.createClass({
  20. getName: function() {
  21. return {
  22. data: {
  23. service: this.refs.service.getDOMNode().value.trim(),
  24. proto: this.refs.proto.getDOMNode().value
  25. }
  26. }
  27. },
  28. clear: function() {
  29. this.refs.service.getDOMNode().value = "";
  30. this.refs.proto.getDOMNode().value = "_udp";
  31. },
  32. render: function() {
  33. var record = this.props.record;
  34. var data = record && record.data || {};
  35. var protos = ['_udp', '_tcp', '_tls'];
  36. var options = protos.map(function(proto) {
  37. return <option key={proto} value={proto}>{proto}</option>
  38. });
  39. return (
  40. <td className="multi-input">
  41. <div>
  42. <label>Service</label>
  43. <input type="text" ref="service" defaultValue={data.service} />
  44. </div>
  45. <div>
  46. <label>Proto</label>
  47. <select ref="proto" defaultValue={data.proto}>{options}</select>
  48. </div>
  49. </td>
  50. )
  51. }
  52. });
  53. var TextValue = React.createClass({
  54. getValue: function() {
  55. return {
  56. content: this.refs.value.getDOMNode().value.trim()
  57. }
  58. },
  59. clear: function() {
  60. this.refs.value.getDOMNode().value = "";
  61. },
  62. render: function() {
  63. return (
  64. <td><input type="text" ref="value" defaultValue={this.props.record.content} /></td>
  65. )
  66. }
  67. });
  68. var MXValue = React.createClass({
  69. getValue: function() {
  70. return {
  71. content: this.refs.value.getDOMNode().value.trim(),
  72. priority: this.refs.priority.getDOMNode().value.trim()
  73. }
  74. },
  75. clear: function() {
  76. this.refs.value.getDOMNode().value = "";
  77. this.refs.priority.getDOMNode().value = "";
  78. },
  79. render: function() {
  80. return (
  81. <td className="multi-input">
  82. <div>
  83. <label>Domain</label>
  84. <input type="text" ref="value" defaultValue={this.props.record.content} />
  85. </div>
  86. <div>
  87. <label>Priority</label>
  88. <input type="number" ref="priority" minValue="0" maxValue="65535" defaultValue={this.props.record.priority} />
  89. </div>
  90. </td>
  91. )
  92. }
  93. });
  94. var SRVValue = React.createClass({
  95. getValue: function() {
  96. return {
  97. data: {
  98. name: this.refs.name.getDOMNode().value.trim(),
  99. priority: this.refs.priority.getDOMNode().value.trim(),
  100. weight: this.refs.weight.getDOMNode().value.trim(),
  101. port: this.refs.port.getDOMNode().value.trim(),
  102. target: this.refs.target.getDOMNode().value.trim()
  103. }
  104. }
  105. },
  106. clear: function() {
  107. this.refs.name.getDOMNode().value = "";
  108. this.refs.priority.getDOMNode().value = "";
  109. this.refs.weight.getDOMNode().value = "";
  110. this.refs.port.getDOMNode().value = "";
  111. this.refs.target.getDOMNode().value = "";
  112. },
  113. render: function() {
  114. var record = this.props.record;
  115. var data = record && record.data || {};
  116. return (
  117. <td className="multi-input">
  118. <div>
  119. <label>Name</label>
  120. <input type="text" ref="name" defaultValue={data.name} />
  121. </div>
  122. <div>
  123. <label>Priority</label>
  124. <input type="number" ref="priority" minValue="0" maxValue="65535" defaultValue={data.priority} />
  125. </div>
  126. <div>
  127. <label>Weight</label>
  128. <input type="number" ref="weight" minValue="0" maxValue="65535" defaultValue={data.weight} />
  129. </div>
  130. <div>
  131. <label>Port</label>
  132. <input type="number" ref="port" minValue="0" maxValue="65535" defaultValue={data.port} />
  133. </div>
  134. <div>
  135. <label>Target</label>
  136. <input type="text" ref="target" defaultValue={data.target} />
  137. </div>
  138. </td>
  139. )
  140. }
  141. });
  142. function getNameComponent(type) {
  143. switch(type) {
  144. case 'SRV':
  145. return SRVName;
  146. default:
  147. return TextName;
  148. }
  149. }
  150. function getValueComponent(type) {
  151. switch(type) {
  152. case 'MX':
  153. return MXValue;
  154. case 'SRV':
  155. return SRVValue;
  156. default:
  157. return TextValue;
  158. }
  159. }
  160. var CloudActive = React.createClass({
  161. render: function() {
  162. var record = this.props.record;
  163. if(record.proxiable.val()) {
  164. var active = record.proxied.val();
  165. if(active) {
  166. return <button className='btn btn-warning' onClick={this.props.onClick}>On</button>
  167. }
  168. else {
  169. return <button className='btn btn-default' onClick={this.props.onClick}>Off</button>
  170. }
  171. }
  172. else {
  173. return <span></span>;
  174. }
  175. }
  176. });
  177. var RecordCreate = React.createClass({
  178. getInitialState: function() {
  179. return {saving: false, type: 'A'};
  180. },
  181. types: ['A', 'AAAA', 'CNAME', 'LOC', 'MX', 'NS', 'SPF', 'SRV', 'TXT'],
  182. changeType: function(event) {
  183. this.setState({ type: event.target.value });
  184. },
  185. finishSave: function(promise) {
  186. promise.then(function() {
  187. this.setState({saving: false});
  188. this.reset();
  189. }.bind(this));
  190. },
  191. reset: function() {
  192. this.refs.name.clear();
  193. this.refs.value.clear();
  194. },
  195. commitAdd: function() {
  196. this.setState({saving: true});
  197. var newRecord = deepMerge({
  198. type: this.refs.type.getDOMNode().value,
  199. ttl: 1 // automatic
  200. }, this.refs.name.getName(), this.refs.value.getValue());
  201. this.finishSave(DomainStore.recordAdd(this.props.domain, newRecord));
  202. },
  203. render: function() {
  204. var className = this.state.saving ? 'saving' : '';
  205. var options = this.types.map(function(type) {
  206. return <option key={type} value={type}>{type}</option>
  207. });
  208. var Name = getNameComponent(this.state.type);
  209. var Value = getValueComponent(this.state.type);
  210. return (
  211. <tr className={className}>
  212. <td>
  213. <select ref="type" onChange={this.changeType}>
  214. {options}
  215. </select>
  216. </td>
  217. <Name ref="name" record={{}} />
  218. <Value ref="value" record={{}} />
  219. <td></td>
  220. <td>
  221. <button className="btn btn-success" onClick={this.commitAdd}>Add</button>
  222. </td>
  223. </tr>
  224. )
  225. }
  226. });
  227. var Record = React.createClass({
  228. getInitialState: function() {
  229. return {state: 'view', saving: false};
  230. },
  231. componentWillReceiveProps: function() {
  232. this.setState({state: 'view', saving: false});
  233. },
  234. setDeleting: function() {
  235. this.setState({state: 'delete'});
  236. },
  237. setEditing: function() {
  238. this.setState({state: 'edit'});
  239. },
  240. cancelEdit: function() {
  241. this.setState({state: 'view'});
  242. },
  243. commitDelete: function() {
  244. this.setState({saving: true});
  245. var record = this.props.record;
  246. DomainStore.recordDelete(record.zone_name.val(), record.id.val());
  247. },
  248. commitEdit: function() {
  249. this.setState({saving: true});
  250. var record = this.props.record;
  251. var newRecord = deepMerge({
  252. id: record.id.val(),
  253. type: record.type.val(),
  254. ttl: 1 // automatic
  255. }, this.refs.name.getName(), this.refs.value.getValue());
  256. if(record.proxied.val()) {
  257. newRecord.proxied = record.proxied.val();
  258. }
  259. DomainStore.recordEdit(record.zone_name.val(), newRecord);
  260. },
  261. toggleProxy: function() {
  262. this.setState({saving: true});
  263. var record = this.props.record;
  264. var newRecord = {
  265. id: record.id.val(),
  266. type: record.type.val(),
  267. name: record.name.val(),
  268. content: record.content.val(),
  269. proxied: !record.proxied.val(),
  270. ttl: 1
  271. };
  272. DomainStore.recordEdit(record.zone_name.val(), newRecord);
  273. },
  274. render: function() {
  275. var record = this.props.record;
  276. var className = this.state.saving ? 'saving' : '';
  277. var displayName = record.name.val();
  278. var zoneName = '.'+record.zone_name.val();
  279. var limit = displayName.length - zoneName.length;
  280. if(limit > 0 && displayName.substring(limit) === zoneName) {
  281. displayName = displayName.substring(0, limit);
  282. }
  283. if(this.state.state === 'edit') {
  284. var Name = getNameComponent(record.type.val());
  285. var Value = getValueComponent(record.type.val());
  286. return (
  287. <tr className={className}>
  288. <td className="record-type"><span className={record.type.val()}>{record.type.val()}</span></td>
  289. <Name ref="name" record={record.val()} />
  290. <Value ref="value" record={record.val()} />
  291. <td>
  292. <a onClick={this.cancelEdit}>Cancel</a>
  293. </td>
  294. <td>
  295. <button className="btn btn-success" onClick={this.commitEdit}>Save</button>
  296. </td>
  297. </tr>
  298. );
  299. }
  300. else if(this.state.state === 'delete') {
  301. return (
  302. <tr className={className}>
  303. <td className="record-type"><span className={record.type.val()}>{record.type.val()}</span></td>
  304. <td><strong>{displayName}</strong></td>
  305. <td>{record.content.val()}</td>
  306. <td>
  307. <a onClick={this.cancelEdit}>Cancel</a>
  308. </td>
  309. <td>
  310. <button className="btn btn-danger" onClick={this.commitDelete}>Delete</button>
  311. </td>
  312. </tr>
  313. );
  314. }
  315. else {
  316. return (
  317. <tr className={className}>
  318. <td className="record-type"><span className={record.type.val()}>{record.type.val()}</span></td>
  319. <td><strong>{displayName}</strong></td>
  320. <td className="value">{record.content.val()}</td>
  321. <td><CloudActive record={record} onClick={this.toggleProxy} /></td>
  322. <td className="actions">
  323. <button className="btn btn-primary" onClick={this.setEditing}>Edit</button>
  324. <span> </span>
  325. <button className="btn btn-danger" onClick={this.setDeleting}>Delete</button>
  326. </td>
  327. </tr>
  328. );
  329. }
  330. }
  331. });
  332. var RecordList = React.createClass({
  333. render: function() {
  334. var records = this.props.records.map(function(record) {
  335. return <Record key={record.id.val()} record={record} />
  336. }.bind(this));
  337. var body;
  338. if(records.length === 0) {
  339. body = (
  340. <tbody>
  341. <tr>
  342. <td colSpan="5">Loading...</td>
  343. </tr>
  344. </tbody>
  345. );
  346. }
  347. else {
  348. body = (
  349. <tbody>
  350. {records}
  351. <RecordCreate domain={this.props.domain} />
  352. </tbody>
  353. );
  354. }
  355. return (
  356. <div id="records">
  357. <table className="table">
  358. <thead>
  359. <tr>
  360. <th className="type">Type</th>
  361. <th className="name">Name</th>
  362. <th className="value">Value</th>
  363. <th className="proxy">Proxy</th>
  364. <th className="actions">Actions</th>
  365. </tr>
  366. </thead>
  367. {body}
  368. </table>
  369. </div>
  370. );
  371. }
  372. });
  373. module.exports = RecordList;