|
@@ -3,7 +3,8 @@
|
|
|
<div class="dialog-root">
|
|
<div class="dialog-root">
|
|
|
|
|
|
|
|
<div class="dialog-header row items-center q-px-md q-pt-md q-pb-sm bg-white">
|
|
<div class="dialog-header row items-center q-px-md q-pt-md q-pb-sm bg-white">
|
|
|
- <q-btn v-close-popup flat round dense icon="mdi-chevron-left" color="primary" />
|
|
|
|
|
|
|
+ <q-btn v-close-popup color="primary" dense flat icon="mdi-chevron-left" round />
|
|
|
|
|
+
|
|
|
<div class="col text-center font16 fontbold gradient-diarista">
|
|
<div class="col text-center font16 fontbold gradient-diarista">
|
|
|
{{ $t('scheduling_page.title') }}
|
|
{{ $t('scheduling_page.title') }}
|
|
|
</div>
|
|
</div>
|
|
@@ -17,6 +18,7 @@
|
|
|
<div class="font14 fontbold text-primary">
|
|
<div class="font14 fontbold text-primary">
|
|
|
{{ $t('scheduling_page.order_summary.info_text') }}
|
|
{{ $t('scheduling_page.order_summary.info_text') }}
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+
|
|
|
<div class="font12 fontmedium text-primary q-mt-xs">
|
|
<div class="font12 fontmedium text-primary q-mt-xs">
|
|
|
{{ $t('scheduling_page.order_summary.info_note') }}
|
|
{{ $t('scheduling_page.order_summary.info_note') }}
|
|
|
</div>
|
|
</div>
|
|
@@ -37,39 +39,50 @@
|
|
|
<q-card-section class="q-pa-md row items-center no-wrap">
|
|
<q-card-section class="q-pa-md row items-center no-wrap">
|
|
|
<div class="col text-center">
|
|
<div class="col text-center">
|
|
|
<div class="text-text">
|
|
<div class="text-text">
|
|
|
- <span class="font14 fontregular">{{ $t('scheduling_page.order_summary.service_label') }}</span>
|
|
|
|
|
- <span class="font14 fontbold">{{ ` ${booking.serviceType.label} (${booking.serviceType.hours})` }}</span>
|
|
|
|
|
|
|
+ <span class="font14 fontregular">
|
|
|
|
|
+ {{ $t('scheduling_page.order_summary.service_label') }}
|
|
|
|
|
+ </span>
|
|
|
|
|
+
|
|
|
|
|
+ <span class="font14 fontbold">
|
|
|
|
|
+ {{ ` ${booking.serviceType.label} (${booking.serviceType.hours})` }}
|
|
|
|
|
+ </span>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="font14 fontbold text-text">{{ formatDate(booking.date) }}</div>
|
|
|
|
|
|
|
+
|
|
|
|
|
+ <div class="font14 fontbold text-text">
|
|
|
|
|
+ {{ formatDate(booking.date) }}
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
<div class="font14 fontbold text-text">
|
|
<div class="font14 fontbold text-text">
|
|
|
{{ $t('scheduling_page.order_summary.time_range', { start: booking.slot.startHour, end: booking.slot.endHour }) }}
|
|
{{ $t('scheduling_page.order_summary.time_range', { start: booking.slot.startHour, end: booking.slot.endHour }) }}
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+
|
|
|
<q-btn
|
|
<q-btn
|
|
|
|
|
+ color="grey-5"
|
|
|
flat round dense
|
|
flat round dense
|
|
|
icon="mdi-minus-circle-outline"
|
|
icon="mdi-minus-circle-outline"
|
|
|
- color="grey-5"
|
|
|
|
|
padding="4px 8px"
|
|
padding="4px 8px"
|
|
|
@click="confirmRemove(idx)"
|
|
@click="confirmRemove(idx)"
|
|
|
/>
|
|
/>
|
|
|
</q-card-section>
|
|
</q-card-section>
|
|
|
|
|
+
|
|
|
<q-btn
|
|
<q-btn
|
|
|
|
|
+ class="full-width q-mt-sm"
|
|
|
|
|
+ color="secondary"
|
|
|
|
|
+ padding="4px 8px"
|
|
|
unelevated rounded no-caps
|
|
unelevated rounded no-caps
|
|
|
:label="$t('scheduling_page.order_summary.send_btn')"
|
|
:label="$t('scheduling_page.order_summary.send_btn')"
|
|
|
- color="secondary"
|
|
|
|
|
- padding="4px 8px"
|
|
|
|
|
- class="full-width q-mt-sm"
|
|
|
|
|
@click="submitOrder"
|
|
@click="submitOrder"
|
|
|
/>
|
|
/>
|
|
|
</q-card>
|
|
</q-card>
|
|
|
|
|
|
|
|
<div class="full-width text-center">
|
|
<div class="full-width text-center">
|
|
|
<q-btn
|
|
<q-btn
|
|
|
- outline rounded no-caps
|
|
|
|
|
- :label="$t('scheduling_page.order_summary.add_date_btn')"
|
|
|
|
|
- color="primary"
|
|
|
|
|
class="q-mt-xs"
|
|
class="q-mt-xs"
|
|
|
|
|
+ color="primary"
|
|
|
|
|
+ outline rounded no-caps
|
|
|
:disable="showCalendar"
|
|
:disable="showCalendar"
|
|
|
|
|
+ :label="$t('scheduling_page.order_summary.add_date_btn')"
|
|
|
@click="showCalendar = true"
|
|
@click="showCalendar = true"
|
|
|
/>
|
|
/>
|
|
|
</div>
|
|
</div>
|
|
@@ -79,12 +92,13 @@
|
|
|
<div v-if="loadingAvailability" class="row items-center justify-center q-py-lg">
|
|
<div v-if="loadingAvailability" class="row items-center justify-center q-py-lg">
|
|
|
<q-spinner-dots color="primary" size="36px" />
|
|
<q-spinner-dots color="primary" size="36px" />
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+
|
|
|
<div v-else class="calendar-wrapper shadow-card">
|
|
<div v-else class="calendar-wrapper shadow-card">
|
|
|
<q-date
|
|
<q-date
|
|
|
v-model="addDateValue"
|
|
v-model="addDateValue"
|
|
|
- minimal
|
|
|
|
|
- color="purple"
|
|
|
|
|
class=""
|
|
class=""
|
|
|
|
|
+ color="purple"
|
|
|
|
|
+ minimal
|
|
|
:first-day-of-week="0"
|
|
:first-day-of-week="0"
|
|
|
:options="dateOptions"
|
|
:options="dateOptions"
|
|
|
@update:model-value="onAddDateSelected"
|
|
@update:model-value="onAddDateSelected"
|
|
@@ -97,17 +111,19 @@
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup>
|
|
<script setup>
|
|
|
-import { ref, computed, onMounted } from 'vue';
|
|
|
|
|
-import { useDialogPluginComponent, useQuasar } from 'quasar';
|
|
|
|
|
|
|
+import { computed, onMounted, ref } from 'vue';
|
|
|
|
|
+import { createSchedule, getClientProviderBlocks } from 'src/api/schedule';
|
|
|
import { date } from 'quasar';
|
|
import { date } from 'quasar';
|
|
|
-import { useI18n } from 'vue-i18n';
|
|
|
|
|
-import { getProviderWorkingDays, getProviderBlockedDays } from 'src/api/providerAvailability';
|
|
|
|
|
import { getAddresses } from 'src/api/address';
|
|
import { getAddresses } from 'src/api/address';
|
|
|
-import { createSchedule, getClientProviderBlocks } from 'src/api/schedule';
|
|
|
|
|
|
|
+import { getProviderBlockedDays, getProviderWorkingDays } from 'src/api/providerAvailability';
|
|
|
|
|
+import { useDialogPluginComponent, useQuasar } from 'quasar';
|
|
|
|
|
+import { useI18n } from 'vue-i18n';
|
|
|
import { userStore } from 'src/stores/user';
|
|
import { userStore } from 'src/stores/user';
|
|
|
|
|
+import { useRouter } from 'vue-router';
|
|
|
|
|
+import { useScheduleCartStore } from 'src/stores/scheduleCart';
|
|
|
|
|
+
|
|
|
import ServiceSelectionSheet from './ServiceSelectionSheet.vue';
|
|
import ServiceSelectionSheet from './ServiceSelectionSheet.vue';
|
|
|
import ServiceTimeSelectionDialog from './ServiceTimeSelectionDialog.vue';
|
|
import ServiceTimeSelectionDialog from './ServiceTimeSelectionDialog.vue';
|
|
|
-import { useRouter } from 'vue-router';
|
|
|
|
|
|
|
|
|
|
const props = defineProps({
|
|
const props = defineProps({
|
|
|
provider: { type: Object, required: true },
|
|
provider: { type: Object, required: true },
|
|
@@ -116,79 +132,120 @@ const props = defineProps({
|
|
|
|
|
|
|
|
defineEmits([...useDialogPluginComponent.emits]);
|
|
defineEmits([...useDialogPluginComponent.emits]);
|
|
|
|
|
|
|
|
|
|
+const $q = useQuasar();
|
|
|
|
|
+const router = useRouter();
|
|
|
|
|
+
|
|
|
const { dialogRef, onDialogOK } = useDialogPluginComponent();
|
|
const { dialogRef, onDialogOK } = useDialogPluginComponent();
|
|
|
-const $q = useQuasar();
|
|
|
|
|
|
|
+
|
|
|
const { t, locale } = useI18n();
|
|
const { t, locale } = useI18n();
|
|
|
-const store = userStore();
|
|
|
|
|
|
|
|
|
|
-const bookings = ref([props.initialBooking]);
|
|
|
|
|
-const submitting = ref(false);
|
|
|
|
|
-const primaryAddress = ref(null);
|
|
|
|
|
-const router = useRouter();
|
|
|
|
|
-const showCalendar = ref(false);
|
|
|
|
|
-const addDateValue = ref(null);
|
|
|
|
|
|
|
+const store = userStore();
|
|
|
|
|
+const scheduleCart = useScheduleCartStore();
|
|
|
|
|
+
|
|
|
|
|
+const bookings = ref([props.initialBooking]);
|
|
|
|
|
+const submitting = ref(false);
|
|
|
|
|
+const primaryAddress = ref(null);
|
|
|
|
|
+const showCalendar = ref(false);
|
|
|
|
|
+const addDateValue = ref(null);
|
|
|
const loadingAvailability = ref(false);
|
|
const loadingAvailability = ref(false);
|
|
|
-const workingDays = ref([]);
|
|
|
|
|
-const blockedDays = ref([]);
|
|
|
|
|
-const providerClientBlocks = ref({
|
|
|
|
|
- existing_schedules: [],
|
|
|
|
|
- fully_blocked_weeks: [],
|
|
|
|
|
-});
|
|
|
|
|
|
|
+const workingDays = ref([]);
|
|
|
|
|
+const blockedDays = ref([]);
|
|
|
|
|
|
|
|
-const normalizeDate = (d) => d.replace(/\//g, '-');
|
|
|
|
|
|
|
+const providerClientBlocks = ref({ existing_schedules: [], fully_blocked_weeks: [] });
|
|
|
|
|
|
|
|
-const getWeekStart = (dateStr) => {
|
|
|
|
|
- const d = new Date(normalizeDate(dateStr) + 'T12:00:00');
|
|
|
|
|
- d.setDate(d.getDate() - d.getDay());
|
|
|
|
|
- return d.toISOString().slice(0, 10);
|
|
|
|
|
-};
|
|
|
|
|
|
|
+const availableWeekDays = computed(() =>
|
|
|
|
|
+ [...new Set(workingDays.value.map(wd => wd.day))]
|
|
|
|
|
+);
|
|
|
|
|
+
|
|
|
|
|
+const blockedDateSet = computed(() =>
|
|
|
|
|
+ new Set(blockedDays.value.filter(bd => bd.period === 'all').map(bd => bd.date))
|
|
|
|
|
+);
|
|
|
|
|
|
|
|
const blockedWeekStartSet = computed(() =>
|
|
const blockedWeekStartSet = computed(() =>
|
|
|
new Set(providerClientBlocks.value.fully_blocked_weeks ?? [])
|
|
new Set(providerClientBlocks.value.fully_blocked_weeks ?? [])
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
-const getServerWeekCount = (newDateStr) => {
|
|
|
|
|
|
|
+const confirmRemove = (idx) => {
|
|
|
|
|
+ $q.dialog({
|
|
|
|
|
+ title: t('scheduling_page.order_summary.remove_confirm_title'),
|
|
|
|
|
+ cancel: { label: t('scheduling_page.order_summary.remove_confirm_cancel'), flat: true, color: 'grey-6' },
|
|
|
|
|
+ ok: { label: t('scheduling_page.order_summary.remove_confirm_ok'), unelevated: true, color: 'primary', rounded: true, noCaps: true },
|
|
|
|
|
+ persistent: true,
|
|
|
|
|
+ }).onOk(() => {
|
|
|
|
|
+ bookings.value.splice(idx, 1);
|
|
|
|
|
+ });
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const dateOptions = (d) => {
|
|
|
|
|
+ const today = date.formatDate(new Date(), 'YYYY/MM/DD');
|
|
|
|
|
+
|
|
|
|
|
+ if (d < today) return false;
|
|
|
|
|
+ if (wouldExceedWeekLimit(d)) return false;
|
|
|
|
|
+
|
|
|
|
|
+ const raw = normalizeDate(d);
|
|
|
|
|
+
|
|
|
|
|
+ const parsed = new Date(`${raw}T12:00:00`);
|
|
|
|
|
+
|
|
|
|
|
+ const dayOfWeek = parsed.getDay();
|
|
|
|
|
+ const isWorking = availableWeekDays.value.includes(dayOfWeek);
|
|
|
|
|
+ const isBlocked = blockedDateSet.value.has(raw);
|
|
|
|
|
+ const isWeekBlocked = blockedWeekStartSet.value.has(getWeekStart(raw));
|
|
|
|
|
+
|
|
|
|
|
+ return isWorking && !isBlocked && !isWeekBlocked;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const formatDate = (dateStr) => {
|
|
|
|
|
+ const d = new Date(normalizeDate(dateStr) + 'T12:00:00');
|
|
|
|
|
+
|
|
|
|
|
+ const localeMap = { pt: 'pt-BR', en: 'en-US', es: 'es-ES' };
|
|
|
|
|
+
|
|
|
|
|
+ const loc = localeMap[locale.value] ?? 'pt-BR';
|
|
|
|
|
+
|
|
|
|
|
+ const weekday = new Intl.DateTimeFormat(loc, { weekday: 'long' }).format(d);
|
|
|
|
|
+ const dateFormatted = new Intl.DateTimeFormat(loc, { day: '2-digit', month: '2-digit', year: 'numeric' }).format(d);
|
|
|
|
|
+
|
|
|
|
|
+ return `${weekday.charAt(0).toUpperCase() + weekday.slice(1)}, ${dateFormatted}`;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const formatHour = (h) => `${String(h).padStart(2, '0')}:00`;
|
|
|
|
|
+
|
|
|
|
|
+const getCartWeekCount = (newDateStr) => {
|
|
|
const newWeek = getWeekStart(newDateStr);
|
|
const newWeek = getWeekStart(newDateStr);
|
|
|
- return (providerClientBlocks.value.existing_schedules ?? []).filter(
|
|
|
|
|
- (schedule) => getWeekStart(schedule.date) === newWeek
|
|
|
|
|
|
|
+
|
|
|
|
|
+ return scheduleCart.items.filter((item) =>
|
|
|
|
|
+ Number(item.provider_id) === Number(props.provider.provider_id) &&
|
|
|
|
|
+ getWeekStart(item.date) === newWeek
|
|
|
).length;
|
|
).length;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const getLocalWeekCount = (newDateStr) => {
|
|
const getLocalWeekCount = (newDateStr) => {
|
|
|
const newWeek = getWeekStart(newDateStr);
|
|
const newWeek = getWeekStart(newDateStr);
|
|
|
|
|
+
|
|
|
return bookings.value.filter((booking) => getWeekStart(booking.date) === newWeek).length;
|
|
return bookings.value.filter((booking) => getWeekStart(booking.date) === newWeek).length;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-const wouldExceedWeekLimit = (newDateStr) => {
|
|
|
|
|
- const count = getServerWeekCount(newDateStr) + getLocalWeekCount(newDateStr);
|
|
|
|
|
- return count >= 2;
|
|
|
|
|
|
|
+const getServerWeekCount = (newDateStr) => {
|
|
|
|
|
+ const newWeek = getWeekStart(newDateStr);
|
|
|
|
|
+
|
|
|
|
|
+ return (providerClientBlocks.value.existing_schedules ?? []).filter(
|
|
|
|
|
+ (schedule) => getWeekStart(schedule.date) === newWeek
|
|
|
|
|
+ ).length;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-const availableWeekDays = computed(() =>
|
|
|
|
|
- [...new Set(workingDays.value.map(wd => wd.day))]
|
|
|
|
|
-);
|
|
|
|
|
|
|
+const getWeekStart = (dateStr) => {
|
|
|
|
|
+ const d = new Date(normalizeDate(dateStr) + 'T12:00:00');
|
|
|
|
|
|
|
|
-const blockedDateSet = computed(() =>
|
|
|
|
|
- new Set(blockedDays.value.filter(bd => bd.period === 'all').map(bd => bd.date))
|
|
|
|
|
-);
|
|
|
|
|
|
|
+ d.setDate(d.getDate() - d.getDay());
|
|
|
|
|
|
|
|
-const dateOptions = (d) => {
|
|
|
|
|
- const today = date.formatDate(new Date(), 'YYYY/MM/DD');
|
|
|
|
|
- if (d < today) return false;
|
|
|
|
|
- if (wouldExceedWeekLimit(d)) return false;
|
|
|
|
|
- const raw = normalizeDate(d);
|
|
|
|
|
- const parsed = new Date(`${raw}T12:00:00`);
|
|
|
|
|
- const dayOfWeek = parsed.getDay();
|
|
|
|
|
- const isWorking = availableWeekDays.value.includes(dayOfWeek);
|
|
|
|
|
- const isBlocked = blockedDateSet.value.has(raw);
|
|
|
|
|
- const isWeekBlocked = blockedWeekStartSet.value.has(getWeekStart(raw));
|
|
|
|
|
- return isWorking && !isBlocked && !isWeekBlocked;
|
|
|
|
|
|
|
+ return d.toISOString().slice(0, 10);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const loadAvailability = async () => {
|
|
const loadAvailability = async () => {
|
|
|
loadingAvailability.value = true;
|
|
loadingAvailability.value = true;
|
|
|
|
|
+
|
|
|
try {
|
|
try {
|
|
|
const clientId = store.user?.client_id;
|
|
const clientId = store.user?.client_id;
|
|
|
|
|
+
|
|
|
const defaultClientBlocks = { existing_schedules: [], fully_blocked_weeks: [] };
|
|
const defaultClientBlocks = { existing_schedules: [], fully_blocked_weeks: [] };
|
|
|
|
|
|
|
|
const [wd, bd, clientBlocks] = await Promise.all([
|
|
const [wd, bd, clientBlocks] = await Promise.all([
|
|
@@ -198,12 +255,15 @@ const loadAvailability = async () => {
|
|
|
? getClientProviderBlocks(clientId, props.provider.provider_id)
|
|
? getClientProviderBlocks(clientId, props.provider.provider_id)
|
|
|
: Promise.resolve(defaultClientBlocks),
|
|
: Promise.resolve(defaultClientBlocks),
|
|
|
]);
|
|
]);
|
|
|
|
|
+
|
|
|
workingDays.value = wd ?? [];
|
|
workingDays.value = wd ?? [];
|
|
|
blockedDays.value = bd ?? [];
|
|
blockedDays.value = bd ?? [];
|
|
|
|
|
+
|
|
|
providerClientBlocks.value = clientBlocks ?? defaultClientBlocks;
|
|
providerClientBlocks.value = clientBlocks ?? defaultClientBlocks;
|
|
|
} catch {
|
|
} catch {
|
|
|
workingDays.value = [];
|
|
workingDays.value = [];
|
|
|
blockedDays.value = [];
|
|
blockedDays.value = [];
|
|
|
|
|
+
|
|
|
providerClientBlocks.value = { existing_schedules: [], fully_blocked_weeks: [] };
|
|
providerClientBlocks.value = { existing_schedules: [], fully_blocked_weeks: [] };
|
|
|
} finally {
|
|
} finally {
|
|
|
loadingAvailability.value = false;
|
|
loadingAvailability.value = false;
|
|
@@ -213,21 +273,24 @@ const loadAvailability = async () => {
|
|
|
const loadPrimaryAddress = async () => {
|
|
const loadPrimaryAddress = async () => {
|
|
|
try {
|
|
try {
|
|
|
const clientId = store.user?.client_id;
|
|
const clientId = store.user?.client_id;
|
|
|
|
|
+
|
|
|
if (!clientId) return;
|
|
if (!clientId) return;
|
|
|
|
|
+
|
|
|
const addresses = await getAddresses('client', clientId);
|
|
const addresses = await getAddresses('client', clientId);
|
|
|
|
|
+
|
|
|
primaryAddress.value = (addresses ?? []).find(a => a.is_primary) ?? null;
|
|
primaryAddress.value = (addresses ?? []).find(a => a.is_primary) ?? null;
|
|
|
} catch {
|
|
} catch {
|
|
|
primaryAddress.value = null;
|
|
primaryAddress.value = null;
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-onMounted(() => Promise.all([loadAvailability(), loadPrimaryAddress()]));
|
|
|
|
|
-
|
|
|
|
|
-const formatHour = (h) => `${String(h).padStart(2, '0')}:00`;
|
|
|
|
|
|
|
+const normalizeDate = (d) => d.replace(/\//g, '-');
|
|
|
|
|
|
|
|
const onAddDateSelected = (val) => {
|
|
const onAddDateSelected = (val) => {
|
|
|
if (!val) return;
|
|
if (!val) return;
|
|
|
|
|
+
|
|
|
addDateValue.value = null;
|
|
addDateValue.value = null;
|
|
|
|
|
+
|
|
|
const valFormatted = normalizeDate(val);
|
|
const valFormatted = normalizeDate(val);
|
|
|
|
|
|
|
|
const blocksOfDate = blockedDays.value.filter(
|
|
const blocksOfDate = blockedDays.value.filter(
|
|
@@ -235,14 +298,17 @@ const onAddDateSelected = (val) => {
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
const dayOfWeek = new Date(`${valFormatted}T12:00:00`).getDay();
|
|
const dayOfWeek = new Date(`${valFormatted}T12:00:00`).getDay();
|
|
|
|
|
+
|
|
|
const dayPeriods = workingDays.value
|
|
const dayPeriods = workingDays.value
|
|
|
.filter(wd => wd.day === dayOfWeek)
|
|
.filter(wd => wd.day === dayOfWeek)
|
|
|
.map(wd => wd.period);
|
|
.map(wd => wd.period);
|
|
|
|
|
|
|
|
const workingDayBlocks = [];
|
|
const workingDayBlocks = [];
|
|
|
|
|
+
|
|
|
if (!dayPeriods.includes('afternoon')) {
|
|
if (!dayPeriods.includes('afternoon')) {
|
|
|
workingDayBlocks.push({ init_hour: '14:00:00', end_hour: '20:00:00' });
|
|
workingDayBlocks.push({ init_hour: '14:00:00', end_hour: '20:00:00' });
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
if (!dayPeriods.includes('morning')) {
|
|
if (!dayPeriods.includes('morning')) {
|
|
|
workingDayBlocks.push({ init_hour: '7:00:00', end_hour: '13:00:00' });
|
|
workingDayBlocks.push({ init_hour: '7:00:00', end_hour: '13:00:00' });
|
|
|
}
|
|
}
|
|
@@ -258,85 +324,94 @@ const onAddDateSelected = (val) => {
|
|
|
.filter((schedule) => schedule.date === valFormatted)
|
|
.filter((schedule) => schedule.date === valFormatted)
|
|
|
.map((schedule) => ({
|
|
.map((schedule) => ({
|
|
|
init_hour: schedule.start_time,
|
|
init_hour: schedule.start_time,
|
|
|
- end_hour: schedule.end_time,
|
|
|
|
|
|
|
+ end_hour: schedule.end_time,
|
|
|
|
|
+ }));
|
|
|
|
|
+
|
|
|
|
|
+ const cartBookingBlocks = scheduleCart.items
|
|
|
|
|
+ .filter((item) =>
|
|
|
|
|
+ Number(item.provider_id) === Number(props.provider.provider_id) &&
|
|
|
|
|
+ normalizeDate(item.date) === valFormatted
|
|
|
|
|
+ )
|
|
|
|
|
+ .map((item) => ({
|
|
|
|
|
+ init_hour: item.start_time,
|
|
|
|
|
+ end_hour: item.end_time,
|
|
|
}));
|
|
}));
|
|
|
|
|
|
|
|
- const partialBlocks = [...blocksOfDate, ...workingDayBlocks, ...existingBookingBlocks, ...serverBookingBlocks];
|
|
|
|
|
|
|
+ const partialBlocks = [
|
|
|
|
|
+ ...blocksOfDate,
|
|
|
|
|
+ ...workingDayBlocks,
|
|
|
|
|
+ ...existingBookingBlocks,
|
|
|
|
|
+ ...serverBookingBlocks,
|
|
|
|
|
+ ...cartBookingBlocks,
|
|
|
|
|
+ ];
|
|
|
|
|
|
|
|
$q.dialog({
|
|
$q.dialog({
|
|
|
- component: ServiceSelectionSheet,
|
|
|
|
|
- componentProps: { provider: props.provider, selectedDate: val, partialBlocks },
|
|
|
|
|
|
|
+ component: ServiceSelectionSheet, componentProps: { provider: props.provider, selectedDate: val, partialBlocks },
|
|
|
}).onOk(({ serviceType, date: date_, provider: prov }) => {
|
|
}).onOk(({ serviceType, date: date_, provider: prov }) => {
|
|
|
$q.dialog({
|
|
$q.dialog({
|
|
|
- component: ServiceTimeSelectionDialog,
|
|
|
|
|
- componentProps: { serviceType, selectedDate: date_, provider: prov, partialBlocks },
|
|
|
|
|
|
|
+ component: ServiceTimeSelectionDialog, componentProps: { serviceType, selectedDate: date_, provider: prov, partialBlocks },
|
|
|
}).onOk((booking) => {
|
|
}).onOk((booking) => {
|
|
|
if (wouldExceedWeekLimit(booking.date)) {
|
|
if (wouldExceedWeekLimit(booking.date)) {
|
|
|
$q.notify({ type: 'negative', message: t('scheduling_page.order_summary.week_limit_error') });
|
|
$q.notify({ type: 'negative', message: t('scheduling_page.order_summary.week_limit_error') });
|
|
|
|
|
+
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
bookings.value.push(booking);
|
|
bookings.value.push(booking);
|
|
|
|
|
+
|
|
|
showCalendar.value = false;
|
|
showCalendar.value = false;
|
|
|
});
|
|
});
|
|
|
});
|
|
});
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-const confirmRemove = (idx) => {
|
|
|
|
|
- $q.dialog({
|
|
|
|
|
- title: t('scheduling_page.order_summary.remove_confirm_title'),
|
|
|
|
|
- cancel: { label: t('scheduling_page.order_summary.remove_confirm_cancel'), flat: true, color: 'grey-6' },
|
|
|
|
|
- ok: { label: t('scheduling_page.order_summary.remove_confirm_ok'), unelevated: true, color: 'primary', rounded: true, noCaps: true },
|
|
|
|
|
- persistent: true,
|
|
|
|
|
- }).onOk(() => {
|
|
|
|
|
- bookings.value.splice(idx, 1);
|
|
|
|
|
- });
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const formatDate = (dateStr) => {
|
|
|
|
|
- const d = new Date(normalizeDate(dateStr) + 'T12:00:00');
|
|
|
|
|
- const localeMap = { pt: 'pt-BR', en: 'en-US', es: 'es-ES' };
|
|
|
|
|
- const loc = localeMap[locale.value] ?? 'pt-BR';
|
|
|
|
|
- const weekday = new Intl.DateTimeFormat(loc, { weekday: 'long' }).format(d);
|
|
|
|
|
- const dateFormatted = new Intl.DateTimeFormat(loc, { day: '2-digit', month: '2-digit', year: 'numeric' }).format(d);
|
|
|
|
|
- return `${weekday.charAt(0).toUpperCase() + weekday.slice(1)}, ${dateFormatted}`;
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
const submitOrder = async () => {
|
|
const submitOrder = async () => {
|
|
|
if (!primaryAddress.value) {
|
|
if (!primaryAddress.value) {
|
|
|
$q.notify({ type: 'warning', message: t('scheduling_page.order_summary.no_primary_address') });
|
|
$q.notify({ type: 'warning', message: t('scheduling_page.order_summary.no_primary_address') });
|
|
|
|
|
+
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- const payload = {
|
|
|
|
|
|
|
+ const payloads = bookings.value.map(b => ({
|
|
|
client_id: store.user.client_id,
|
|
client_id: store.user.client_id,
|
|
|
provider_id: props.provider.provider_id,
|
|
provider_id: props.provider.provider_id,
|
|
|
address_id: primaryAddress.value.id,
|
|
address_id: primaryAddress.value.id,
|
|
|
schedule_type: 'default',
|
|
schedule_type: 'default',
|
|
|
- schedules: bookings.value.map(b => ({
|
|
|
|
|
- date: normalizeDate(b.date),
|
|
|
|
|
- period_type: b.serviceType.hoursCount,
|
|
|
|
|
- start_time: formatHour(b.slot.startHour),
|
|
|
|
|
- end_time: formatHour(b.slot.endHour),
|
|
|
|
|
- total_amount: b.serviceType.price,
|
|
|
|
|
- offers_meal: b.meal === 'offer' ? true : b.meal === 'no_offer' ? false : null,
|
|
|
|
|
- })),
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ date: normalizeDate(b.date),
|
|
|
|
|
+ period_type: b.serviceType.hoursCount,
|
|
|
|
|
+ start_time: formatHour(b.slot.startHour),
|
|
|
|
|
+ end_time: formatHour(b.slot.endHour),
|
|
|
|
|
+ total_amount: b.serviceType.price,
|
|
|
|
|
+ offers_meal: b.meal === 'offer' ? true : b.meal === 'no_offer' ? false : null,
|
|
|
|
|
+ }));
|
|
|
|
|
|
|
|
submitting.value = true;
|
|
submitting.value = true;
|
|
|
|
|
+
|
|
|
try {
|
|
try {
|
|
|
- await createSchedule(payload);
|
|
|
|
|
|
|
+ await Promise.all(payloads.map((payload) => createSchedule(payload)));
|
|
|
|
|
+
|
|
|
$q.notify({ type: 'positive', message: t('scheduling_page.order_summary.submit_success') });
|
|
$q.notify({ type: 'positive', message: t('scheduling_page.order_summary.submit_success') });
|
|
|
|
|
+
|
|
|
onDialogOK();
|
|
onDialogOK();
|
|
|
} catch (err) {
|
|
} catch (err) {
|
|
|
const msg = err?.response?.data?.message
|
|
const msg = err?.response?.data?.message
|
|
|
?? err?.message
|
|
?? err?.message
|
|
|
?? t('scheduling_page.order_summary.submit_error');
|
|
?? t('scheduling_page.order_summary.submit_error');
|
|
|
|
|
+
|
|
|
$q.notify({ type: 'negative', message: msg });
|
|
$q.notify({ type: 'negative', message: msg });
|
|
|
} finally {
|
|
} finally {
|
|
|
submitting.value = false;
|
|
submitting.value = false;
|
|
|
|
|
+
|
|
|
router.push({ name: 'DashboardPage' });
|
|
router.push({ name: 'DashboardPage' });
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
+
|
|
|
|
|
+const wouldExceedWeekLimit = (newDateStr) => {
|
|
|
|
|
+ const count = getServerWeekCount(newDateStr) + getLocalWeekCount(newDateStr) + getCartWeekCount(newDateStr);
|
|
|
|
|
+
|
|
|
|
|
+ return count >= 2;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+onMounted(() => Promise.all([loadAvailability(), loadPrimaryAddress()]));
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
<style scoped lang="scss">
|