|
|
@@ -129,10 +129,12 @@ import { ref, computed, onMounted } from 'vue';
|
|
|
import { useDialogPluginComponent, useQuasar } from 'quasar';
|
|
|
import { date } from 'quasar';
|
|
|
import { getProviderWorkingDays, getProviderBlockedDays } from 'src/api/providerAvailability';
|
|
|
+import { getClientProviderBlocks } from 'src/api/schedule';
|
|
|
import { getProviderReceivedReviews } from 'src/api/review';
|
|
|
+import { userStore } from 'src/stores/user';
|
|
|
import ServiceSelectionSheet from './ServiceSelectionSheet.vue';
|
|
|
import ServiceTimeSelectionDialog from './ServiceTimeSelectionDialog.vue';
|
|
|
-import OrderSummaryDialog from './OrderSummaryDialog.vue';
|
|
|
+import OrderSummaryDialog from './OrderSummaryDialog.vue'
|
|
|
|
|
|
const props = defineProps({
|
|
|
provider: {
|
|
|
@@ -145,108 +147,206 @@ defineEmits([...useDialogPluginComponent.emits]);
|
|
|
|
|
|
const { dialogRef } = useDialogPluginComponent();
|
|
|
const $q = useQuasar();
|
|
|
+const store = userStore();
|
|
|
+
|
|
|
+const selectedDate = ref(null);
|
|
|
+const workingDays = ref([]);
|
|
|
+const blockedDays = ref([]);
|
|
|
+const loadingAvailability = ref(true);
|
|
|
+const reviews = ref([]);
|
|
|
+const loadingReviews = ref(true);
|
|
|
+
|
|
|
+
|
|
|
+const bookings = ref([]);
|
|
|
+const providerClientBlocks = ref({
|
|
|
+ existing_schedules: [],
|
|
|
+ fully_blocked_weeks: [],
|
|
|
+});
|
|
|
+
|
|
|
+
|
|
|
+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 normalizeDate = (dateStr) => (dateStr ?? '').replace(/\//g, '-');
|
|
|
+
|
|
|
+const getWeekStart = (dateStr) => {
|
|
|
+ const normalizedDate = normalizeDate(dateStr);
|
|
|
+ const d = new Date(`${normalizedDate}T12:00:00`);
|
|
|
+ d.setDate(d.getDate() - d.getDay());
|
|
|
+ return d.toISOString().split('T')[0];
|
|
|
+};
|
|
|
+
|
|
|
+const blockedWeekStartSet = computed(() =>
|
|
|
+ new Set(providerClientBlocks.value.fully_blocked_weeks ?? [])
|
|
|
+);
|
|
|
+
|
|
|
+const getServerWeekCount = (selectedDate) => {
|
|
|
+ const weekStart = getWeekStart(selectedDate);
|
|
|
+ return (providerClientBlocks.value.existing_schedules ?? []).filter(
|
|
|
+ (schedule) => getWeekStart(schedule.date) === weekStart
|
|
|
+ ).length;
|
|
|
+};
|
|
|
+
|
|
|
+const getLocalWeekCount = (selectedDate) => {
|
|
|
+ const weekStart = getWeekStart(selectedDate);
|
|
|
+ return bookings.value.filter((booking) => getWeekStart(booking.date) === weekStart).length;
|
|
|
+};
|
|
|
+
|
|
|
+const wouldExceedWeekLimit = (selectedDate) => {
|
|
|
+ const serverCount = getServerWeekCount(selectedDate);
|
|
|
+ const localCount = getLocalWeekCount(selectedDate);
|
|
|
+ return (serverCount + localCount) >= 2;
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+const dateOptions = (d) => {
|
|
|
+ const today = date.formatDate(new Date(), 'YYYY/MM/DD');
|
|
|
+ if (d < today) return false;
|
|
|
+
|
|
|
+ const raw = normalizeDate(d);
|
|
|
+ const parsed = new Date(`${raw}T12:00:00`);
|
|
|
+ const dayOfWeek = parsed.getDay();
|
|
|
+
|
|
|
+ const isWorkingDay = availableWeekDays.value.includes(dayOfWeek);
|
|
|
+ const isBlocked = blockedDateSet.value.has(raw);
|
|
|
+ const isWeekBlocked = blockedWeekStartSet.value.has(getWeekStart(raw));
|
|
|
+
|
|
|
+ if (!isWorkingDay || isBlocked || isWeekBlocked) return false;
|
|
|
+
|
|
|
+ if (wouldExceedWeekLimit(raw)) return false;
|
|
|
+
|
|
|
+ return true;
|
|
|
+};
|
|
|
+
|
|
|
|
|
|
const onDateSelected = (val) => {
|
|
|
if (!val) return;
|
|
|
+
|
|
|
selectedDate.value = null;
|
|
|
- const valFormatted = val.replace(/\//g, '-');
|
|
|
+ const valFormatted = normalizeDate(val);
|
|
|
+
|
|
|
|
|
|
const blocksOfDate = blockedDays.value.filter(
|
|
|
(bd) => bd.date === valFormatted && bd.period !== 'all'
|
|
|
);
|
|
|
|
|
|
+
|
|
|
const dayOfWeek = new Date(`${valFormatted}T12:00:00`).getDay();
|
|
|
+
|
|
|
const dayPeriods = workingDays.value
|
|
|
.filter((wd) => wd.day === dayOfWeek)
|
|
|
.map((wd) => wd.period);
|
|
|
|
|
|
const workingDayBlocks = [];
|
|
|
+
|
|
|
if (!dayPeriods.includes('afternoon')) {
|
|
|
workingDayBlocks.push({ init_hour: '14:00:00', end_hour: '20:00:00' });
|
|
|
}
|
|
|
+
|
|
|
if (!dayPeriods.includes('morning')) {
|
|
|
- workingDayBlocks.push({ init_hour: '7:00:00', end_hour: '13:00:00' });
|
|
|
+ workingDayBlocks.push({ init_hour: '07:00:00', end_hour: '13:00:00' });
|
|
|
}
|
|
|
|
|
|
- const partialBlocks = [...blocksOfDate, ...workingDayBlocks];
|
|
|
+
|
|
|
+ const localBookingBlocks = bookings.value
|
|
|
+ .filter(b => b.date.replace(/\//g, '-') === valFormatted)
|
|
|
+ .map(b => ({
|
|
|
+ init_hour: `${b.slot.startHour}:00:00`,
|
|
|
+ end_hour: `${b.slot.endHour}:00:00`
|
|
|
+ }));
|
|
|
+
|
|
|
+ const serverBookingBlocks = (providerClientBlocks.value.existing_schedules ?? [])
|
|
|
+ .filter((schedule) => normalizeDate(schedule.date) === valFormatted)
|
|
|
+ .map((schedule) => ({
|
|
|
+ init_hour: schedule.start_time,
|
|
|
+ end_hour: schedule.end_time,
|
|
|
+ }));
|
|
|
+
|
|
|
+
|
|
|
+ const partialBlocks = [
|
|
|
+ ...blocksOfDate,
|
|
|
+ ...workingDayBlocks,
|
|
|
+ ...localBookingBlocks,
|
|
|
+ ...serverBookingBlocks,
|
|
|
+ ];
|
|
|
+
|
|
|
+ // fluxo
|
|
|
+ $q.dialog({
|
|
|
+ component: ServiceSelectionSheet,
|
|
|
+ componentProps: {
|
|
|
+ provider: props.provider,
|
|
|
+ selectedDate: val,
|
|
|
+ partialBlocks
|
|
|
+ },
|
|
|
+}).onOk(({ serviceType, date: date_, provider: prov }) => {
|
|
|
|
|
|
$q.dialog({
|
|
|
- component: ServiceSelectionSheet,
|
|
|
- componentProps: { provider: props.provider, selectedDate: val, partialBlocks },
|
|
|
- }).onOk(({ serviceType, date: date_, provider: prov }) => {
|
|
|
- $q.dialog({
|
|
|
- component: ServiceTimeSelectionDialog,
|
|
|
- componentProps: { serviceType, selectedDate: date_, provider: prov, partialBlocks },
|
|
|
- }).onOk((booking) => {
|
|
|
- $q.dialog({
|
|
|
- component: OrderSummaryDialog,
|
|
|
- componentProps: { provider: props.provider, initialBooking: booking },
|
|
|
- });
|
|
|
- });
|
|
|
- });
|
|
|
-};
|
|
|
+ component: ServiceTimeSelectionDialog,
|
|
|
+ componentProps: {
|
|
|
+ serviceType,
|
|
|
+ selectedDate: date_,
|
|
|
+ provider: prov,
|
|
|
+ partialBlocks
|
|
|
+ },
|
|
|
+ }).onOk((booking) => {
|
|
|
|
|
|
-const selectedDate = ref(null);
|
|
|
-const workingDays = ref([]);
|
|
|
-const blockedDays = ref([]);
|
|
|
-const loadingAvailability = ref(true);
|
|
|
+ bookings.value.push(booking);
|
|
|
|
|
|
-const reviews = ref([]);
|
|
|
-const loadingReviews = ref(true);
|
|
|
+ $q.dialog({
|
|
|
+ component: OrderSummaryDialog,
|
|
|
+ componentProps: {
|
|
|
+ provider: props.provider,
|
|
|
+ initialBooking: booking
|
|
|
+ }
|
|
|
+ });
|
|
|
|
|
|
-const avatarColors = [
|
|
|
- { background: '#ffd5df', color: '#932e57' },
|
|
|
- { background: '#d7e8ff', color: '#2158a8' },
|
|
|
- { background: '#dfd', color: '#2a7a3b' },
|
|
|
- { background: '#ffe5cc', color: '#8a4500' },
|
|
|
-];
|
|
|
+ });
|
|
|
|
|
|
-const avatarStyle = computed(() => {
|
|
|
- const idx = (props.provider?.provider_id ?? 0) % avatarColors.length;
|
|
|
- return avatarColors[idx];
|
|
|
});
|
|
|
-
|
|
|
-const clientAvatarStyle = (review) => {
|
|
|
- const idx = (review.id ?? 0) % avatarColors.length;
|
|
|
- return avatarColors[idx];
|
|
|
};
|
|
|
|
|
|
-const availableWeekDays = computed(() =>
|
|
|
- [...new Set(workingDays.value.map((wd) => wd.day))]
|
|
|
-);
|
|
|
-
|
|
|
-const blockedDateSet = computed(() =>
|
|
|
- new Set(blockedDays.value.map((bd) => bd.date && bd.period == 'all'))
|
|
|
-);
|
|
|
-
|
|
|
-const dateOptions = (d) => {
|
|
|
- const today = date.formatDate(new Date(), 'YYYY/MM/DD');
|
|
|
- if (d < today) return false;
|
|
|
- const raw = d.replace(/\//g, '-');
|
|
|
- const parsed = new Date(`${raw}T12:00:00`);
|
|
|
- const dayOfWeek = parsed.getDay();
|
|
|
- const isWorkingDay = availableWeekDays.value.includes(dayOfWeek);
|
|
|
- const isBlocked = blockedDateSet.value.has(raw);
|
|
|
- return isWorkingDay && !isBlocked;
|
|
|
-};
|
|
|
|
|
|
const loadAvailability = async () => {
|
|
|
loadingAvailability.value = true;
|
|
|
+
|
|
|
try {
|
|
|
- const [wd, bd] = await Promise.all([
|
|
|
+ const clientId = store.user?.client?.id;
|
|
|
+ const defaultClientBlocks = { existing_schedules: [], fully_blocked_weeks: [] };
|
|
|
+
|
|
|
+ const [wd, bd, clientBlocks] = await Promise.all([
|
|
|
getProviderWorkingDays(props.provider.provider_id),
|
|
|
getProviderBlockedDays(props.provider.provider_id),
|
|
|
+ clientId
|
|
|
+ ? getClientProviderBlocks(clientId, props.provider.provider_id)
|
|
|
+ : Promise.resolve(defaultClientBlocks),
|
|
|
]);
|
|
|
+
|
|
|
workingDays.value = wd ?? [];
|
|
|
blockedDays.value = bd ?? [];
|
|
|
+ providerClientBlocks.value = clientBlocks ?? defaultClientBlocks;
|
|
|
+
|
|
|
} catch {
|
|
|
workingDays.value = [];
|
|
|
blockedDays.value = [];
|
|
|
+ providerClientBlocks.value = { existing_schedules: [], fully_blocked_weeks: [] };
|
|
|
+
|
|
|
} finally {
|
|
|
loadingAvailability.value = false;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+
|
|
|
const loadReviews = async () => {
|
|
|
loadingReviews.value = true;
|
|
|
try {
|
|
|
@@ -260,7 +360,8 @@ const loadReviews = async () => {
|
|
|
};
|
|
|
|
|
|
onMounted(() => {
|
|
|
- Promise.all([loadAvailability(), loadReviews()]);
|
|
|
+ loadAvailability();
|
|
|
+ loadReviews();
|
|
|
});
|
|
|
</script>
|
|
|
|