Stations.vue 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. <template>
  2. <div>
  3. <div class="container">
  4. <table class="table is-striped">
  5. <thead>
  6. <tr>
  7. <td>ID</td>
  8. <td>Name</td>
  9. <td>Type</td>
  10. <td>Display Name</td>
  11. <td>Description</td>
  12. <td>Options</td>
  13. </tr>
  14. </thead>
  15. <tbody>
  16. <tr v-for="(station, index) in stations" :key="index">
  17. <td>
  18. <span>{{station._id}}</span>
  19. </td>
  20. <td>
  21. <span>{{station.name}}</span>
  22. </td>
  23. <td>
  24. <span>{{station.type}}</span>
  25. </td>
  26. <td>
  27. <span>{{station.displayName}}</span>
  28. </td>
  29. <td>
  30. <span>{{station.description}}</span>
  31. </td>
  32. <td>
  33. <a class="button is-info" v-on:click="edit(station)">Edit</a>
  34. <a class="button is-danger" v-on:click="removeStation(index)" href="#">Remove</a>
  35. </td>
  36. </tr>
  37. </tbody>
  38. </table>
  39. </div>
  40. <div class="container">
  41. <div class="card is-fullwidth">
  42. <header class="card-header">
  43. <p class="card-header-title">Create official station</p>
  44. </header>
  45. <div class="card-content">
  46. <div class="content">
  47. <div class="control is-horizontal">
  48. <div class="control is-grouped">
  49. <p class="control is-expanded">
  50. <input class="input" type="text" placeholder="Name" v-model="newStation.name" />
  51. </p>
  52. <p class="control is-expanded">
  53. <input
  54. class="input"
  55. type="text"
  56. placeholder="Display Name"
  57. v-model="newStation.displayName"
  58. />
  59. </p>
  60. </div>
  61. </div>
  62. <label class="label">Description</label>
  63. <p class="control is-expanded">
  64. <input
  65. class="input"
  66. type="text"
  67. placeholder="Short description"
  68. v-model="newStation.description"
  69. />
  70. </p>
  71. <div class="control is-grouped genre-wrapper">
  72. <div class="sector">
  73. <p class="control has-addons">
  74. <input
  75. class="input"
  76. id="new-genre"
  77. type="text"
  78. placeholder="Genre"
  79. v-on:keyup.enter="addGenre()"
  80. />
  81. <a class="button is-info" href="#" v-on:click="addGenre()">Add genre</a>
  82. </p>
  83. <span class="tag is-info" v-for="(genre, index) in newStation.genres" :key="index">
  84. {{ genre }}
  85. <button class="delete is-info" v-on:click="removeGenre(index)"></button>
  86. </span>
  87. </div>
  88. <div class="sector">
  89. <p class="control has-addons">
  90. <input
  91. class="input"
  92. id="new-blacklisted-genre"
  93. type="text"
  94. placeholder="Blacklisted Genre"
  95. v-on:keyup.enter="addBlacklistedGenre()"
  96. />
  97. <a
  98. class="button is-info"
  99. href="#"
  100. v-on:click="addBlacklistedGenre()"
  101. >Add blacklisted genre</a>
  102. </p>
  103. <span
  104. class="tag is-info"
  105. v-for="(genre, index) in newStation.blacklistedGenres"
  106. :key="index"
  107. >
  108. {{ genre }}
  109. <button
  110. class="delete is-info"
  111. v-on:click="removeBlacklistedGenre(index)"
  112. ></button>
  113. </span>
  114. </div>
  115. </div>
  116. </div>
  117. </div>
  118. <footer class="card-footer">
  119. <a class="card-footer-item" v-on:click="createStation()" href="#">Create</a>
  120. </footer>
  121. </div>
  122. </div>
  123. <edit-station v-if="modals.editStation"></edit-station>
  124. </div>
  125. </template>
  126. <script>
  127. import { mapState, mapActions } from "vuex";
  128. import { Toast } from "vue-roaster";
  129. import io from "../../io";
  130. import EditStation from "./EditStation.vue";
  131. export default {
  132. components: { EditStation },
  133. data() {
  134. return {
  135. stations: [],
  136. newStation: {
  137. genres: [],
  138. blacklistedGenres: []
  139. }
  140. };
  141. },
  142. computed: {
  143. ...mapState("modals", {
  144. modals: state => state.modals.station
  145. })
  146. },
  147. methods: {
  148. createStation: function() {
  149. let _this = this;
  150. let {
  151. newStation: {
  152. name,
  153. displayName,
  154. description,
  155. genres,
  156. blacklistedGenres
  157. }
  158. } = this;
  159. if (name == undefined)
  160. return Toast.methods.addToast("Field (Name) cannot be empty", 3000);
  161. if (displayName == undefined)
  162. return Toast.methods.addToast(
  163. "Field (Display Name) cannot be empty",
  164. 3000
  165. );
  166. if (description == undefined)
  167. return Toast.methods.addToast(
  168. "Field (Description) cannot be empty",
  169. 3000
  170. );
  171. _this.socket.emit(
  172. "stations.create",
  173. {
  174. name,
  175. type: "official",
  176. displayName,
  177. description,
  178. genres,
  179. blacklistedGenres
  180. },
  181. result => {
  182. Toast.methods.addToast(result.message, 3000);
  183. if (result.status == "success")
  184. this.newStation = {
  185. genres: [],
  186. blacklistedGenres: []
  187. };
  188. }
  189. );
  190. },
  191. removeStation: function(index) {
  192. this.socket.emit("stations.remove", this.stations[index]._id, res => {
  193. Toast.methods.addToast(res.message, 3000);
  194. });
  195. },
  196. edit: function(station) {
  197. this.editStation({
  198. _id: station._id,
  199. name: station.name,
  200. type: station.type,
  201. partyMode: station.partyMode,
  202. description: station.description,
  203. privacy: station.privacy,
  204. displayName: station.displayName
  205. });
  206. this.toggleModal({
  207. sector: "station",
  208. modal: "editStation"
  209. });
  210. },
  211. addGenre: function() {
  212. let genre = $("#new-genre")
  213. .val()
  214. .toLowerCase()
  215. .trim();
  216. if (this.newStation.genres.indexOf(genre) !== -1)
  217. return Toast.methods.addToast("Genre already exists", 3000);
  218. if (genre) {
  219. this.newStation.genres.push(genre);
  220. $("#new-genre").val("");
  221. } else Toast.methods.addToast("Genre cannot be empty", 3000);
  222. },
  223. removeGenre: function(index) {
  224. this.newStation.genres.splice(index, 1);
  225. },
  226. addBlacklistedGenre: function() {
  227. let genre = $("#new-blacklisted-genre")
  228. .val()
  229. .toLowerCase()
  230. .trim();
  231. if (this.newStation.blacklistedGenres.indexOf(genre) !== -1)
  232. return Toast.methods.addToast("Genre already exists", 3000);
  233. if (genre) {
  234. this.newStation.blacklistedGenres.push(genre);
  235. $("#new-blacklisted-genre").val("");
  236. } else Toast.methods.addToast("Genre cannot be empty", 3000);
  237. },
  238. removeBlacklistedGenre: function(index) {
  239. this.newStation.blacklistedGenres.splice(index, 1);
  240. },
  241. init: function() {
  242. let _this = this;
  243. _this.socket.emit("stations.index", data => {
  244. _this.stations = data.stations;
  245. });
  246. _this.socket.emit("apis.joinAdminRoom", "stations", data => {});
  247. },
  248. ...mapActions("modals", ["toggleModal"]),
  249. ...mapActions("admin/stations", ["editStation"])
  250. },
  251. mounted: function() {
  252. let _this = this;
  253. io.getSocket(socket => {
  254. _this.socket = socket;
  255. if (_this.socket.connected) _this.init();
  256. _this.socket.on("event:admin.station.added", station => {
  257. _this.stations.push(station);
  258. });
  259. _this.socket.on("event:admin.station.removed", stationId => {
  260. _this.stations = _this.stations.filter(station => {
  261. return station._id !== stationId;
  262. });
  263. });
  264. io.onConnect(() => {
  265. _this.init();
  266. });
  267. });
  268. }
  269. };
  270. </script>
  271. <style lang='scss' scoped>
  272. .tag {
  273. margin-top: 5px;
  274. &:not(:last-child) {
  275. margin-right: 5px;
  276. }
  277. }
  278. td {
  279. word-wrap: break-word;
  280. max-width: 10vw;
  281. vertical-align: middle;
  282. }
  283. .is-info:focus {
  284. background-color: #0398db;
  285. }
  286. .genre-wrapper {
  287. display: flex;
  288. justify-content: space-around;
  289. }
  290. .card-footer-item {
  291. color: #029ce3;
  292. }
  293. </style>