index.vue 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. <template>
  2. <div>
  3. <v-container fluid class="inner-container">
  4. <v-row>
  5. <v-col cols="12" class="text-h6 text-uppercase font-weight-bold">{{$t('welcome_freemium_title')}}</v-col>
  6. <!-- Bloc événements -->
  7. <v-col cols="12" md="7">
  8. <v-card>
  9. <v-tabs v-model="tab" class="tabs-title">
  10. <v-tab value="future" class="text-none">{{ $t('futur_event') }}</v-tab>
  11. <v-tab value="past" class="text-none">{{ $t('past_event') }}</v-tab>
  12. </v-tabs>
  13. <v-tabs-window v-model="tab" >
  14. <v-tabs-window-item value="future">
  15. <v-btn color="primary" to="freemium/events/new" class="ml-5 mt-5">{{
  16. $t('add_event')
  17. }}</v-btn>
  18. <div v-if="statusUpcomingEvents == FETCHING_STATUS.PENDING">
  19. <v-col cols="12" class="loader">
  20. <v-skeleton-loader
  21. v-for="i in 5"
  22. class="mx-auto"
  23. type="avatar, subtitle"
  24. ></v-skeleton-loader>
  25. </v-col>
  26. </div>
  27. <span v-if="upcomingEvents?.items.length == 0" class="no_event">
  28. {{ $t('no_future_event') }}
  29. </span>
  30. <UiEventList
  31. v-if="
  32. statusUpcomingEvents == FETCHING_STATUS.SUCCESS &&
  33. upcomingEvents?.items
  34. "
  35. :events="upcomingEvents.items"
  36. :pagination="upcomingEvents.pagination"
  37. @load="loadUpcomingEvents"
  38. @edit="editEvent"
  39. />
  40. </v-tabs-window-item>
  41. <v-tabs-window-item value="past">
  42. <div v-if="statusPastEvents == FETCHING_STATUS.PENDING">
  43. <v-col cols="12" class="loader">
  44. <v-skeleton-loader
  45. v-for="i in 5"
  46. class="mx-auto"
  47. type="avatar, subtitle"
  48. ></v-skeleton-loader>
  49. </v-col>
  50. </div>
  51. <span v-if="pastEvents?.items.length == 0" class="no_event">
  52. {{ $t('no_past_event') }}
  53. </span>
  54. <UiEventList
  55. v-if="
  56. statusPastEvents == FETCHING_STATUS.SUCCESS &&
  57. pastEvents?.items
  58. "
  59. :events="pastEvents.items"
  60. :pagination="pastEvents.pagination"
  61. @load="loadPastEvents"
  62. @edit="editEvent"
  63. />
  64. </v-tabs-window-item>
  65. </v-tabs-window>
  66. </v-card>
  67. </v-col>
  68. <!-- Bloc structure -->
  69. <v-col cols="12" md="5">
  70. <v-card
  71. v-if="statusOrganization == FETCHING_STATUS.SUCCESS"
  72. class="pa-5"
  73. >
  74. <v-card-title class="text-h6">
  75. <v-icon icon="fa fa-hotel" class="text-button icon-hotel" />
  76. <span class="organization_title">{{
  77. $t('my_organization')
  78. }}</span>
  79. </v-card-title>
  80. <v-card-text>
  81. <div>
  82. <strong>{{ $t('name') }} :</strong> {{ organization?.name }}
  83. </div>
  84. <div>
  85. <strong>{{ $t('email') }} :</strong> {{ organization?.email }}
  86. </div>
  87. </v-card-text>
  88. </v-card>
  89. <v-btn
  90. block
  91. prepend-icon="fa-solid fa-pen"
  92. class="my-5 text-black"
  93. to="freemium/organization"
  94. >
  95. {{ $t('edit_organization') }}
  96. </v-btn>
  97. <v-btn block class="text-black btn btn_trial" @click="startTrial">
  98. <span
  99. ><v-icon icon="fa fa-ticket" /> {{ $t('try_premium_light')
  100. }}<br />
  101. {{ $t('30_days_free') }}</span
  102. >
  103. </v-btn>
  104. </v-col>
  105. </v-row>
  106. </v-container>
  107. <LayoutDialogTrialAlreadyDid
  108. :show="showDialogTrialAlReadyDid"
  109. @close-dialog="showDialogTrialAlReadyDid = false"
  110. />
  111. </div>
  112. </template>
  113. <script setup lang="ts">
  114. import Query from '~/services/data/Query'
  115. import { type Ref, ref } from 'vue'
  116. import { useEntityFetch } from '~/composables/data/useEntityFetch'
  117. import Organization from '~/models/Freemium/Organization'
  118. import Event from '~/models/Freemium/Event'
  119. import OrderBy from '~/services/data/Filters/OrderBy'
  120. import {
  121. FETCHING_STATUS,
  122. ORDER_BY_DIRECTION,
  123. TIME_STRATEGY,
  124. } from '~/types/enum/data'
  125. import PageFilter from '~/services/data/Filters/PageFilter'
  126. import TimeFilter from '~/services/data/Filters/TimeFilter'
  127. import Country from '~/models/Core/Country'
  128. import DateUtils from '~/services/utils/dateUtils'
  129. import UrlUtils from '~/services/utils/urlUtils'
  130. import { useApiLegacyRequestService } from '~/composables/data/useApiLegacyRequestService'
  131. import { useAdminUrl } from '~/composables/utils/useAdminUrl'
  132. definePageMeta({
  133. name: 'freemium_dashboard_page',
  134. })
  135. //Ref Définition
  136. const { fetch, fetchCollection } = useEntityFetch()
  137. const { apiRequestService } = useApiLegacyRequestService()
  138. const { makeAdminUrl } = useAdminUrl()
  139. const tab = ref(null)
  140. const upcomingPage = ref(1)
  141. const pastPage = ref(1)
  142. const showDialogTrialAlReadyDid: Ref<boolean> = ref(false)
  143. //Fetch
  144. const { data: organization, status: statusOrganization } = fetch(Organization)
  145. const {
  146. data: upcomingEvents,
  147. status: statusUpcomingEvents,
  148. refresh: refreshUpcomingEvents,
  149. } = fetchEvents()
  150. const {
  151. data: pastEvents,
  152. status: statusPastEvents,
  153. refresh: refreshPastEvents,
  154. } = fetchEvents(true)
  155. /**
  156. * Charge une page des événements à venir
  157. * @param pageNumber
  158. */
  159. function loadUpcomingEvents(pageNumber: number) {
  160. upcomingPage.value = pageNumber
  161. refreshUpcomingEvents()
  162. }
  163. /**
  164. * Cahrge une page des événements passées
  165. * @param pageNumber
  166. */
  167. function loadPastEvents(pageNumber: number) {
  168. pastPage.value = pageNumber
  169. refreshPastEvents()
  170. }
  171. /**
  172. * Redirige vers la page d'édition d'un événement
  173. * @param eventId
  174. */
  175. function editEvent(eventId: number) {
  176. navigateTo(UrlUtils.join('freemium/events', eventId))
  177. }
  178. /**
  179. * Récupère la liste des événements
  180. * @param past
  181. */
  182. function fetchEvents(past: boolean = false) {
  183. const today = computed(() => DateUtils.formatIsoShortDate(new Date()))
  184. const query = new Query(
  185. new OrderBy(
  186. 'datetimeStart',
  187. past ? ORDER_BY_DIRECTION.DESC : ORDER_BY_DIRECTION.ASC,
  188. ),
  189. new PageFilter(past ? pastPage : upcomingPage, ref(5)),
  190. new TimeFilter(
  191. 'datetimeStart',
  192. today,
  193. past ? TIME_STRATEGY.BEFORE : TIME_STRATEGY.AFTER,
  194. ),
  195. )
  196. return fetchCollection(Event, null, query)
  197. }
  198. /**
  199. * Action lorsque l'on souhaite démarrer l'essai
  200. */
  201. async function startTrial() {
  202. try {
  203. await apiRequestService.get('/trial/is_available')
  204. await navigateTo(makeAdminUrl('trial'), {
  205. external: true,
  206. })
  207. } catch (error) {
  208. showDialogTrialAlReadyDid.value = true
  209. }
  210. }
  211. /**
  212. * Nettoyage du store
  213. */
  214. onUnmounted(() => {
  215. useRepo(Organization).flush()
  216. useRepo(Event).flush()
  217. useRepo(Country).flush()
  218. })
  219. </script>
  220. <style scoped lang="scss">
  221. .tabs-title {
  222. padding-left: 20px;
  223. background-color: rgb(var(--v-theme-neutral-soft));
  224. .v-tab--selected {
  225. color: rgb(var(--v-theme-neutral-soft--sub));
  226. }
  227. }
  228. .v-card {
  229. margin-bottom: 16px;
  230. color: rgb(var(--v-theme-on-primary-alt));
  231. }
  232. .v-card-text {
  233. div {
  234. line-height: 2;
  235. }
  236. }
  237. .organization_title {
  238. font-weight: 500;
  239. }
  240. .icon-hotel {
  241. margin: 0 5px 4px 0;
  242. }
  243. .btn {
  244. border: 1px solid;
  245. cursor: pointer;
  246. }
  247. .inner-container {
  248. margin: 0 auto;
  249. padding: 30px;
  250. }
  251. .btn_trial {
  252. height: 55px;
  253. background-color: rgb(var(--v-theme-x-create-btn));
  254. color: #000;
  255. span {
  256. text-align: center;
  257. line-height: 1.2; /* optionnel : pour resserrer ou espacer */
  258. }
  259. .v-icon {
  260. transform: rotate(135deg); /* angle en degrés */
  261. font-size: 16px;
  262. padding-right: 5px;
  263. margin: 0 5px 4px 0;
  264. }
  265. }
  266. .no_event {
  267. padding: 25px;
  268. font-size: 16px;
  269. display: block;
  270. }
  271. .loader{
  272. height: 500px;
  273. .v-skeleton-loader{
  274. margin-bottom: 20px;
  275. }
  276. }
  277. </style>