|
|
@@ -4,11 +4,15 @@
|
|
|
<div class="page-shell">
|
|
|
<span class="page-title gradient-diarista">Serviço Sob Medida</span>
|
|
|
|
|
|
- <!-- Seu pedido -->
|
|
|
- <q-card flat bordered class=" figma-card compact-card">
|
|
|
- <div class="field-label text-center gradient-diarista">
|
|
|
- Quantidade de serviço
|
|
|
-</div>
|
|
|
+ <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
|
|
|
@@ -30,36 +34,38 @@
|
|
|
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="card-title gradient-diarista">Seu pedido</div>
|
|
|
-
|
|
|
- <div class="options-grid">
|
|
|
- <div
|
|
|
- v-for="tipo in option"
|
|
|
- :key="tipo"
|
|
|
- class="option-col"
|
|
|
- >
|
|
|
- <q-radio
|
|
|
- v-model="selectedOption"
|
|
|
- :val="tipo"
|
|
|
- :label="tipo"
|
|
|
- color="purple"
|
|
|
- dense
|
|
|
- />
|
|
|
- </div>
|
|
|
- </div>
|
|
|
|
|
|
- <div class="field-label text-center gradient-diarista">Tipo de serviço</div>
|
|
|
-<div class="options-grid">
|
|
|
+ <div class="field-label text-center gradient-diarista">
|
|
|
+ Tipo de serviço
|
|
|
+</div>
|
|
|
+
|
|
|
+<div class="service-type-inline">
|
|
|
<div
|
|
|
- v-for="tipo in selectedServiceTypes"
|
|
|
- :key="tipo"
|
|
|
+ v-for="serviceType in serviceTypes"
|
|
|
+ :key="serviceType.id"
|
|
|
class="option-col"
|
|
|
>
|
|
|
<q-radio
|
|
|
v-model="selectedServiceType"
|
|
|
- :val="tipo"
|
|
|
- :label="tipo"
|
|
|
+ :val="serviceType.id"
|
|
|
+ :label="serviceType.description"
|
|
|
color="purple"
|
|
|
dense
|
|
|
/>
|
|
|
@@ -67,23 +73,25 @@
|
|
|
</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"
|
|
|
+ :key="item.id"
|
|
|
class="option-col"
|
|
|
>
|
|
|
<q-checkbox
|
|
|
v-model="selectedSpecialties"
|
|
|
- :val="item"
|
|
|
- :label="item"
|
|
|
+ :val="item.id"
|
|
|
+ :label="item.description"
|
|
|
color="purple"
|
|
|
dense
|
|
|
/>
|
|
|
</div>
|
|
|
+</div>
|
|
|
</div>
|
|
|
|
|
|
- i<div class="field-label text-center gradient-diarista">
|
|
|
+ <div class="field-label text-center gradient-diarista">
|
|
|
Descreva detalhes do pedido
|
|
|
<span class="optional">(opcional)</span>
|
|
|
</div>
|
|
|
@@ -150,47 +158,52 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { ref, computed, watch } from 'vue'
|
|
|
+import { ref, computed, watch, onMounted } from 'vue'
|
|
|
import { useQuasar } from 'quasar'
|
|
|
|
|
|
import ServiceSelectionSheet from 'src/pages/search/components/ServiceSelectionSheet.vue'
|
|
|
import ServiceTimeSelectionDialog from 'src/pages/search/components/ServiceTimeSelectionDialog.vue'
|
|
|
|
|
|
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 { userStore } from 'src/stores/user'
|
|
|
import { calculateDailyPrices } from 'src/helpers/utils'
|
|
|
|
|
|
const $q = useQuasar()
|
|
|
-const user =userStore();
|
|
|
-
|
|
|
-const option = ['Residencial', 'Comercial']
|
|
|
-
|
|
|
-const selectedServiceTypes = [
|
|
|
- 'Limpeza',
|
|
|
- 'Pré-mudança',
|
|
|
- 'Evento',
|
|
|
- 'Pós-obra'
|
|
|
-]
|
|
|
-
|
|
|
-const specialties = [
|
|
|
- 'Passar roupa',
|
|
|
- 'Limpar vidros',
|
|
|
- 'Lavar roupa',
|
|
|
- 'Cozinhar'
|
|
|
-]
|
|
|
-
|
|
|
-const selectedOption = ref('Residencial')
|
|
|
-const selectedServiceType = ref('Limpeza')
|
|
|
+const user = userStore()
|
|
|
+
|
|
|
+const addressTypesOptions = computed(() => [
|
|
|
+ { label: 'Residencial', value: 'home' },
|
|
|
+ { label: 'Comercial', value: 'commercial' }
|
|
|
+])
|
|
|
+
|
|
|
+// listas vindas da API
|
|
|
+const serviceTypes = ref([])
|
|
|
+const specialties = ref([])
|
|
|
+
|
|
|
+// 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 priceRange = ref({ min: 150, max: 300 })
|
|
|
const selectedDate = ref(null)
|
|
|
+const quantity = ref(1)
|
|
|
|
|
|
const PRICE_LIMITS = Object.freeze({
|
|
|
min: 100,
|
|
|
max: 500
|
|
|
})
|
|
|
|
|
|
+const priceRange = ref({
|
|
|
+ min: 150,
|
|
|
+ max: 300
|
|
|
+})
|
|
|
+
|
|
|
const minPosition = computed(() =>
|
|
|
((priceRange.value.min - PRICE_LIMITS.min) /
|
|
|
(PRICE_LIMITS.max - PRICE_LIMITS.min)) * 100
|
|
|
@@ -201,13 +214,28 @@ 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 () {
|
|
|
$q.dialog({
|
|
|
component: ServiceSelectionSheet,
|
|
|
componentProps: {
|
|
|
- provider: providerMock.value,
|
|
|
+ provider: calculateDailyPrices(priceRange.value.max * quantity.value),
|
|
|
selectedDate: selectedDate.value
|
|
|
}
|
|
|
}).onOk((payload) => {
|
|
|
@@ -225,34 +253,30 @@ function openServiceTimeSelection (serviceType) {
|
|
|
provider: calculateDailyPrices(priceRange.value.max * quantity.value),
|
|
|
selectedDate: selectedDate.value
|
|
|
}
|
|
|
- }).onOk(async (payloadFinal) => {
|
|
|
- await saveFinalOrder(payloadFinal)
|
|
|
-})
|
|
|
+ }).onOk(saveFinalOrder)
|
|
|
}
|
|
|
|
|
|
-/**
|
|
|
- * abre modal somente quando usuário escolher um dia
|
|
|
- */
|
|
|
-watch(selectedDate, (newDate, previousDate) => {
|
|
|
- if (!newDate) return
|
|
|
- if (newDate === previousDate) return
|
|
|
+async function saveFinalOrder (payloadFinal) {
|
|
|
+ let [startHour, endHour] = payloadFinal.slot.split('-')
|
|
|
|
|
|
- openServiceSelection()
|
|
|
-})
|
|
|
+ if(startHour < 10) {
|
|
|
+ startHour = '0' + startHour
|
|
|
+ }
|
|
|
|
|
|
-async function saveFinalOrder (payloadFinal) {
|
|
|
- const [startHour, endHour] = payloadFinal.slot.split('-')
|
|
|
+ if(endHour < 10) {
|
|
|
+ endHour = '0' + endHour
|
|
|
+ }
|
|
|
|
|
|
const payload = {
|
|
|
client_id: user.user.client.id,
|
|
|
- // address_id: 1,endereço principal do cliente, por enquanto hardcoded
|
|
|
- // quantity: 1,pegar do tipo de serviço escolhido, por enquanto hardcoded
|
|
|
+ address_id: address.value?.id,
|
|
|
+ quantity: quantity.value,
|
|
|
date: payloadFinal.date,
|
|
|
period_type: String(payloadFinal.serviceType.hoursCount),
|
|
|
- start_time: `${startHour}:00:00`,
|
|
|
- end_time: `${endHour}:00:00`,
|
|
|
+ start_time: `${startHour}:00`,
|
|
|
+ end_time: `${endHour}:00`,
|
|
|
address_type: selectedOption.value.toLowerCase(),
|
|
|
- // service_type_id: 1 pega do cadastro do tipo de serviço, por enquanto hardcoded
|
|
|
+ service_type_id: selectedServiceType.value,
|
|
|
description: description.value,
|
|
|
min_price: priceRange.value.min,
|
|
|
max_price: priceRange.value.max,
|
|
|
@@ -268,16 +292,12 @@ async function saveFinalOrder (payloadFinal) {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
-const quantity = ref(1)
|
|
|
-
|
|
|
function increaseQuantity () {
|
|
|
quantity.value++
|
|
|
}
|
|
|
|
|
|
function decreaseQuantity () {
|
|
|
- if (quantity.value > 1) {
|
|
|
- quantity.value--
|
|
|
- }
|
|
|
+ if (quantity.value > 1) quantity.value--
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
@@ -319,6 +339,26 @@ function decreaseQuantity () {
|
|
|
margin-bottom: 16px;
|
|
|
}
|
|
|
|
|
|
+.options-grid {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(2, 1fr);
|
|
|
+ gap: 12px 20px;
|
|
|
+ justify-items: center;
|
|
|
+ margin-bottom: 12px;
|
|
|
+
|
|
|
+ :deep(.q-radio__label),
|
|
|
+ :deep(.q-checkbox__label) {
|
|
|
+ color: #000 !important;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 400;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.q-checkbox__inner),
|
|
|
+ :deep(.q-radio__inner) {
|
|
|
+ color: #a78bfa !important;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
.section-title {
|
|
|
margin-left: 16px;
|
|
|
}
|
|
|
@@ -335,11 +375,7 @@ function decreaseQuantity () {
|
|
|
font-weight: 400;
|
|
|
}
|
|
|
|
|
|
-.options-grid {
|
|
|
- display: grid;
|
|
|
- grid-template-columns: repeat(2, 1fr);
|
|
|
- gap: 12px 20px;
|
|
|
-}
|
|
|
+
|
|
|
|
|
|
.option-col {
|
|
|
min-width: 0;
|
|
|
@@ -351,6 +387,7 @@ function decreaseQuantity () {
|
|
|
|
|
|
.description-box :deep(textarea) {
|
|
|
min-height: 90px;
|
|
|
+ resize: none;
|
|
|
}
|
|
|
|
|
|
/* CARD */
|
|
|
@@ -488,6 +525,13 @@ function decreaseQuantity () {
|
|
|
font-weight: 700;
|
|
|
}
|
|
|
|
|
|
+.service-type-inline {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(2, 160px);
|
|
|
+ gap: 12px 24px;
|
|
|
+ justify-content: center;
|
|
|
+ margin: 0 auto 16px;
|
|
|
+}
|
|
|
/* hover individual do balão */
|
|
|
.price-pin:hover {
|
|
|
background: linear-gradient(180deg, #7b68ff, #5f46d8);
|