Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 

287 рядки
7.5 KiB

  1. <template>
  2. <div class="dark">
  3. <div v-if="reservations" class="stats">
  4. <h1>
  5. {{reservations.length}} active reservations
  6. </h1>
  7. <h2>
  8. {{invalidatedCount}} invalidated<br>
  9. {{validCount}} to go
  10. </h2>
  11. </div>
  12. <div class="guest-table">
  13. <vuetable v-if="reservations"
  14. :fields="fields"
  15. :api-mode="false"
  16. :data="reservations"
  17. :sort-order="sortOrder"
  18. :show-sort-icons="true"
  19. >
  20. <template v-slot:timestamp="props">
  21. <template v-if="props.rowData.timestamp">
  22. {{new Date(props.rowData.timestamp).toLocaleString('de-DE', {day:'numeric',month:'numeric',hour:'numeric',minute:'numeric'})}}
  23. </template>
  24. </template>
  25. <template v-slot:invalidated="props">
  26. <template v-if="props.rowData.invalidated">
  27. {{new Date(props.rowData.invalidated).toLocaleString('de-DE', {day:'numeric',month:'numeric',hour:'numeric',minute:'numeric'})}}
  28. </template>
  29. <button v-else @click="invalidate(props.rowData.token)">invalidate</button>
  30. </template>
  31. <template v-slot:actions="props">
  32. <button @click="cancel(props.rowData.token)">cancel</button>
  33. </template>
  34. <template v-slot:headsup_sent="props">
  35. <template v-if="props.rowData.headsup_sent">
  36. {{new Date(props.rowData.headsup_sent).toLocaleString('de-DE', {day:'numeric',month:'numeric',hour:'numeric',minute:'numeric'})}}
  37. </template>
  38. <button @click="sendHeadsup(props.rowData.id)">X</button>
  39. </template>
  40. </vuetable>
  41. </div>
  42. <label for="showSensitiveData">
  43. <input type="checkbox" name="showSensitiveData" id="showSensitiveData" :value="showSensitiveData" @change="showSensitiveData = !showSensitiveData">
  44. show sensitive data
  45. </label>
  46. <div class="add-guest-form">
  47. <form class="input-form" @submit.prevent="onAddGuest">
  48. <div class="input-headline">add new reservation</div>
  49. <input class="input-text" v-model="anzeigename" placeholder="nickname (public)" />
  50. <input class="input-text" v-model="name" placeholder="real name" />
  51. <input class="input-text" v-model="email" placeholder="email" />
  52. <input class="input-btn" type="submit" :value="(addGuestSending ? 'sending ...' : 'reserve')" :disabled="(addGuestSending ? true : false)" />
  53. <div v-if="addGuestError">
  54. oh no something went wrong!<br>
  55. {{addGuestError.detail}}
  56. </div>
  57. </form>
  58. <button @click="sendHeadsups">SEND ALL EMAILS</button>
  59. </div>
  60. <!-- {{reservations}} -->
  61. </div>
  62. </template>
  63. <script>
  64. import Vuetable from 'vuetable-2'
  65. export default {
  66. name: 'Reservations',
  67. components: {
  68. Vuetable
  69. },
  70. data() {
  71. return {
  72. reservations: null,
  73. sortOrder: [
  74. {
  75. field: 'name',
  76. direction: 'asc',
  77. }
  78. ],
  79. showSensitiveData: false,
  80. addGuestSending: false,
  81. addGuestError: null,
  82. anzeigename: '',
  83. name: '',
  84. email: '',
  85. }
  86. },
  87. computed: {
  88. secret () {
  89. return this.$route.params.secret
  90. },
  91. invalidatedCount () {
  92. if (this.reservations && this.reservations.length) {
  93. const filtered = this.reservations.filter(r => !!r.invalidated)
  94. return filtered.length
  95. }
  96. return null
  97. },
  98. validCount () {
  99. if (this.reservations && this.reservations.length) {
  100. const filtered = this.reservations.filter(r => !r.invalidated)
  101. return filtered.length
  102. }
  103. return null
  104. },
  105. fields () {
  106. if (this.showSensitiveData) {
  107. return ['id','timestamp', 'anzeigename', 'name', 'email', 'token', 'invalidated','actions', 'headsup_sent']
  108. } else {
  109. return ['id','timestamp', 'anzeigename', 'invalidated','actions', 'headsup_sent']
  110. }
  111. }
  112. },
  113. methods: {
  114. async getReservations() {
  115. const response = await fetch(`${process.env.VUE_APP_API_URL}guest/management/${this.$route.params.secret}`)
  116. const data = await response.json()
  117. this.reservations = data
  118. },
  119. async cancel(token) {
  120. let data = { token }
  121. const response = await fetch(
  122. `${process.env.VUE_APP_API_URL}guest/cancel`, {
  123. method: 'POST',
  124. headers: { 'Content-Type': 'application/json' },
  125. body: JSON.stringify(data),
  126. }
  127. )
  128. if (response.status === 200) {
  129. this.getReservations()
  130. } else {
  131. alert(await response.json())
  132. }
  133. },
  134. async invalidate(token) {
  135. let data = { token }
  136. const response = await fetch(
  137. `${process.env.VUE_APP_API_URL}guest/invalidate`, {
  138. method: 'POST',
  139. headers: { 'Content-Type': 'application/json' },
  140. body: JSON.stringify(data),
  141. }
  142. )
  143. if (response.status === 200) {
  144. this.getReservations()
  145. } else {
  146. alert(await response.json())
  147. }
  148. },
  149. async sendHeadsup(id) {
  150. let data = {
  151. guest_id: id,
  152. secret: this.secret
  153. }
  154. const response = await fetch(
  155. `${process.env.VUE_APP_API_URL}guest/management/send_headsup`, {
  156. method: 'POST',
  157. headers: { 'Content-Type': 'application/json' },
  158. body: JSON.stringify(data),
  159. }
  160. )
  161. if (response.status === 200) {
  162. this.getReservations()
  163. } else {
  164. alert(await response.json())
  165. }
  166. },
  167. async sendHeadsups() {
  168. let data = {
  169. secret: this.secret
  170. }
  171. const response = await fetch(
  172. `${process.env.VUE_APP_API_URL}guest/management/send_headsups`, {
  173. method: 'POST',
  174. headers: { 'Content-Type': 'application/json' },
  175. body: JSON.stringify(data),
  176. }
  177. )
  178. if (response.status === 200) {
  179. this.getReservations()
  180. } else {
  181. alert(await response.json())
  182. }
  183. },
  184. async onAddGuest() {
  185. if (this.name.length == 0) {
  186. return false
  187. }
  188. this.addGuestSending = true
  189. let data = {
  190. anzeigename: this.anzeigename,
  191. name: this.name,
  192. email: this.email,
  193. newsletter: false,
  194. }
  195. const response = await fetch(
  196. `${process.env.VUE_APP_API_URL}guest`, {
  197. method: 'POST',
  198. headers: {
  199. 'Content-Type': 'application/json',
  200. },
  201. body: JSON.stringify(data),
  202. }
  203. )
  204. if (response.status === 200) {
  205. this.anzeigename = ''
  206. this.name = ''
  207. this.email = ''
  208. this.getReservations()
  209. } else {
  210. this.getReservations()
  211. this.addGuestError = true
  212. this.addGuestError = await response.json()
  213. }
  214. this.addGuestSending = false
  215. return false
  216. },
  217. },
  218. mounted () {
  219. this.getReservations()
  220. },
  221. }
  222. </script>
  223. <style>
  224. .stats {
  225. display: flex;
  226. flex-direction: column;
  227. align-items: center;
  228. }
  229. .vuetable thead {
  230. background-color: rgb(255, 0, 0);
  231. }
  232. .vuetable-body tr {
  233. border-bottom: 1px dashed rgb(255, 0, 0);
  234. }
  235. .vuetable-body tr:hover {
  236. background-color: rgba(255, 0, 0, 0.281)
  237. }
  238. .vuetable-body td {
  239. padding: .5em;
  240. margin: 0;
  241. }
  242. .guest-table {
  243. display: flex;
  244. flex-direction: column;
  245. align-items: center;
  246. }
  247. .add-guest-form {
  248. display: flex;
  249. flex-direction: column;
  250. align-items: center;
  251. margin-top: 120px
  252. }
  253. button {
  254. background: #fff;
  255. color: #000;
  256. border: none;
  257. text-transform: uppercase;
  258. cursor: pointer;
  259. font-size: 18px;
  260. font-family: inherit;
  261. }
  262. button:hover {
  263. background: #ff0;
  264. color: #000;
  265. }
  266. </style>