DashboardNextSchedules.vue 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. <template>
  2. <div class="q-mx-md q-mb-md">
  3. <div
  4. class="dashboard-section-title gradient-diarista q-mb-sm font16 fontbold"
  5. >
  6. {{ $t("dashboard_client.next_schedules.title") }}
  7. </div>
  8. <div class="scroll-wrapper">
  9. <div
  10. ref="trackRef"
  11. class="scroll-track"
  12. @scroll="onTrackScroll"
  13. >
  14. <q-card
  15. v-for="item in data"
  16. :key="item.id"
  17. class="schedule-card card-border shadow-card bg-surface"
  18. :flat="false"
  19. >
  20. <q-card-section
  21. class="q-pa-md row col-12 no-wrap schedule-card-section"
  22. >
  23. <div class="column text-center schedule-avatar col-4">
  24. <div class="col-7 column">
  25. <q-avatar
  26. class="q-mx-auto"
  27. :style="avatarColors[item.id % avatarColors.length]"
  28. >
  29. <img
  30. v-if="item.provider_photo"
  31. style="object-fit: cover; border-radius: 50%"
  32. :src="item.provider_photo"
  33. />
  34. <span v-else>
  35. {{
  36. item.provider_name?.slice(0, 1).toUpperCase() ?? "—"
  37. }}
  38. </span>
  39. </q-avatar>
  40. </div>
  41. <div class="col-5 column justify-end font10 fontbold">
  42. <span class="text-pill text-primary customColor">
  43. {{
  44. item.schedule_type === "custom"
  45. ? $t("dashboard_client.next_schedules.tag_custom")
  46. : $t("dashboard_client.next_schedules.tag_default")
  47. }}
  48. </span>
  49. </div>
  50. </div>
  51. <div class="col-4 column text-text q-px-lg schedule-main">
  52. <div class="column schedule-info">
  53. <div>
  54. <span
  55. class="text-provider-name provider-name-ellipsis font12 fontbold"
  56. >
  57. {{
  58. getFirstName(item.provider_name) ||
  59. $t("dashboard_client.next_schedules.no_provider")
  60. }}
  61. </span>
  62. </div>
  63. <div class="column schedule-date-time font9">
  64. <div class="schedule-line">
  65. <span class="text-schedule-date-bold fontbold">
  66. {{ formatWeekday(item.date) }}
  67. </span>
  68. <span class="text-schedule-date-regular">
  69. {{ ", " + formatDayMonth(item.date) }}
  70. </span>
  71. </div>
  72. <div class="schedule-line q-pt-xs">
  73. <span class="text-schedule-date-regular">
  74. {{ $t("dashboard_client.next_schedules.from") }}&nbsp;
  75. </span>
  76. <span class="text-schedule-date-bold fontbold">
  77. {{ item.start_time?.slice(0, 5) }}
  78. {{ $t("dashboard_client.next_schedules.to") }}
  79. {{ item.end_time?.slice(0, 5) }}
  80. </span>
  81. </div>
  82. </div>
  83. </div>
  84. <div class="schedule-place">
  85. <div class="column justify-end">
  86. <div
  87. class="row items-center no-wrap text-pill-place schedule-place-content"
  88. >
  89. <span class="font10 fontbold q-ml-sm">
  90. <q-icon
  91. color="primary"
  92. size="12px"
  93. :name="addressIcon(item.address?.address_type ?? item.custom_address_type)"
  94. />
  95. {{
  96. addressLabel(
  97. item.address?.address_type ??
  98. item.custom_address_type,
  99. )
  100. }}
  101. </span>
  102. </div>
  103. </div>
  104. </div>
  105. </div>
  106. <div class="column text-text schedule-price">
  107. <div class="column schedule-price-info">
  108. <span class="text-price-main font16 fontbold">
  109. {{
  110. formatScheduleTotal(
  111. item,
  112. platformFees,
  113. t("dashboard_client.next_schedules.to_combine"),
  114. )
  115. }}
  116. </span>
  117. <span
  118. class="text-price-label schedule-price-label font12 fontmedium"
  119. >
  120. {{ formatLabelByPeriodType(item.period_type) }}
  121. </span>
  122. </div>
  123. <div class="column">
  124. <q-btn
  125. class="full-width"
  126. color="primary"
  127. no-caps
  128. padding="1px 5px"
  129. rounded
  130. size="sm"
  131. unelevated
  132. :label="$t('dashboard_client.next_schedules.details')"
  133. @click="emit('view-details', item)"
  134. />
  135. </div>
  136. </div>
  137. </q-card-section>
  138. </q-card>
  139. </div>
  140. </div>
  141. <DefaultCarouselIndicator
  142. :active="activeIndex"
  143. :count="pageCount"
  144. />
  145. </div>
  146. </template>
  147. <script setup>
  148. import { avatarColors } from "src/helpers/avatarColors";
  149. import { formatDayMonth, formatWeekday } from "src/helpers/scheduleDate";
  150. import { formatLabelByPeriodType, getFirstName } from "src/helpers/utils";
  151. import {
  152. formatScheduleTotal,
  153. } from "src/helpers/paymentPlatformFees";
  154. import { onMounted } from "vue";
  155. import { useCarouselIndicator } from "src/composables/useCarouselIndicator";
  156. import { useI18n } from "vue-i18n";
  157. import { usePaymentPlatformFees } from "src/composables/usePaymentPlatformFees";
  158. import DefaultCarouselIndicator from "src/components/defaults/DefaultCarouselIndicator.vue";
  159. const props = defineProps({ data: { type: Array, default: () => [] } });
  160. const emit = defineEmits(["view-details"]);
  161. const { activeIndex, onTrackScroll, pageCount, trackRef } =
  162. useCarouselIndicator(() => props.data);
  163. const { platformFees, loadPlatformFees } = usePaymentPlatformFees();
  164. const { t } = useI18n();
  165. const addressIcon = (type) =>
  166. type === "home" ? "mdi-home-outline" : "mdi-office-building-outline";
  167. const addressLabel = (type) => {
  168. if (type === "home") return t("address.types.commercial.home");
  169. if (type === "apartment")
  170. return t("dashboard_client.next_schedules.place_apartment");
  171. if (type === "commercial") return t("address.types.commercial.commercial");
  172. return t("dashboard_client.next_schedules.place_unknown");
  173. };
  174. onMounted(() => {
  175. loadPlatformFees().catch(() => {});
  176. });
  177. </script>
  178. <style scoped lang="scss">
  179. .scroll-wrapper {
  180. overflow: hidden;
  181. }
  182. .scroll-track {
  183. display: flex;
  184. flex-direction: row;
  185. gap: 12px;
  186. overflow-x: auto;
  187. overscroll-behavior-x: contain;
  188. scroll-snap-type: x proximity;
  189. padding-bottom: 8px;
  190. &::-webkit-scrollbar {
  191. display: none;
  192. }
  193. &::after {
  194. content: "";
  195. flex: 0 0 1px;
  196. }
  197. }
  198. .schedule-card {
  199. scroll-snap-align: start;
  200. min-width: 90%;
  201. }
  202. .schedule-card-section {
  203. align-items: stretch;
  204. gap: 8px;
  205. }
  206. .schedule-avatar {
  207. flex: 0 0 52px;
  208. max-width: 52px;
  209. min-width: 52px;
  210. }
  211. .schedule-main {
  212. flex: 1 1 auto;
  213. min-width: 0;
  214. overflow: hidden;
  215. gap: 8px;
  216. }
  217. .schedule-info {
  218. flex: 0 1 auto;
  219. gap: 6px;
  220. min-height: 0;
  221. }
  222. .schedule-date-time {
  223. min-width: 0;
  224. }
  225. .schedule-line {
  226. display: block;
  227. max-width: 100%;
  228. line-height: 1.15;
  229. overflow: visible;
  230. white-space: normal;
  231. }
  232. .schedule-price {
  233. flex: 0 0 92px;
  234. max-width: 92px;
  235. min-width: 92px;
  236. gap: 8px;
  237. justify-content: space-between;
  238. }
  239. .schedule-price-info {
  240. gap: 2px;
  241. }
  242. .schedule-price-label {
  243. align-self: stretch;
  244. line-height: 1.15;
  245. white-space: normal;
  246. }
  247. .schedule-place {
  248. margin-top: auto;
  249. min-width: 0;
  250. }
  251. .schedule-place-content {
  252. max-width: 100%;
  253. min-width: 0;
  254. overflow: hidden;
  255. .q-icon {
  256. flex: 0 0 auto;
  257. }
  258. span {
  259. flex: 1 1 auto;
  260. min-width: 0;
  261. overflow: hidden;
  262. text-overflow: ellipsis;
  263. white-space: nowrap;
  264. }
  265. }
  266. .provider-name-ellipsis {
  267. display: block;
  268. max-width: 100%;
  269. overflow: hidden;
  270. text-overflow: ellipsis;
  271. white-space: nowrap;
  272. }
  273. .customColor {
  274. background: linear-gradient(180deg, #8b5cf6 50%, #ec4899 50%);
  275. background-clip: text;
  276. -webkit-background-clip: text;
  277. -webkit-text-fill-color: transparent;
  278. }
  279. </style>