DashboardNextSchedules.vue 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. <template>
  2. <div class="q-mx-md q-mb-md">
  3. <div class="dashboard-section-title gradient-diarista q-mb-sm">{{ $t('dashboard_client.next_schedules.title') }}</div>
  4. <div class="scroll-wrapper">
  5. <div class="scroll-track">
  6. <q-card
  7. v-for="item in data"
  8. :key="item.id"
  9. class="schedule-card card-border shadow-card bg-surface"
  10. :flat="false"
  11. >
  12. <q-card-section class="q-pa-md row col-12 no-wrap">
  13. <div class="col-3 column text-center">
  14. <div class="col-7">
  15. <q-avatar :style="avatarColors[item.id % avatarColors.length]" class="text-weight-bold q-mx-auto">
  16. {{ item.provider_name?.slice(0,1) ?? '—' }}
  17. </q-avatar>
  18. </div>
  19. <div class="col-5 column justify-end">
  20. <span class="text-pill text-primary customColor">
  21. {{ item.schedule_type === 'custom' ? $t('dashboard_client.next_schedules.tag_custom') : $t('dashboard_client.next_schedules.tag_default') }}
  22. </span>
  23. </div>
  24. </div>
  25. <div class="col-5 column text-text q-px-sm">
  26. <div class="col-7 column">
  27. <div class="col-4">
  28. <span class="text-provider-name">{{ item.provider_name ?? $t('dashboard_client.next_schedules.no_provider') }}</span>
  29. </div>
  30. <div class="col-4 column">
  31. <div class="col-6">
  32. <span class="text-schedule-date-bold">{{ formatWeekday(item.date) }}</span><span class="text-schedule-date-regular">{{ ', ' + formatDayMonth(item.date) }}</span>
  33. </div>
  34. <div class="col-6 q-pt-sm">
  35. <span class="text-schedule-date-regular">{{ $t('dashboard_client.next_schedules.from') }} </span>
  36. <span class="text-schedule-date-bold">{{ item.start_time?.slice(0,5) }} {{ $t('dashboard_client.next_schedules.to') }} {{ item.end_time?.slice(0,5) }}</span>
  37. </div>
  38. </div>
  39. </div>
  40. <div class="col-5">
  41. <div class="full-height column justify-end">
  42. <div class="row text-pill-place">
  43. <q-icon :name="addressIcon(item.address?.address_type ?? item.custom_address_type)" size="15px" color="primary" />
  44. <span class="row items-end">{{ addressLabel(item.address?.address_type ?? item.custom_address_type) }}</span>
  45. </div>
  46. </div>
  47. </div>
  48. </div>
  49. <div class="col-4 column text-text">
  50. <div class="column col-5">
  51. <span class="text-price-main col-6">
  52. {{ item.total_amount && item.total_amount !== '0.00' ? formatCurrency(item.total_amount) : $t('dashboard_client.next_schedules.to_combine') }}
  53. </span>
  54. <span class="text-price-label col-6">
  55. {{ formatLabelByPeriodType(item.period_type) }}
  56. </span>
  57. </div>
  58. <div class="col-7 column justify-end items-end">
  59. <q-btn
  60. unelevated rounded no-caps
  61. color="primary"
  62. padding="1px 5px"
  63. size="sm"
  64. class="full-width"
  65. :label="$t('dashboard_client.next_schedules.details')"
  66. />
  67. </div>
  68. </div>
  69. </q-card-section>
  70. </q-card>
  71. </div>
  72. </div>
  73. </div>
  74. </template>
  75. <script setup>
  76. import { useI18n } from 'vue-i18n';
  77. import { formatCurrency } from 'src/helpers/utils';
  78. defineProps({ data: { type: Array, default: () => [] } });
  79. const { t } = useI18n();
  80. const avatarColors = [
  81. { background: '#ffd5df', color: '#932e57' },
  82. { background: '#d7e8ff', color: '#2158a8' },
  83. { background: '#dfd', color: '#2a7a3b' },
  84. { background: '#ffe5cc', color: '#8a4500' },
  85. ];
  86. const formatWeekday = (iso) => {
  87. if (!iso) return '';
  88. const d = new Date(iso);
  89. const w = d.toLocaleDateString('pt-BR', { weekday: 'long' });
  90. return w.charAt(0).toUpperCase() + w.slice(1);
  91. };
  92. const formatDayMonth = (iso) => {
  93. if (!iso) return '';
  94. return new Date(iso).toLocaleDateString('pt-BR', { day: '2-digit', month: '2-digit' });
  95. };
  96. const addressIcon = (type) => type === 'home' ? 'mdi-home-outline' : 'mdi-office-building-outline';
  97. const addressLabel = (type) => {
  98. if (type === 'home') return t('address.types.commercial.home');
  99. if (type === 'apartment') return t('dashboard_client.next_schedules.place_apartment');
  100. if (type === 'commercial') return t('address.types.commercial.commercial');
  101. return t('dashboard_client.next_schedules.place_unknown');
  102. };
  103. const formatLabelByPeriodType = (type) => {
  104. switch (type) {
  105. case '2': return t('period_types.2');
  106. case '4': return t('period_types.4');
  107. case '6': return t('period_types.6');
  108. case '8': return t('period_types.8');
  109. default: return t('period_types.unknown');
  110. }
  111. };
  112. </script>
  113. <style scoped lang="scss">
  114. .scroll-wrapper { overflow: hidden; }
  115. .scroll-track {
  116. display: flex;
  117. flex-direction: row;
  118. gap: 12px;
  119. overflow-x: auto;
  120. overscroll-behavior-x: contain;
  121. scroll-snap-type: x proximity;
  122. padding-bottom: 8px;
  123. &::-webkit-scrollbar { display: none; }
  124. &::after { content: ''; flex: 0 0 1px; }
  125. }
  126. .schedule-card {
  127. min-width: 80%;
  128. min-height: 90px;
  129. }
  130. .customColor {
  131. background: linear-gradient(180deg, #8B5CF6 50%, #EC4899 50%);
  132. background-clip: text;
  133. -webkit-background-clip: text;
  134. -webkit-text-fill-color: transparent;
  135. }
  136. </style>