|
|
@@ -2,98 +2,108 @@
|
|
|
<template>
|
|
|
<q-page class="sob-medida-page">
|
|
|
<div class="page-shell">
|
|
|
- <span class="page-title gradient-diarista">Serviço Sob Medida</span>
|
|
|
-
|
|
|
- <q-card flat bordered class="figma-card compact-card">
|
|
|
- <div class="card-title text-center gradient-diarista">
|
|
|
- Seu pedido
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="field-label text-center gradient-diarista">
|
|
|
- Quantidade de serviço
|
|
|
- </div>
|
|
|
-
|
|
|
-
|
|
|
-<div class="quantity-stepper">
|
|
|
- <q-btn
|
|
|
- round
|
|
|
- flat
|
|
|
- icon="remove"
|
|
|
- class="quantity-btn"
|
|
|
- @click="decreaseQuantity"
|
|
|
- />
|
|
|
-
|
|
|
- <span class="quantity-value gradient-diarista">
|
|
|
- {{ quantity }}
|
|
|
- </span>
|
|
|
-
|
|
|
- <q-btn
|
|
|
- round
|
|
|
- flat
|
|
|
- icon="add"
|
|
|
- class="quantity-btn"
|
|
|
- @click="increaseQuantity"
|
|
|
- />
|
|
|
-
|
|
|
-</div>
|
|
|
- <div class="options-grid gradient-diarista">
|
|
|
- <div
|
|
|
- v-for="option in addressTypesOptions"
|
|
|
- :key="option.value"
|
|
|
- class="option-col"
|
|
|
- >
|
|
|
- <q-radio
|
|
|
- v-model="selectedOption"
|
|
|
- :val="option.value"
|
|
|
- :label="option.label"
|
|
|
- color="purple"
|
|
|
- dense
|
|
|
- />
|
|
|
- </div>
|
|
|
-</div>
|
|
|
-
|
|
|
- <div class="field-label text-center gradient-diarista">
|
|
|
- Tipo de serviço
|
|
|
-</div>
|
|
|
-
|
|
|
-<div class="service-type-inline">
|
|
|
- <div
|
|
|
- v-for="serviceType in serviceTypes"
|
|
|
- :key="serviceType.id"
|
|
|
- class="option-col"
|
|
|
- >
|
|
|
- <q-radio
|
|
|
- v-model="selectedServiceType"
|
|
|
- :val="serviceType.id"
|
|
|
- :label="serviceType.description"
|
|
|
- color="purple"
|
|
|
- dense
|
|
|
- />
|
|
|
- </div>
|
|
|
-</div>
|
|
|
-
|
|
|
-<div class="field-label text-center gradient-diarista">Especialidade preferencial?</div>
|
|
|
-<div class="options-grid">
|
|
|
-<div class="options-grid">
|
|
|
- <div
|
|
|
- v-for="item in specialties"
|
|
|
- :key="item.id"
|
|
|
- class="option-col"
|
|
|
- >
|
|
|
- <q-checkbox
|
|
|
- v-model="selectedSpecialties"
|
|
|
- :val="item.id"
|
|
|
- :label="item.description"
|
|
|
- color="purple"
|
|
|
- dense
|
|
|
- />
|
|
|
- </div>
|
|
|
-</div>
|
|
|
-</div>
|
|
|
+ <span class="page-title gradient-diarista">
|
|
|
+ {{ $t('sob_medida.page_title') }}
|
|
|
+ </span>
|
|
|
+
|
|
|
+ <!-- CARD PEDIDO -->
|
|
|
+ <q-card flat bordered class="figma-card compact-card">
|
|
|
+ <div class="card-title text-center gradient-diarista">
|
|
|
+ {{ $t('sob_medida.your_order') }}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- quantidade -->
|
|
|
+ <div class="field-label text-center gradient-diarista">
|
|
|
+ {{ $t('sob_medida.quantity_service') }}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="quantity-stepper">
|
|
|
+ <q-btn
|
|
|
+ round
|
|
|
+ flat
|
|
|
+ icon="remove"
|
|
|
+ class="quantity-btn"
|
|
|
+ @click="decreaseQuantity"
|
|
|
+ />
|
|
|
+
|
|
|
+ <span class="quantity-value gradient-diarista">
|
|
|
+ {{ quantity }}
|
|
|
+ </span>
|
|
|
+
|
|
|
+ <q-btn
|
|
|
+ round
|
|
|
+ flat
|
|
|
+ icon="add"
|
|
|
+ class="quantity-btn"
|
|
|
+ @click="increaseQuantity"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- endereço -->
|
|
|
+ <div class="options-grid gradient-diarista">
|
|
|
+ <div
|
|
|
+ v-for="option in addressTypesOptions"
|
|
|
+ :key="option.value"
|
|
|
+ class="option-col"
|
|
|
+ >
|
|
|
+ <q-radio
|
|
|
+ v-model="selectedOption"
|
|
|
+ :val="option.value"
|
|
|
+ :label="option.label"
|
|
|
+ color="purple"
|
|
|
+ dense
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- tipo serviço -->
|
|
|
+ <div class="field-label text-center gradient-diarista">
|
|
|
+ {{ $t('sob_medida.service_type') }}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="service-type-inline">
|
|
|
+ <div
|
|
|
+ v-for="serviceType in serviceTypes"
|
|
|
+ :key="serviceType.id"
|
|
|
+ class="option-col"
|
|
|
+ >
|
|
|
+ <q-radio
|
|
|
+ v-model="selectedServiceType"
|
|
|
+ :val="serviceType.id"
|
|
|
+ :label="serviceType.description"
|
|
|
+ color="purple"
|
|
|
+ dense
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- especialidades -->
|
|
|
+ <div class="field-label text-center gradient-diarista">
|
|
|
+ {{ $t('sob_medida.preferred_specialty') }}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="options-grid specialties-grid">
|
|
|
+ <div
|
|
|
+ v-for="item in specialties"
|
|
|
+ :key="item.id"
|
|
|
+ class="specialty-col"
|
|
|
+ >
|
|
|
+ <q-checkbox
|
|
|
+ v-model="selectedSpecialties"
|
|
|
+ :val="item.id"
|
|
|
+ :label="item.description"
|
|
|
+ color="purple"
|
|
|
+ dense
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
|
|
|
+ <!-- descrição -->
|
|
|
<div class="field-label text-center gradient-diarista">
|
|
|
- Descreva detalhes do pedido
|
|
|
- <span class="optional">(opcional)</span>
|
|
|
+ {{ $t('sob_medida.description_label') }}
|
|
|
+ <span class="optional">
|
|
|
+ {{ $t('sob_medida.optional') }}
|
|
|
+ </span>
|
|
|
</div>
|
|
|
|
|
|
<q-input
|
|
|
@@ -104,62 +114,67 @@
|
|
|
color="dark"
|
|
|
input-class="text-black"
|
|
|
class="description-box"
|
|
|
- placeholder="Olá, desejo profissional dedicado que irá fazer..."
|
|
|
+ :placeholder="$t('sob_medida.description_placeholder')"
|
|
|
/>
|
|
|
</q-card>
|
|
|
|
|
|
- <!-- Faixa -->
|
|
|
- <div class="section-title gradient-diarista">Faixa de preço por 8 horas</div>
|
|
|
-<q-card flat bordered class="figma-card compact-card">
|
|
|
- <div class="range-container">
|
|
|
- <!-- bolha min -->
|
|
|
- <div
|
|
|
- class="price-pin"
|
|
|
- :style="{ left: minPosition + '%' }"
|
|
|
- >
|
|
|
- <span>{{ priceRange.min }}</span>
|
|
|
- </div>
|
|
|
+ <!-- FAIXA -->
|
|
|
+ <div class="section-title gradient-diarista">
|
|
|
+ {{ $t('sob_medida.price_range_title') }}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <q-card flat bordered class="figma-card compact-card">
|
|
|
+ <div class="range-container">
|
|
|
+ <div
|
|
|
+ class="price-pin"
|
|
|
+ :style="{ left: minPosition + '%' }"
|
|
|
+ >
|
|
|
+ <span>{{ priceRange.min }}</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div
|
|
|
+ class="price-pin"
|
|
|
+ :style="{ left: maxPosition + '%' }"
|
|
|
+ >
|
|
|
+ <span>{{ priceRange.max }}</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <q-range
|
|
|
+ v-model="priceRange"
|
|
|
+ :min="PRICE_LIMITS.min"
|
|
|
+ :max="PRICE_LIMITS.max"
|
|
|
+ color="secondary"
|
|
|
+ class="price-range"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
|
|
|
- <!-- bolha max -->
|
|
|
- <div
|
|
|
- class="price-pin"
|
|
|
- :style="{ left: maxPosition + '%' }"
|
|
|
- >
|
|
|
- <span>{{ priceRange.max }}</span>
|
|
|
- </div>
|
|
|
+ <div class="range-helper gradient-diarista">
|
|
|
+ {{ $t('sob_medida.price_range_helper') }}
|
|
|
+ </div>
|
|
|
+ </q-card>
|
|
|
+
|
|
|
+ <!-- DATA -->
|
|
|
+ <div class="section-title gradient-diarista">
|
|
|
+ {{ $t('sob_medida.date_and_time') }}
|
|
|
+ </div>
|
|
|
|
|
|
- <q-range
|
|
|
- v-model="priceRange"
|
|
|
- :min="PRICE_LIMITS.min"
|
|
|
- :max="PRICE_LIMITS.max"
|
|
|
- color="secondary"
|
|
|
- class="price-range"
|
|
|
- />
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="range-helper gradient-diarista">
|
|
|
- Selecione a faixa de preço integral para receber propostas de diaristas.
|
|
|
- </div>
|
|
|
-</q-card>
|
|
|
-
|
|
|
- <!-- Data -->
|
|
|
- <div class="section-title gradient-diarista">Data e hora</div>
|
|
|
<q-card flat bordered class="figma-card date-card">
|
|
|
<q-date
|
|
|
- v-model="selectedDate"
|
|
|
- minimal
|
|
|
- color="purple"
|
|
|
- class="figma-date calendar-custom "
|
|
|
-/>
|
|
|
+ v-model="selectedDate"
|
|
|
+ minimal
|
|
|
+ color="purple"
|
|
|
+ class="figma-date calendar-custom"
|
|
|
+ />
|
|
|
</q-card>
|
|
|
-
|
|
|
</div>
|
|
|
</q-page>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
+// IMPORTS
|
|
|
import { ref, computed, watch, onMounted } from 'vue'
|
|
|
import { useQuasar } from 'quasar'
|
|
|
+import { useRouter } from 'vue-router'
|
|
|
|
|
|
import ServiceSelectionSheet from 'src/pages/search/components/ServiceSelectionSheet.vue'
|
|
|
import ServiceTimeSelectionDialog from 'src/pages/search/components/ServiceTimeSelectionDialog.vue'
|
|
|
@@ -168,28 +183,24 @@ import { createCustomSchedule } from 'src/api/customSchedules'
|
|
|
import { getPrimaryAddress } from 'src/api/address'
|
|
|
import { getPublicServiceTypes } from 'src/api/serviceTypes'
|
|
|
import { getPublicSpecialties } from 'src/api/specialties'
|
|
|
+import {useI18n} from 'vue-i18n'
|
|
|
import { userStore } from 'src/stores/user'
|
|
|
import { calculateDailyPrices } from 'src/helpers/utils'
|
|
|
|
|
|
+// SETUP
|
|
|
const $q = useQuasar()
|
|
|
+const router = useRouter()
|
|
|
const user = userStore()
|
|
|
+const { t } = useI18n()
|
|
|
|
|
|
-const addressTypesOptions = computed(() => [
|
|
|
- { label: 'Residencial', value: 'home' },
|
|
|
- { label: 'Comercial', value: 'commercial' }
|
|
|
-])
|
|
|
-
|
|
|
-// listas vindas da API
|
|
|
+// REFS
|
|
|
const serviceTypes = ref([])
|
|
|
const specialties = ref([])
|
|
|
+const address = ref(null)
|
|
|
|
|
|
-// seleções do usuário
|
|
|
const selectedServiceType = ref(null)
|
|
|
const selectedSpecialties = ref([])
|
|
|
const selectedOption = ref('home')
|
|
|
-
|
|
|
-// campos do formulário
|
|
|
-const address = ref([])
|
|
|
const description = ref('')
|
|
|
const selectedDate = ref(null)
|
|
|
const quantity = ref(1)
|
|
|
@@ -204,6 +215,16 @@ const priceRange = ref({
|
|
|
max: 300
|
|
|
})
|
|
|
|
|
|
+// COMPUTED
|
|
|
+const addressTypesOptions = computed(() => [
|
|
|
+ { label: t('sob_medida.residential'), value: 'home' },
|
|
|
+ { label: t('sob_medida.commercial'), value: 'commercial' }
|
|
|
+])
|
|
|
+
|
|
|
+const providerPrices = computed(() =>
|
|
|
+ calculateDailyPrices(priceRange.value.max * quantity.value)
|
|
|
+)
|
|
|
+
|
|
|
const minPosition = computed(() =>
|
|
|
((priceRange.value.min - PRICE_LIMITS.min) /
|
|
|
(PRICE_LIMITS.max - PRICE_LIMITS.min)) * 100
|
|
|
@@ -214,28 +235,12 @@ const maxPosition = computed(() =>
|
|
|
(PRICE_LIMITS.max - PRICE_LIMITS.min)) * 100
|
|
|
)
|
|
|
|
|
|
-onMounted(async () => {
|
|
|
- const { data } = await getPrimaryAddress(user.user.client.id, 'client')
|
|
|
- address.value = data.payload
|
|
|
-
|
|
|
- const publicServiceTypes = await getPublicServiceTypes()
|
|
|
- serviceTypes.value = publicServiceTypes
|
|
|
-
|
|
|
- const publicSpecialties = await getPublicSpecialties()
|
|
|
- specialties.value = publicSpecialties
|
|
|
-})
|
|
|
-
|
|
|
-
|
|
|
-watch(selectedDate, (newDate, oldDate) => {
|
|
|
- if (!newDate || newDate === oldDate) return
|
|
|
- openServiceSelection()
|
|
|
-})
|
|
|
-
|
|
|
-function openServiceSelection () {
|
|
|
+// FUNCTIONS
|
|
|
+const openServiceSelection = () => {
|
|
|
$q.dialog({
|
|
|
component: ServiceSelectionSheet,
|
|
|
componentProps: {
|
|
|
- provider: calculateDailyPrices(priceRange.value.max * quantity.value),
|
|
|
+ provider: providerPrices.value,
|
|
|
selectedDate: selectedDate.value
|
|
|
}
|
|
|
}).onOk((payload) => {
|
|
|
@@ -245,27 +250,22 @@ function openServiceSelection () {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
-function openServiceTimeSelection (serviceType) {
|
|
|
+const openServiceTimeSelection = (serviceType) => {
|
|
|
$q.dialog({
|
|
|
component: ServiceTimeSelectionDialog,
|
|
|
componentProps: {
|
|
|
serviceType,
|
|
|
- provider: calculateDailyPrices(priceRange.value.max * quantity.value),
|
|
|
+ provider: providerPrices.value,
|
|
|
selectedDate: selectedDate.value
|
|
|
}
|
|
|
}).onOk(saveFinalOrder)
|
|
|
}
|
|
|
|
|
|
-async function saveFinalOrder (payloadFinal) {
|
|
|
+const saveFinalOrder = async (payloadFinal) => {
|
|
|
let [startHour, endHour] = payloadFinal.slot.split('-')
|
|
|
|
|
|
- if(startHour < 10) {
|
|
|
- startHour = '0' + startHour
|
|
|
- }
|
|
|
-
|
|
|
- if(endHour < 10) {
|
|
|
- endHour = '0' + endHour
|
|
|
- }
|
|
|
+ startHour = String(startHour).padStart(2, '0')
|
|
|
+ endHour = String(endHour).padStart(2, '0')
|
|
|
|
|
|
const payload = {
|
|
|
client_id: user.user.client.id,
|
|
|
@@ -275,7 +275,7 @@ async function saveFinalOrder (payloadFinal) {
|
|
|
period_type: String(payloadFinal.serviceType.hoursCount),
|
|
|
start_time: `${startHour}:00`,
|
|
|
end_time: `${endHour}:00`,
|
|
|
- address_type: selectedOption.value.toLowerCase(),
|
|
|
+ address_type: selectedOption.value,
|
|
|
service_type_id: selectedServiceType.value,
|
|
|
description: description.value,
|
|
|
min_price: priceRange.value.min,
|
|
|
@@ -288,17 +288,34 @@ async function saveFinalOrder (payloadFinal) {
|
|
|
|
|
|
$q.notify({
|
|
|
type: 'positive',
|
|
|
- message: 'Pedido sob medida salvo com sucesso!'
|
|
|
+ message: t('sob_medida.success_message')
|
|
|
})
|
|
|
+
|
|
|
+ router.push('/#showSuccessModal')
|
|
|
}
|
|
|
|
|
|
-function increaseQuantity () {
|
|
|
+const increaseQuantity = () => {
|
|
|
quantity.value++
|
|
|
}
|
|
|
|
|
|
-function decreaseQuantity () {
|
|
|
+const decreaseQuantity = () => {
|
|
|
if (quantity.value > 1) quantity.value--
|
|
|
}
|
|
|
+
|
|
|
+// WATCH
|
|
|
+watch(selectedDate, (newDate, oldDate) => {
|
|
|
+ if (!newDate || newDate === oldDate) return
|
|
|
+ openServiceSelection()
|
|
|
+})
|
|
|
+
|
|
|
+// LIFECYCLE
|
|
|
+onMounted(async () => {
|
|
|
+ const { data } = await getPrimaryAddress(user.user.client.id, 'client')
|
|
|
+ address.value = data.payload
|
|
|
+
|
|
|
+ serviceTypes.value = await getPublicServiceTypes()
|
|
|
+ specialties.value = await getPublicSpecialties()
|
|
|
+})
|
|
|
</script>
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
@@ -532,6 +549,31 @@ function decreaseQuantity () {
|
|
|
justify-content: center;
|
|
|
margin: 0 auto 16px;
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
+.specialties-grid {
|
|
|
+ justify-items: center;
|
|
|
+ gap: 10px 18px;
|
|
|
+ margin: 0 auto 16px;
|
|
|
+}
|
|
|
+
|
|
|
+.specialty-col {
|
|
|
+ min-width: 0;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+}
|
|
|
+
|
|
|
+.specialties-grid :deep(.q-checkbox) {
|
|
|
+ justify-content: flex-start;
|
|
|
+}
|
|
|
+
|
|
|
+.specialties-grid :deep(.q-checkbox__label) {
|
|
|
+ color: #000 !important;
|
|
|
+ font-size: 13px;
|
|
|
+ line-height: 1.2;
|
|
|
+ white-space: normal;
|
|
|
+}
|
|
|
+
|
|
|
/* hover individual do balão */
|
|
|
.price-pin:hover {
|
|
|
background: linear-gradient(180deg, #7b68ff, #5f46d8);
|