瀏覽代碼

feat: :sparkles: feat(agendamento-sob-medida) foi ajustado a listagem das oportundidades e tambem feito as correções nas rotas

Foram realizados ajustes no fluxo de oportunidades, juntamente com correções no código responsável pela listagem no frontend. Além disso, foi implementada a criação/adequação da rota necessária para garantir a correta busca e exibição dos dados, assegurando que as informações fossem carregadas e apresentadas de forma consistente na interface

fase:dev | origin:escopo
kayo henrique 3 天之前
父節點
當前提交
3952279739

+ 11 - 0
src/api/opportunities.js

@@ -27,3 +27,14 @@ export const getOpportunityById = async (id) => {
     return null
   }
 }
+
+
+export const proposalOpportunity = async (scheduleId, providerId) => {
+  try {
+    const { data } = await api.post(`/custom-schedule/${scheduleId}/propose`, { provider_id: providerId })
+    return data.payload || null
+  } catch (error) {
+    console.error('[API] proposalOpportunity error:', error)
+    return null
+  }
+} 

+ 24 - 69
src/pages/opportunities/OpportunitiesPage.vue

@@ -1,14 +1,7 @@
 <template>
   <q-page class="opportunities-page">
     <div class="page-header">
-      <q-btn
-        flat
-        round
-        dense
-        icon="chevron_left"
-        class="back-btn"
-        @click="router.back()"
-      />
+      <q-btn flat round dense icon="chevron_left" class="back-btn" @click="router.back()" />
       <div class="page-title">
         {{ $t('provider.dashboard.opportunities.title') }}
       </div>
@@ -25,24 +18,16 @@
       <q-spinner-dots color="secondary" size="32px" />
     </div>
 
-    <div
-      v-else-if="!opportunities.length"
-      class="text-center q-pa-md text-grey"
-    >
+    <div v-else-if="!opportunities.length" class="text-center q-pa-md text-grey">
       {{ $t('provider.dashboard.opportunities.empty') }}
     </div>
 
     <div v-else class="opportunity-list">
-      <q-card
-        v-for="item in opportunities"
-        :key="item.id"
-        flat
-        class="opportunity-card"
-      >
+      <q-card v-for="item in opportunities" :key="item.id" flat class="opportunity-card">
         <div class="avatar-column">
           <img :src="item.avatar" class="client-avatar" />
           <div class="service-type">
-            {{ item.serviceType }}
+            {{ item.custom_schedule?.service_type.description }}
           </div>
         </div>
 
@@ -67,26 +52,18 @@
 
         <div class="right-content">
           <div class="price">
-            {{ $t('provider.dashboard.opportunities.currency', { value: item.price }) }}
+            {{ $t('provider.dashboard.opportunities.currency', { value: chooseprice(item.period_type) }) }}
           </div>
 
           <div class="service-address">
-            {{ item.address }}
+            {{ item.custom_schedule?.address_type }}
           </div>
 
           <div class="distance">
             {{ $t('provider.dashboard.opportunities.distance_km', { distance: item.distance }) }}
           </div>
 
-          <q-btn
-            unelevated
-            rounded
-            no-caps
-            color="secondary"
-            :label="$t('provider.dashboard.opportunities.details')"
-            class="details-btn"
-            @click="goToOpportunityDetails(item)"
-          />
+          <q-btn unelevated rounded no-caps color="secondary" :label="$t('provider.dashboard.opportunities.details')" class="details-btn" @click="goToOpportunityDetails(item)" />
         </div>
       </q-card>
     </div>
@@ -95,58 +72,21 @@
 <script setup>
 import { ref, onMounted } from 'vue'
 import { useRouter } from 'vue-router'
-import { useI18n } from 'vue-i18n'
 
 import { getProviderOpportunities } from 'src/api/opportunities'
 import { userStore } from 'src/stores/user'
 
 const router = useRouter()
-const { t } = useI18n()
 const user = userStore()
 
 const opportunities = ref([])
 const loading = ref(false)
 
