DashboardClientProposals.vue 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. <template>
  2. <div class="scroll-wrapper">
  3. <div
  4. class="dashboard-section-title gradient-diarista q-mb-sm font16 fontbold q-px-md"
  5. >
  6. {{ $t("dashboard_client.client_proposals.title") }}
  7. </div>
  8. <div class="scroll-track q-pb-md q-px-md text-text">
  9. <q-card
  10. v-for="item in data"
  11. :key="item.id"
  12. :flat="false"
  13. class="proposal-card shadow-card"
  14. >
  15. <div class="row no-wrap">
  16. <div class="column">
  17. <q-avatar
  18. class="q-mx-auto"
  19. :style="avatarColors[item.id % avatarColors.length]"
  20. >
  21. <img
  22. v-if="item.provider_photo"
  23. :src="item.provider_photo"
  24. style="object-fit: cover; border-radius: 50%"
  25. />
  26. <span v-else>
  27. {{ item.provider_name?.slice(0, 1) ?? "—" }}
  28. </span>
  29. </q-avatar>
  30. <q-space />
  31. <div class="customColor font8 fontbold">
  32. <div>
  33. {{ $t("dashboard_client.client_proposals.candidate") }}
  34. </div>
  35. <div class="no-wrap">
  36. {{ $t("dashboard_client.client_proposals.custom") }}
  37. </div>
  38. </div>
  39. </div>
  40. <div class="content">
  41. <div class="row justify-between items-center text-text">
  42. <div>
  43. <div class="font12 fontmedium">
  44. {{ item.provider_name }}
  45. <span class="font9 fontregular">
  46. {{
  47. $t("dashboard_client.client_proposals.age", {
  48. idade: item.idade,
  49. })
  50. }}
  51. </span>
  52. </div>
  53. <span
  54. v-if="getProviderGenderLabel(item, t)"
  55. class="font9 fontregular text-grey-7"
  56. >
  57. {{ getProviderGenderLabel(item, t) }}
  58. </span>
  59. <span class="font10 fontmedium">
  60. <q-icon
  61. class="font9 fontregular"
  62. color="warning"
  63. name="star"
  64. size="12px"
  65. />
  66. <span>
  67. {{ item.average_rating }}
  68. </span>
  69. </span>
  70. </div>
  71. </div>
  72. <div class="column font9 text-text">
  73. <div class="schedule-line">
  74. <span class="text-schedule-date-bold fontbold">
  75. {{ formatWeekday(item.date) }}
  76. </span>
  77. <span class="text-schedule-date-regular">
  78. {{ ", " + formatDayMonth(item.date) }}
  79. </span>
  80. </div>
  81. <div class="schedule-line q-pt-xs">
  82. <span class="text-schedule-date-regular">
  83. {{ $t("dashboard_client.next_schedules.from") }}&nbsp;
  84. </span>
  85. <span class="text-schedule-date-bold fontbold">
  86. {{ item.start_time?.slice(0, 5) }}
  87. {{ $t("dashboard_client.next_schedules.to") }}
  88. {{ item.end_time?.slice(0, 5) }}
  89. </span>
  90. </div>
  91. </div>
  92. <!-- PREÇO -->
  93. <div class="price text-text">
  94. <span class="font9 fontmedium distance">
  95. <span class="fontbold font10">
  96. {{ $t("dashboard_client.client_proposals.distance") }}
  97. </span>
  98. <span class="font10 fontregular q-ml-sm">
  99. {{ (item.distance_km ?? "--") + " km" }}
  100. </span>
  101. </span>
  102. <span class="font14 fontbold">
  103. {{
  104. filterCurrency(
  105. chooseprice(item.period_type, item.daily_price_8h),
  106. )
  107. }}
  108. </span>
  109. <span class="col-6 font10 fontmedium">
  110. {{ formatLabelByPeriodType(item.period_type) }}
  111. </span>
  112. </div>
  113. <div class="actions">
  114. <q-btn
  115. class="btn-reject"
  116. flat
  117. label="recusar"
  118. padding="4px 12px"
  119. size="sm"
  120. @click="() => handleRefuseProposal(item.id)"
  121. />
  122. <q-btn
  123. class="btn-accept"
  124. label="aceitar"
  125. padding="4px 12px"
  126. size="sm"
  127. @click="() => handleAcceptProposal(item.id)"
  128. />
  129. </div>
  130. </div>
  131. </div>
  132. </q-card>
  133. </div>
  134. </div>
  135. </template>
  136. <script setup>
  137. import { acceptProposal, refuseProposal } from "src/api/customSchedules";
  138. import { avatarColors } from "src/helpers/avatarColors";
  139. import { useQuasar } from "quasar";
  140. import SchedulePaymentDialog from "src/components/dashboard/SchedulePaymentDialog.vue";
  141. import {
  142. chooseprice,
  143. filterCurrency,
  144. formatLabelByPeriodType,
  145. } from "src/helpers/utils";
  146. import { getProviderGenderLabel } from "src/helpers/provider";
  147. import { useI18n } from "vue-i18n";
  148. const emit = defineEmits(["refreshData"]);
  149. defineProps({
  150. data: {
  151. type: Array,
  152. default: () => [],
  153. },
  154. });
  155. const { t } = useI18n();
  156. const $q = useQuasar();
  157. // const formatTime = (time) => {
  158. // if (!time) return '';
  159. // const [hour, minute] = time.split(':');
  160. // return `${hour}:${minute}`;
  161. // };
  162. // const formatDate = (date) => {
  163. // if (!date) return '';
  164. // const d = new Date(date);
  165. // const weekday = d.toLocaleDateString('pt-BR', {
  166. // weekday: 'long'
  167. // });
  168. // const day = String(d.getDate()).padStart(2, '0');
  169. // const month = String(d.getMonth() + 1).padStart(2, '0');
  170. // return `${weekday}, ${day}-${month}`;
  171. // };
  172. const formatDayMonth = (iso) => {
  173. if (!iso) return "";
  174. return new Date(iso).toLocaleDateString("pt-BR", {
  175. day: "2-digit",
  176. month: "2-digit",
  177. });
  178. };
  179. const formatWeekday = (iso) => {
  180. if (!iso) return "";
  181. const d = new Date(iso);
  182. const w = d.toLocaleDateString("pt-BR", { weekday: "long" });
  183. return w.charAt(0).toUpperCase() + w.slice(1);
  184. };
  185. const handleAcceptProposal = async (proposalId) => {
  186. try {
  187. const response = await acceptProposal(proposalId);
  188. const schedule = response.payload;
  189. const servicePackage = {
  190. id: schedule.id,
  191. total_amount: schedule.total_amount,
  192. provider: schedule.provider,
  193. schedules: [
  194. {
  195. ...schedule,
  196. },
  197. ],
  198. };
  199. $q.dialog({
  200. component: SchedulePaymentDialog,
  201. componentProps: {
  202. servicePackage,
  203. },
  204. }).onOk(() => {
  205. emit("refreshData");
  206. });
  207. } catch (error) {
  208. console.log(error);
  209. }
  210. };
  211. const handleRefuseProposal = async (proposalId) => {
  212. // isLoading.value = true
  213. try {
  214. await refuseProposal(proposalId);
  215. // await loadProposals()
  216. emit("refreshData");
  217. } catch (error) {
  218. console.log(error);
  219. } finally {
  220. // isLoading.value = false
  221. }
  222. };
  223. </script>
  224. <style scoped lang="scss">
  225. .scroll-wrapper {
  226. overflow-x: auto;
  227. scrollbar-width: none;
  228. }
  229. .scroll-track {
  230. display: flex;
  231. gap: 12px;
  232. }
  233. .scroll-wrapper::-webkit-scrollbar {
  234. display: none;
  235. }
  236. .proposal-card {
  237. position: relative;
  238. min-width: 330px;
  239. max-width: 330px;
  240. border-radius: 18px;
  241. padding: 12px;
  242. background: white;
  243. flex-shrink: 0;
  244. }
  245. /* alinhar topo */
  246. // .proposal-card .row:first-child {
  247. // align-items: flex-start !important;
  248. // }
  249. .content {
  250. width: 100%;
  251. display: flex;
  252. flex-direction: column;
  253. justify-content: space-between;
  254. gap: 2px;
  255. }
  256. .price .distance {
  257. margin-top: 4px;
  258. }
  259. .datetime {
  260. margin-top: 2px;
  261. line-height: 1.2;
  262. }
  263. .price {
  264. position: absolute;
  265. top: 12px;
  266. right: 12px;
  267. display: flex;
  268. flex-direction: column;
  269. align-items: flex-end;
  270. }
  271. /* avatar alinhado */
  272. .q-avatar {
  273. margin-left: 0 !important;
  274. margin-right: 10px !important;
  275. }
  276. /* botões */
  277. .actions {
  278. display: flex;
  279. justify-content: flex-end;
  280. gap: 8px;
  281. margin-top: auto;
  282. }
  283. .btn-reject {
  284. background: #eee6ff;
  285. color: #6b4eff;
  286. border-radius: 20px;
  287. padding: 3px 12px;
  288. }
  289. .btn-accept {
  290. background: linear-gradient(90deg, #6b4eff, #9f6bff);
  291. color: white;
  292. border-radius: 20px;
  293. padding: 3px 12px;
  294. }
  295. .customColor {
  296. background: linear-gradient(180deg, #8b5cf6 50%, #ec4899 50%);
  297. background-clip: text;
  298. -webkit-background-clip: text;
  299. -webkit-text-fill-color: transparent;
  300. }
  301. </style>