index.vue 8.3 KB

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