-const formatHour = (time) =>
-  time ? time.slice(0, 5).replace(':', 'h') : ''
-
-
-const normalizeOpportunity = (item) => ({
-  id: item.id,
-
-  avatar: item.client?.user?.photo || '/icons/avatar.svg',
-
-  clientName:
-    item.client?.user?.name ||
-    t('provider.dashboard.opportunities.client_default'),
-
-  rating: item.client?.average_rating || 5.0,
-
-  date: new Date(
-    item.custom_schedule?.created_at || item.created_at
-  ).toLocaleDateString(),
-
-  hour: `${t('common.from')} ${formatHour(item.start_time)} ${t('common.to')} ${formatHour(item.end_time)}`,
-
-  address:
-    item.address?.address ||
-    t('provider.dashboard.opportunities.address_not_found'),
-
-  serviceType:
-    item.custom_schedule?.service_type?.descritpion ||
-    t('provider.dashboard.opportunities.client_default'),
-
-  price: Number(
-    item.custom_schedule?.max_price || 0
-  ).toFixed(2),
-
-  distance: 0
-})
 
 const goToOpportunityDetails = (item) => {
 
   const id = item.custom_schedule?.id || item.id
-  
+
   router.push({
     name: 'OpportunityDetailsPage',
     params: { id },
@@ -154,6 +94,21 @@ const goToOpportunityDetails = (item) => {
   })
 }
 
+const chooseprice = (periodType) => {
+  switch (periodType) {
+    case "8":
+      return user.user.provider_daily_price_8h
+    case "6":
+      return user.user.provider_daily_price_6h
+    case "4":
+      return user.user.provider_daily_price_4h
+    case "2":
+      return user.user.provider_daily_price_2h
+    default:
+      return 0
+  }
+}
+
 const loadOpportunities = async () => {
   loading.value = true
 
@@ -162,7 +117,7 @@ const loadOpportunities = async () => {
       user.user.provider.id
     )
 
-    opportunities.value = (response || []).map(normalizeOpportunity)
+    opportunities.value = (response || [])
   } catch (error) {
     console.error('Erro ao buscar oportunidades:', error)
     opportunities.value = []

+ 38 - 55
src/pages/opportunities/components/OpportunityDetailsPage.vue

@@ -3,14 +3,7 @@
 
     <!-- HEADER -->
     <div class="page-header">
-      <q-btn
-        flat
-        round
-        dense
-        icon="chevron_left"
-        class="back-btn"
-        @click="router.back()"
-      />
+      <q-btn flat round dense icon="chevron_left" class="back-btn" @click="router.back()" />
       <div class="page-title">
         {{ $t('provider.dashboard.opportunity_details.title') }}
       </div>
@@ -49,17 +42,12 @@
       </span>
       <br />
       {{ $t('provider.dashboard.opportunity_details.para') }}
-      <strong>{{ details.tags[0] }}</strong>
+      <!-- <strong>{{ details.tags[0] }}</strong> -->
     </div>
 
     <!-- TAGS -->
     <div v-if="details.tags?.length" class="tags-row">
-      <q-chip
-        v-for="(tag, index) in details.tags"
-        :key="index"
-        outline
-        class="chip"
-      >
+      <q-chip v-for="(tag, index) in details.tags" :key="index" outline class="chip">
         {{ tag }}
       </q-chip>
     </div>
@@ -75,14 +63,7 @@
     </div>
 
     <!-- BOTÃO -->
-    <q-btn
-      unelevated
-      rounded
-      no-caps
-      class="accept-btn"
-      :label="$t('provider.dashboard.opportunity_details.button_accept')"
-      @click="goToProposalFlow"
-    />
+    <q-btn unelevated rounded no-caps class="accept-btn" :label="$t('provider.dashboard.opportunity_details.button_accept')" @click="goToProposalFlow" />
 
     <!-- ALERTA -->
     <div class="alert-box">
@@ -98,51 +79,51 @@
 <script setup>
 import { ref, onMounted } from 'vue'
 import { useRouter, useRoute } from 'vue-router'
-import { useI18n } from 'vue-i18n'
-import AvatarMock from 'src/assets/foto_diarista_login.svg'
-import { getOpportunityById } from 'src/api/opportunities'
+import { userStore } from 'src/stores/user'
+import { getOpportunityById, proposalOpportunity } from 'src/api/opportunities'
+
 
 const router = useRouter()
 const route = useRoute()
-const { t } = useI18n()
 
+const user = userStore()
 const details = ref(null)
 const loading = ref(true)
 
-const formatHour = (time) =>
-  time ? time.slice(0, 5).replace(':', 'h') : ''
+// const formatHour = (time) =>
+//   time ? time.slice(0, 5).replace(':', 'h') : ''
 
-const normalizeDetails = (item) => ({
-  avatar: AvatarMock,
+// const normalizeDetails = (item) => ({
+//   avatar: AvatarMock,
 
-  clientName:
-    item?.schedule?.client_name ||
-    t('provider.dashboard.opportunity_details.client_default'),
+//   clientName:
+//     item?.schedule?.client_name ||
+//     t('provider.dashboard.opportunity_details.client_default'),
 
-  price: `R$ ${Number(item?.max_price || 0).toFixed(2)}`,
+//   price: `R$ ${Number(item?.max_price || 0).toFixed(2)}`,
 
-  date: item?.schedule?.date || '',
+//   date: item?.schedule?.date || '',
 
-  hour:
-    item?.schedule?.start_time && item?.schedule?.end_time
-      ? `Das ${formatHour(item.schedule.start_time)} às ${formatHour(item.schedule.end_time)}`
-      : t('provider.dashboard.opportunity_details.hour_not_found'),
+//   hour:
+//     item?.schedule?.start_time && item?.schedule?.end_time
+//       ? `Das ${formatHour(item.schedule.start_time)} às ${formatHour(item.schedule.end_time)}`
+//       : t('provider.dashboard.opportunity_details.hour_not_found'),
 
-  address:
-    item?.schedule?.address?.address ||
-    t('provider.dashboard.opportunity_details.address_not_found'),
+//   address:
+//     item?.schedule?.address?.address ||
+//     t('provider.dashboard.opportunity_details.address_not_found'),
 
-  distance:
-    item?.distance
-      ? `${item.distance} km`
-      : t('provider.dashboard.opportunity_details.distance_default'),
+//   distance:
+//     item?.distance
+//       ? `${item.distance} km`
+//       : t('provider.dashboard.opportunity_details.distance_default'),
 
-  tags: [item?.service_type_name].filter(Boolean),
+//   tags: [item?.service_type_name].filter(Boolean),
 
-  description:
-    item?.description ||
-    t('provider.dashboard.opportunity_details.description_not_found')
-})
+//   description:
+//     item?.description ||
+//     t('provider.dashboard.opportunity_details.description_not_found')
+// })
 
 onMounted(async () => {
   try {
@@ -150,7 +131,7 @@ onMounted(async () => {
     const response = await getOpportunityById(id)
 
     if (response) {
-      details.value = normalizeDetails(response)
+      details.value = response
     } else {
       console.warn('Nenhum dado retornado')
     }
@@ -161,8 +142,10 @@ onMounted(async () => {
   }
 })
 
-const goToProposalFlow = () => {
-  console.log('Ir para proposta', details.value)
+const goToProposalFlow = async () => {
+
+  await proposalOpportunity(details.value.schedule_id, user.user.provider.id)
+  router.push({ name: 'DashboardPage' })
 }
 </script>