News.vue 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  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 { mapActions, mapState } from "vuex";
  211. import { Toast } from "vue-roaster";
  212. import io from "../../io";
  213. import EditNews from "../Modals/EditNews.vue";
  214. export default {
  215. components: { EditNews },
  216. data() {
  217. return {
  218. news: [],
  219. creating: {
  220. title: "",
  221. description: "",
  222. bugs: [],
  223. features: [],
  224. improvements: [],
  225. upcoming: []
  226. }
  227. };
  228. },
  229. mounted() {
  230. io.getSocket(socket => {
  231. this.socket = socket;
  232. this.socket.emit("news.index", res => {
  233. this.news = res.data;
  234. return res.data;
  235. });
  236. this.socket.on("event:admin.news.created", news => {
  237. this.news.unshift(news);
  238. });
  239. this.socket.on("event:admin.news.removed", news => {
  240. this.news = this.news.filter(item => item._id !== news._id);
  241. });
  242. if (this.socket.connected) this.init();
  243. io.onConnect(() => this.init());
  244. });
  245. },
  246. computed: {
  247. ...mapState("modals", {
  248. modals: state => state.modals.admin
  249. }),
  250. ...mapState("admin/news", {
  251. editing: state => state.editing
  252. })
  253. },
  254. methods: {
  255. createNews() {
  256. const {
  257. creating: { bugs, features, improvements, upcoming }
  258. } = this;
  259. if (this.creating.title === "")
  260. return Toast.methods.addToast(
  261. "Field (Title) cannot be empty",
  262. 3000
  263. );
  264. if (this.creating.description === "")
  265. return Toast.methods.addToast(
  266. "Field (Description) cannot be empty",
  267. 3000
  268. );
  269. if (
  270. bugs.length <= 0 &&
  271. features.length <= 0 &&
  272. improvements.length <= 0 &&
  273. upcoming.length <= 0
  274. )
  275. return Toast.methods.addToast(
  276. "You must have at least one News Item",
  277. 3000
  278. );
  279. return this.socket.emit("news.create", this.creating, result => {
  280. Toast.methods.addToast(result.message, 4000);
  281. if (result.status === "success")
  282. this.creating = {
  283. title: "",
  284. description: "",
  285. bugs: [],
  286. features: [],
  287. improvements: [],
  288. upcoming: []
  289. };
  290. });
  291. },
  292. removeNews(news) {
  293. this.socket.emit("news.remove", news, res =>
  294. Toast.methods.addToast(res.message, 8000)
  295. );
  296. },
  297. editNews(news) {
  298. this.editNews(news);
  299. this.openModal({ sector: "admin", modal: "editNews" });
  300. },
  301. addChange(type) {
  302. const change = document.getElementById(`new-${type}`).value.trim();
  303. if (this.creating[type].indexOf(change) !== -1)
  304. return Toast.methods.addToast(`Tag already exists`, 3000);
  305. if (change) {
  306. document.getElementById(`new-${type}`).value = "";
  307. this.creating[type].push(change);
  308. return true;
  309. }
  310. return Toast.methods.addToast(`${type} cannot be empty`, 3000);
  311. },
  312. removeChange(type, index) {
  313. this.creating[type].splice(index, 1);
  314. },
  315. init() {
  316. this.socket.emit("apis.joinAdminRoom", "news", () => {});
  317. },
  318. ...mapActions("modals", ["openModal", "closeModal"]),
  319. ...mapActions("admin/news", ["editNews"])
  320. }
  321. };
  322. </script>
  323. <style lang="scss" scoped>
  324. @import "styles/global.scss";
  325. .tag:not(:last-child) {
  326. margin-right: 5px;
  327. }
  328. td {
  329. vertical-align: middle;
  330. }
  331. .is-info:focus {
  332. background-color: $primary-color;
  333. }
  334. .card-footer-item {
  335. color: $primary-color;
  336. }
  337. </style>