Kaynağa Gözat

feat: :sparkles: feat (agendamentos) tela de ver detalhes dos proximos servicos com acoes

foi refatorada a tela de ver detalhes dos proximos servicos para o layout correto, e foram adicionadas as  funcoes de cancelar e redirecionar para o chatbot

fase:dev | origin:escopo
Gustavo Zanatta 1 hafta önce
ebeveyn
işleme
317c3b4c47

+ 210 - 0
src/components/dashboard/NextSchedulesDetailsDialog.vue

@@ -0,0 +1,210 @@
+<template>
+  <q-dialog ref="dialogRef" @hide="onDialogHide">
+    <q-card class="next-schedule-dialog-card bg-surface shadow-card" :flat="false">
+
+      <div class="row justify-end q-pt-sm q-pr-sm">
+        <q-btn flat round dense icon="close" color="grey-6" size="sm" @click="onDialogCancel" />
+      </div>
+
+      <q-card-section class="column items-center q-pt-xs q-pb-sm">
+        <q-avatar size="72px" class="q-mb-sm">
+          <img :src="schedule.customer_photo || 'https://cdn.quasar.dev/img/avatar.png'" />
+        </q-avatar>
+        <div class="text-subtitle1 text-weight-bold text-text">
+          {{ schedule.customer_name ?? schedule.client_name }}
+        </div>
+        <div class="text-price q-mt-xs">{{ formatCurrency(schedule.total_amount) }}</div>
+        <div class="text-caption text-grey-6 q-mt-xxs">{{ periodLabel }}</div>
+      </q-card-section>
+
+      <q-separator class="q-mx-lg" />
+
+      <q-card-section class="column items-center q-py-sm text-text">
+        <div class="text-body2 text-center">
+          <span class="text-weight-bold">{{ weekdayLabel }}</span>
+          <span class="text-grey-7">{{', ' + dayMonthLabel }}</span>
+        </div>
+        <div class="text-body2 text-center q-mt-xxs">
+          {{ $t('common.from') }}
+          <span class="text-weight-bold">{{ schedule.start_time?.slice(0, 5) }}</span>
+          {{ $t('common.to') }}
+          <span class="text-weight-bold">{{ schedule.end_time?.slice(0, 5) }}</span>
+        </div>
+      </q-card-section>
+
+      <q-separator class="q-mx-lg" />
+
+      <q-card-section class="column items-center q-py-sm">
+        <div class="row items-center q-gutter-x-xs">
+          <q-icon
+            :name="schedule.offers_meal ? 'mdi-silverware' : 'mdi-close-circle-outline'"
+            :color="schedule.offers_meal ? 'secondary' : 'grey-5'"
+            size="18px"
+          />
+          <span class="text-body2" :class="schedule.offers_meal ? 'text-text' : 'text-grey-6'">
+            {{ schedule.offers_meal
+              ? $t('provider.dashboard.next_schedules.offers_meal')
+              : $t('provider.dashboard.next_schedules.no_meal') }}
+          </span>
+        </div>
+      </q-card-section>
+
+      <q-separator class="q-mx-lg" />
+
+      <q-card-section class="column items-center q-py-sm q-px-lg">
+        <div class="row items-start q-gutter-x-xs no-wrap">
+          <q-icon name="mdi-map-marker-outline" color="primary" size="20px" class="q-mt-xxs flex-shrink-0" />
+          <span class="text-body2 text-grey-8 text-center">{{ formattedAddress || 'N/A' }}</span>
+        </div>
+        <q-btn
+          flat
+          no-caps
+          dense
+          color="primary"
+          icon="mdi-content-copy"
+          :label="$t('provider.dashboard.next_schedules.copy_address')"
+          class="q-mt-xs btn-copy"
+          @click="copyAddress"
+        />
+      </q-card-section>
+
+      <q-card-section class="q-pt-xs q-pb-sm q-px-lg">
+        <q-btn
+          unelevated
+          rounded
+          no-caps
+          color="primary"
+          class="full-width btn-close"
+          :label="$t('provider.dashboard.cancel_schedule.close')"
+          @click="onDialogCancel"
+        />
+      </q-card-section>
+
+      <q-card-section class="q-pt-none q-pb-md text-center">
+        <div class="row justify-center q-gutter-x-lg">
+          <q-btn
+            flat
+            no-caps
+            color="grey-7"
+            size="sm"
+            :label="$t('provider.dashboard.next_schedules.btn_cancel_service')"
+            @click="openCancelDialog"
+          />
+          <q-btn
+            flat
+            no-caps
+            color="grey-7"
+            size="sm"
+            :label="$t('provider.dashboard.next_schedules.btn_need_help')"
+            @click="openHelp"
+          />
+        </div>
+      </q-card-section>
+
+    </q-card>
+  </q-dialog>
+</template>
+
+<script setup>
+import { computed } from 'vue'
+import { useDialogPluginComponent, useQuasar } from 'quasar'
+import { useI18n } from 'vue-i18n'
+import { formatCurrency, formatAddress } from 'src/helpers/utils'
+import { labelsPeriodTypes } from 'src/helpers/arraysOptions/labelsPeriodTypes.js'
+import ScheduleCancelDialog from './ScheduleCancelDialog.vue'
+import ProfileHelpDialog from 'src/components/profile/ProfileHelpDialog.vue'
+
+const props = defineProps({
+  schedule: {
+    type: Object,
+    required: true
+  }
+})
+
+const { t } = useI18n()
+const $q = useQuasar()
+const { dialogRef, onDialogHide, onDialogCancel, onDialogOK } = useDialogPluginComponent()
+
+const periodLabel = computed(() => {
+  const found = labelsPeriodTypes.find(l => l.value == props.schedule.period_type)
+  return found ? t(found.label) : ''
+})
+
+const parseLocalDate = (dateStr) => {
+  if (!dateStr) return null
+  const s = String(dateStr)
+  const iso = s.match(/^(\d{4})-(\d{2})-(\d{2})/)
+  if (iso) return new Date(+iso[1], +iso[2] - 1, +iso[3])
+  const dmy = s.match(/^(\d{2})\/(\d{2})\/(\d{4})/)
+  if (dmy) return new Date(+dmy[3], +dmy[2] - 1, +dmy[1])
+  return null
+}
+
+const weekdayLabel = computed(() => {
+  const d = parseLocalDate(props.schedule.date)
+  if (!d) return ''
+  const w = d.toLocaleDateString('pt-BR', { weekday: 'long' })
+  return w.charAt(0).toUpperCase() + w.slice(1)
+})
+
+const dayMonthLabel = computed(() => {
+  const d = parseLocalDate(props.schedule.date)
+  if (!d) return ''
+  return d.toLocaleDateString('pt-BR', { day: '2-digit', month: '2-digit' })
+})
+
+const formattedAddress = computed(() => formatAddress(props.schedule.address))
+
+const copyAddress = () => {
+  const addr = formattedAddress.value
+  if (addr) navigator.clipboard.writeText(addr)
+  $q.notify({ message: t('provider.dashboard.next_schedules.address_copied'), color: 'positive' })
+}
+
+const openCancelDialog = () => {
+  $q.dialog({
+    component: ScheduleCancelDialog,
+    componentProps: { schedule: props.schedule }
+  }).onOk(() => {
+    onDialogOK({ action: 'cancelled', id: props.schedule.id })
+  })
+}
+
+const openHelp = () => {
+  $q.dialog({ component: ProfileHelpDialog })
+}
+</script>
+
+<style scoped lang="scss">
+.next-schedule-dialog-card {
+  width: 320px;
+  max-width: 92vw;
+  border-radius: 20px !important;
+  overflow: hidden;
+}
+
+.text-price {
+  font-size: 26px;
+  font-weight: 700;
+  color: var(--q-primary);
+}
+
+.btn-close {
+  font-weight: 700;
+  font-size: 16px;
+  padding: 10px 0;
+}
+
+.btn-copy {
+  font-size: 12px;
+  font-weight: 600;
+}
+
+.flex-shrink-0 {
+  flex-shrink: 0;
+}
+
+.q-mt-xxs {
+  margin-top: 2px;
+}
+</style>

