News.vue 8.1 KB

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