News.vue 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. <template>
  2. <div>
  3. <div class="container">
  4. <table class="table is-striped">
  5. <thead>
  6. <tr>
  7. <td>Title</td>
  8. <td>Description</td>
  9. <td>Bugs</td>
  10. <td>Features</td>
  11. <td>Improvements</td>
  12. <td>Upcoming</td>
  13. <td>Options</td>
  14. </tr>
  15. </thead>
  16. <tbody>
  17. <tr v-for="(news, index) in news" :key="index">
  18. <td>
  19. <strong>{{ news.title }}</strong>
  20. </td>
  21. <td>{{ news.description }}</td>
  22. <td>{{ news.bugs.join(', ') }}</td>
  23. <td>{{ news.features.join(', ') }}</td>
  24. <td>{{ news.improvements.join(', ') }}</td>
  25. <td>{{ news.upcoming.join(', ') }}</td>
  26. <td>
  27. <button class="button is-primary" v-on:click="editNews(news)">Edit</button>
  28. <button class="button is-danger" v-on:click="removeNews(news)">Remove</button>
  29. </td>
  30. </tr>
  31. </tbody>
  32. </table>
  33. <div class="card is-fullwidth">
  34. <header class="card-header">
  35. <p class="card-header-title">Create News</p>
  36. </header>
  37. <div class="card-content">
  38. <div class="content">
  39. <label class="label">Title & Description</label>
  40. <div class="control is-horizontal">
  41. <div class="control is-grouped">
  42. <p class="control is-expanded">
  43. <input class="input" type="text" placeholder="Title" v-model="creating.title" />
  44. </p>
  45. <p class="control is-expanded">
  46. <input
  47. class="input"
  48. type="text"
  49. placeholder="Short description"
  50. v-model="creating.description"
  51. />
  52. </p>
  53. </div>
  54. </div>
  55. <div class="columns">
  56. <div class="column">
  57. <label class="label">Bugs</label>
  58. <p class="control has-addons">
  59. <input
  60. class="input"
  61. id="new-bugs"
  62. type="text"
  63. placeholder="Bug"
  64. v-on:keyup.enter="addChange('bugs')"
  65. />
  66. <a class="button is-info" href="#" v-on:click="addChange('bugs')">Add</a>
  67. </p>
  68. <span class="tag is-info" v-for="(bug, index) in creating.bugs" :key="index">
  69. {{ bug }}
  70. <button class="delete is-info" v-on:click="removeChange('bugs', index)"></button>
  71. </span>
  72. </div>
  73. <div class="column">
  74. <label class="label">Features</label>
  75. <p class="control has-addons">
  76. <input
  77. class="input"
  78. id="new-features"
  79. type="text"
  80. placeholder="Feature"
  81. v-on:keyup.enter="addChange('features')"
  82. />
  83. <a class="button is-info" href="#" v-on:click="addChange('features')">Add</a>
  84. </p>
  85. <span
  86. class="tag is-info"
  87. v-for="(feature, index) in creating.features"
  88. :key="index"
  89. >
  90. {{ feature }}
  91. <button
  92. class="delete is-info"
  93. v-on:click="removeChange('features', index)"
  94. ></button>
  95. </span>
  96. </div>
  97. </div>
  98. <div class="columns">
  99. <div class="column">
  100. <label class="label">Improvements</label>
  101. <p class="control has-addons">
  102. <input
  103. class="input"
  104. id="new-improvements"
  105. type="text"
  106. placeholder="Improvement"
  107. v-on:keyup.enter="addChange('improvements')"
  108. />
  109. <a class="button is-info" href="#" v-on:click="addChange('improvements')">Add</a>
  110. </p>
  111. <span
  112. class="tag is-info"
  113. v-for="(improvement, index) in creating.improvements"
  114. :key="index"
  115. >
  116. {{ improvement }}
  117. <button
  118. class="delete is-info"
  119. v-on:click="removeChange('improvements', index)"
  120. ></button>
  121. </span>
  122. </div>
  123. <div class="column">
  124. <label class="label">Upcoming</label>
  125. <p class="control has-addons">
  126. <input
  127. class="input"
  128. id="new-upcoming"
  129. type="text"
  130. placeholder="Upcoming"
  131. v-on:keyup.enter="addChange('upcoming')"
  132. />
  133. <a class="button is-info" href="#" v-on:click="addChange('upcoming')">Add</a>
  134. </p>
  135. <span
  136. class="tag is-info"
  137. v-for="(upcoming, index) in creating.upcoming"
  138. :key="index"
  139. >
  140. {{ upcoming }}
  141. <button
  142. class="delete is-info"
  143. v-on:click="removeChange('upcoming', index)"
  144. ></button>
  145. </span>
  146. </div>
  147. </div>
  148. </div>
  149. </div>
  150. <footer class="card-footer">
  151. <a class="card-footer-item" v-on:click="createNews()" href="#">Create</a>
  152. </footer>
  153. </div>
  154. </div>
  155. <edit-news v-if="modals.editNews"></edit-news>
  156. </div>
  157. </template>
  158. <script>
  159. import { Toast } from "vue-roaster";
  160. import io from "../../io";
  161. import EditNews from "../Modals/EditNews.vue";
  162. export default {
  163. components: { EditNews },
  164. data() {
  165. return {
  166. modals: { editNews: false },
  167. news: [],
  168. creating: {
  169. title: "",
  170. description: "",
  171. bugs: [],
  172. features: [],
  173. improvements: [],
  174. upcoming: []
  175. },
  176. editing: {}
  177. };
  178. },
  179. methods: {
  180. toggleModal: function() {
  181. this.modals.editNews = !this.modals.editNews;
  182. },
  183. createNews: function() {
  184. let _this = this;
  185. let {
  186. creating: { bugs, features, improvements, upcoming }
  187. } = this;
  188. if (this.creating.title === "")
  189. return Toast.methods.addToast("Field (Title) cannot be empty", 3000);
  190. if (this.creating.description === "")
  191. return Toast.methods.addToast(
  192. "Field (Description) cannot be empty",
  193. 3000
  194. );
  195. if (
  196. bugs.length <= 0 &&
  197. features.length <= 0 &&
  198. improvements.length <= 0 &&
  199. upcoming.length <= 0
  200. )
  201. return Toast.methods.addToast(
  202. "You must have at least one News Item",
  203. 3000
  204. );
  205. _this.socket.emit("news.create", _this.creating, result => {
  206. Toast.methods.addToast(result.message, 4000);
  207. if (result.status == "success")
  208. _this.creating = {
  209. title: "",
  210. description: "",
  211. bugs: [],
  212. features: [],
  213. improvements: [],
  214. upcoming: []
  215. };
  216. });
  217. },
  218. removeNews: function(news) {
  219. this.socket.emit("news.remove", news, res => {
  220. Toast.methods.addToast(res.message, 8000);
  221. });
  222. },
  223. editNews: function(news) {
  224. this.editing = news;
  225. this.toggleModal();
  226. },
  227. updateNews: function(close) {
  228. let _this = this;
  229. this.socket.emit("news.update", _this.editing._id, _this.editing, res => {
  230. Toast.methods.addToast(res.message, 4000);
  231. if (res.status === "success") {
  232. if (close) _this.toggleModal();
  233. }
  234. });
  235. },
  236. addChange: function(type) {
  237. let change = $(`#new-${type}`)
  238. .val()
  239. .trim();
  240. if (this.creating[type].indexOf(change) !== -1)
  241. return Toast.methods.addToast(`Tag already exists`, 3000);
  242. if (change) {
  243. $(`#new-${type}`).val("");
  244. this.creating[type].push(change);
  245. } else Toast.methods.addToast(`${type} cannot be empty`, 3000);
  246. },
  247. removeChange: function(type, index) {
  248. this.creating[type].splice(index, 1);
  249. },
  250. init: function() {
  251. this.socket.emit("apis.joinAdminRoom", "news", data => {});
  252. }
  253. },
  254. mounted: function() {
  255. let _this = this;
  256. io.getSocket(socket => {
  257. _this.socket = socket;
  258. _this.socket.emit("news.index", result => {
  259. _this.news = result.data;
  260. });
  261. _this.socket.on("event:admin.news.created", news => {
  262. _this.news.unshift(news);
  263. });
  264. _this.socket.on("event:admin.news.removed", news => {
  265. _this.news = _this.news.filter(item => item._id !== news._id);
  266. });
  267. if (_this.socket.connected) _this.init();
  268. io.onConnect(() => {
  269. _this.init();
  270. });
  271. });
  272. }
  273. };
  274. </script>
  275. <style lang='scss' scoped>
  276. .tag:not(:last-child) {
  277. margin-right: 5px;
  278. }
  279. td {
  280. vertical-align: middle;
  281. }
  282. .is-info:focus {
  283. background-color: #0398db;
  284. }
  285. .card-footer-item {
  286. color: #03a9f4;
  287. }
  288. </style>