Stations.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. <script setup lang="ts">
  2. import { defineAsyncComponent, ref } from "vue";
  3. import Toast from "toasters";
  4. import { useWebsocketsStore } from "@/stores/websockets";
  5. import { useModalsStore } from "@/stores/modals";
  6. import { TableColumn, TableFilter, TableEvents } from "@/types/advancedTable";
  7. const AdvancedTable = defineAsyncComponent(
  8. () => import("@/components/AdvancedTable.vue")
  9. );
  10. const RunJobDropdown = defineAsyncComponent(
  11. () => import("@/components/RunJobDropdown.vue")
  12. );
  13. const QuickConfirm = defineAsyncComponent(
  14. () => import("@/components/QuickConfirm.vue")
  15. );
  16. const UserLink = defineAsyncComponent(
  17. () => import("@/components/UserLink.vue")
  18. );
  19. const { socket } = useWebsocketsStore();
  20. const columnDefault = ref(<TableColumn>{
  21. sortable: true,
  22. hidable: true,
  23. defaultVisibility: "shown",
  24. draggable: true,
  25. resizable: true,
  26. minWidth: 150,
  27. maxWidth: 600
  28. });
  29. const columns = ref(<TableColumn[]>[
  30. {
  31. name: "options",
  32. displayName: "Options",
  33. properties: ["_id", "name"],
  34. sortable: false,
  35. hidable: false,
  36. resizable: false,
  37. minWidth: 129,
  38. defaultWidth: 129
  39. },
  40. {
  41. name: "_id",
  42. displayName: "Station ID",
  43. properties: ["_id"],
  44. sortProperty: "_id",
  45. minWidth: 230,
  46. defaultWidth: 230
  47. },
  48. {
  49. name: "name",
  50. displayName: "Name",
  51. properties: ["name"],
  52. sortProperty: "name"
  53. },
  54. {
  55. name: "displayName",
  56. displayName: "Display Name",
  57. properties: ["displayName"],
  58. sortProperty: "displayName"
  59. },
  60. {
  61. name: "description",
  62. displayName: "Description",
  63. properties: ["description"],
  64. sortProperty: "description",
  65. defaultVisibility: "hidden"
  66. },
  67. {
  68. name: "type",
  69. displayName: "Type",
  70. properties: ["type"],
  71. sortProperty: "type"
  72. },
  73. {
  74. name: "privacy",
  75. displayName: "Privacy",
  76. properties: ["privacy"],
  77. sortProperty: "privacy"
  78. },
  79. {
  80. name: "owner",
  81. displayName: "Owner",
  82. properties: ["owner", "type"],
  83. sortProperty: "owner",
  84. defaultWidth: 150
  85. },
  86. {
  87. name: "theme",
  88. displayName: "Theme",
  89. properties: ["theme"],
  90. sortProperty: "theme",
  91. defaultVisibility: "hidden"
  92. },
  93. {
  94. name: "requestsEnabled",
  95. displayName: "Requests Enabled",
  96. properties: ["requests.enabled"],
  97. sortProperty: "requests.enabled",
  98. minWidth: 180,
  99. defaultWidth: 180,
  100. defaultVisibility: "hidden"
  101. },
  102. {
  103. name: "requestsAccess",
  104. displayName: "Requests Access",
  105. properties: ["requests.access"],
  106. sortProperty: "requests.access",
  107. minWidth: 180,
  108. defaultWidth: 180,
  109. defaultVisibility: "hidden"
  110. },
  111. {
  112. name: "requestsLimit",
  113. displayName: "Requests Limit",
  114. properties: ["requests.limit"],
  115. sortProperty: "requests.limit",
  116. minWidth: 180,
  117. defaultWidth: 180,
  118. defaultVisibility: "hidden"
  119. },
  120. {
  121. name: "autofillEnabled",
  122. displayName: "Autofill Enabled",
  123. properties: ["autofill.enabled"],
  124. sortProperty: "autofill.enabled",
  125. minWidth: 180,
  126. defaultWidth: 180,
  127. defaultVisibility: "hidden"
  128. },
  129. {
  130. name: "autofillLimit",
  131. displayName: "Autofill Limit",
  132. properties: ["autofill.limit"],
  133. sortProperty: "autofill.limit",
  134. minWidth: 180,
  135. defaultWidth: 180,
  136. defaultVisibility: "hidden"
  137. },
  138. {
  139. name: "autofillMode",
  140. displayName: "Autofill Mode",
  141. properties: ["autofill.mode"],
  142. sortProperty: "autofill.mode",
  143. minWidth: 180,
  144. defaultWidth: 180,
  145. defaultVisibility: "hidden"
  146. }
  147. ]);
  148. const filters = ref(<TableFilter[]>[
  149. {
  150. name: "_id",
  151. displayName: "Station ID",
  152. property: "_id",
  153. filterTypes: ["exact"],
  154. defaultFilterType: "exact"
  155. },
  156. {
  157. name: "name",
  158. displayName: "Name",
  159. property: "name",
  160. filterTypes: ["contains", "exact", "regex"],
  161. defaultFilterType: "contains"
  162. },
  163. {
  164. name: "displayName",
  165. displayName: "Display Name",
  166. property: "displayName",
  167. filterTypes: ["contains", "exact", "regex"],
  168. defaultFilterType: "contains"
  169. },
  170. {
  171. name: "description",
  172. displayName: "Description",
  173. property: "description",
  174. filterTypes: ["contains", "exact", "regex"],
  175. defaultFilterType: "contains"
  176. },
  177. {
  178. name: "type",
  179. displayName: "Type",
  180. property: "type",
  181. filterTypes: ["exact"],
  182. defaultFilterType: "exact",
  183. dropdown: [
  184. ["official", "Official"],
  185. ["community", "Community"]
  186. ]
  187. },
  188. {
  189. name: "privacy",
  190. displayName: "Privacy",
  191. property: "privacy",
  192. filterTypes: ["exact"],
  193. defaultFilterType: "exact",
  194. dropdown: [
  195. ["public", "Public"],
  196. ["unlisted", "Unlisted"],
  197. ["private", "Private"]
  198. ]
  199. },
  200. {
  201. name: "owner",
  202. displayName: "Owner",
  203. property: "owner",
  204. filterTypes: ["contains", "exact", "regex"],
  205. defaultFilterType: "contains"
  206. },
  207. {
  208. name: "theme",
  209. displayName: "Theme",
  210. property: "theme",
  211. filterTypes: ["exact"],
  212. defaultFilterType: "exact",
  213. dropdown: [
  214. ["blue", "Blue"],
  215. ["purple", "Purple"],
  216. ["teal", "Teal"],
  217. ["orange", "Orange"],
  218. ["red", "Red"]
  219. ]
  220. },
  221. {
  222. name: "requestsEnabled",
  223. displayName: "Requests Enabled",
  224. property: "requests.enabled",
  225. filterTypes: ["boolean"],
  226. defaultFilterType: "boolean"
  227. },
  228. {
  229. name: "requestsAccess",
  230. displayName: "Requests Access",
  231. property: "requests.access",
  232. filterTypes: ["exact"],
  233. defaultFilterType: "exact",
  234. dropdown: [
  235. ["owner", "Owner"],
  236. ["user", "User"]
  237. ]
  238. },
  239. {
  240. name: "requestsLimit",
  241. displayName: "Requests Limit",
  242. property: "requests.limit",
  243. filterTypes: [
  244. "numberLesserEqual",
  245. "numberLesser",
  246. "numberGreater",
  247. "numberGreaterEqual",
  248. "numberEquals"
  249. ],
  250. defaultFilterType: "numberLesser"
  251. },
  252. {
  253. name: "autofillEnabled",
  254. displayName: "Autofill Enabled",
  255. property: "autofill.enabled",
  256. filterTypes: ["boolean"],
  257. defaultFilterType: "boolean"
  258. },
  259. {
  260. name: "autofillLimit",
  261. displayName: "Autofill Limit",
  262. property: "autofill.limit",
  263. filterTypes: [
  264. "numberLesserEqual",
  265. "numberLesser",
  266. "numberGreater",
  267. "numberGreaterEqual",
  268. "numberEquals"
  269. ],
  270. defaultFilterType: "numberLesser"
  271. },
  272. {
  273. name: "autofillMode",
  274. displayName: "Autofill Mode",
  275. property: "autofill.mode",
  276. filterTypes: ["exact"],
  277. defaultFilterType: "exact",
  278. dropdown: [
  279. ["random", "Random"],
  280. ["sequential", "Sequential"]
  281. ]
  282. }
  283. ]);
  284. const events = ref(<TableEvents>{
  285. adminRoom: "stations",
  286. updated: {
  287. event: "station.updated",
  288. id: "station._id",
  289. item: "station"
  290. },
  291. removed: {
  292. event: "admin.station.deleted",
  293. id: "stationId"
  294. }
  295. });
  296. const jobs = ref([
  297. {
  298. name: "Clear every station queue",
  299. socket: "stations.clearEveryStationQueue"
  300. }
  301. ]);
  302. const { openModal } = useModalsStore();
  303. const remove = stationId => {
  304. socket.dispatch(
  305. "stations.remove",
  306. stationId,
  307. res => new Toast(res.message)
  308. );
  309. };
  310. </script>
  311. <template>
  312. <div class="admin-tab">
  313. <page-metadata title="Admin | Stations" />
  314. <div class="card tab-info">
  315. <div class="info-row">
  316. <h1>Stations</h1>
  317. <p>Manage stations or create an official station</p>
  318. </div>
  319. <div class="button-row">
  320. <button
  321. class="button is-primary"
  322. @click="
  323. openModal({
  324. modal: 'createStation',
  325. data: { official: true }
  326. })
  327. "
  328. >
  329. Create Station
  330. </button>
  331. <run-job-dropdown :jobs="jobs" />
  332. </div>
  333. </div>
  334. <advanced-table
  335. :column-default="columnDefault"
  336. :columns="columns"
  337. :filters="filters"
  338. data-action="stations.getData"
  339. name="admin-stations"
  340. :events="events"
  341. >
  342. <template #column-options="slotProps">
  343. <div class="row-options">
  344. <button
  345. class="button is-primary icon-with-button material-icons"
  346. @click="
  347. openModal({
  348. modal: 'manageStation',
  349. data: {
  350. stationId: slotProps.item._id,
  351. sector: 'admin'
  352. }
  353. })
  354. "
  355. :disabled="slotProps.item.removed"
  356. content="Manage Station"
  357. v-tippy
  358. >
  359. settings
  360. </button>
  361. <quick-confirm
  362. @confirm="remove(slotProps.item._id)"
  363. :disabled="slotProps.item.removed"
  364. >
  365. <button
  366. class="button is-danger icon-with-button material-icons"
  367. content="Remove Station"
  368. v-tippy
  369. >
  370. delete_forever
  371. </button>
  372. </quick-confirm>
  373. <router-link
  374. :to="{ path: `/${slotProps.item.name}` }"
  375. target="_blank"
  376. class="button is-primary icon-with-button material-icons"
  377. :disabled="slotProps.item.removed"
  378. content="View Station"
  379. v-tippy
  380. >
  381. radio
  382. </router-link>
  383. </div>
  384. </template>
  385. <template #column-_id="slotProps">
  386. <span :title="slotProps.item._id">{{
  387. slotProps.item._id
  388. }}</span>
  389. </template>
  390. <template #column-name="slotProps">
  391. <span :title="slotProps.item.name">{{
  392. slotProps.item.name
  393. }}</span>
  394. </template>
  395. <template #column-displayName="slotProps">
  396. <span :title="slotProps.item.displayName">{{
  397. slotProps.item.displayName
  398. }}</span>
  399. </template>
  400. <template #column-type="slotProps">
  401. <span :title="slotProps.item.type">{{
  402. slotProps.item.type
  403. }}</span>
  404. </template>
  405. <template #column-description="slotProps">
  406. <span :title="slotProps.item.description">{{
  407. slotProps.item.description
  408. }}</span>
  409. </template>
  410. <template #column-privacy="slotProps">
  411. <span :title="slotProps.item.privacy">{{
  412. slotProps.item.privacy
  413. }}</span>
  414. </template>
  415. <template #column-owner="slotProps">
  416. <span v-if="slotProps.item.type === 'official'">Musare</span>
  417. <user-link v-else :user-id="slotProps.item.owner" />
  418. </template>
  419. <template #column-theme="slotProps">
  420. <span :title="slotProps.item.theme">{{
  421. slotProps.item.theme
  422. }}</span>
  423. </template>
  424. <template #column-requestsEnabled="slotProps">
  425. <span :title="slotProps.item.requests.enabled">{{
  426. slotProps.item.requests.enabled
  427. }}</span>
  428. </template>
  429. <template #column-requestsAccess="slotProps">
  430. <span :title="slotProps.item.requests.access">{{
  431. slotProps.item.requests.access
  432. }}</span>
  433. </template>
  434. <template #column-requestsLimit="slotProps">
  435. <span :title="slotProps.item.requests.limit">{{
  436. slotProps.item.requests.limit
  437. }}</span>
  438. </template>
  439. <template #column-autofillEnabled="slotProps">
  440. <span :title="slotProps.item.autofill.enabled">{{
  441. slotProps.item.autofill.enabled
  442. }}</span>
  443. </template>
  444. <template #column-autofillLimit="slotProps">
  445. <span :title="slotProps.item.autofill.limit">{{
  446. slotProps.item.autofill.limit
  447. }}</span>
  448. </template>
  449. <template #column-autofillMode="slotProps">
  450. <span :title="slotProps.item.autofill.mode">{{
  451. slotProps.item.autofill.mode
  452. }}</span>
  453. </template>
  454. </advanced-table>
  455. </div>
  456. </template>