+ 4 - 1
src/i18n/locales/en.json

@@ -295,7 +295,10 @@
         "place_home": "Residential",
         "place_apartment": "Apartment",
         "place_unknown": "Address",
-        "address_copied": "Address copied to clipboard"
+        "address_copied": "Address copied to clipboard",
+        "copy_address": "copy address",
+        "btn_cancel_service": "Cancel service",
+        "btn_need_help": "I need help"
       },
       "last_schedules": {
         "title": "Last ones performed",

+ 4 - 1
src/i18n/locales/es.json

@@ -295,7 +295,10 @@
         "place_home": "Residencial",
         "place_apartment": "Apartamento",
         "place_unknown": "Dirección",
-        "address_copied": "Dirección copiada al portapapeles"
+        "address_copied": "Dirección copiada al portapapeles",
+        "copy_address": "copiar dirección",
+        "btn_cancel_service": "Cancelar servicio",
+        "btn_need_help": "Preciso de ayuda"
       },
       "last_schedules": {
         "title": "Últimas realizadas",

+ 4 - 1
src/i18n/locales/pt.json

@@ -295,7 +295,10 @@
         "place_home": "Residencial",
         "place_apartment": "Apartamento",
         "place_unknown": "Endereço",
-        "address_copied": "Endereço copiado!"
+        "address_copied": "Endereço copiado!",
+        "copy_address": "copiar endereço",
+        "btn_cancel_service": "Cancelar serviço",
+        "btn_need_help": "Preciso de ajuda"
       },
       "last_schedules": {
         "title": "Últimas realizadas",

+ 3 - 2
src/pages/dashboard/DashboardPage.vue

@@ -34,6 +34,7 @@ import DashboardTodayServices from 'src/components/dashboard/DashboardTodayServi
 import DashboardNextSchedules from 'src/components/dashboard/DashboardNextSchedules.vue';
 import DashboardOpportunities from 'src/components/dashboard/DashboardOpportunities.vue';
 import SolicitationDetailsDialog from 'src/components/dashboard/SolicitationDetailsDialog.vue';
+import NextSchedulesDetailsDialog from 'src/components/dashboard/NextSchedulesDetailsDialog.vue';
 import { onMounted, ref } from 'vue';
 import { useQuasar } from 'quasar';
 import { dadosDashboard } from 'src/api/dashboard';
@@ -84,8 +85,8 @@ const openDetailsDialog = (solicitation, initialView = 'details') => {
 
 const openNextScheduleDialog = (schedule) => {
   $q.dialog({
-    component: SolicitationDetailsDialog,
-    componentProps: { solicitation: schedule }
+    component: NextSchedulesDetailsDialog,
+    componentProps: { schedule }
   }).onOk(async ({ action }) => {
     if (action === 'cancelled') {
       await loadDashboard();