Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/updates #85

Merged
merged 5 commits into from
Apr 2, 2025
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: pet update, routing refactor
  • Loading branch information
BastianHolmes committed Apr 1, 2025
commit 44e91534b667de6df552647f174fc7a8f3a46632
22 changes: 22 additions & 0 deletions src/modules/Booking/api/queries.ts
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ import { EQueryKeys } from "./keys"
import {
checkRoomAvailableInDates,
findAllBookingsInDates,
findBlockingBookingsForRoomInDates,
getBookingById,
} from "@/generated/bookings"
import {
@@ -64,3 +65,24 @@ export const useGetBookings = ({
checkOutDate: convertServerDateToAnFormView(checkOutDate),
})),
})

export const useBlockingBookingsForRoomInDates = (
roomId: number,
checkInDate: string,
checkOutDate: string
) =>
useQuery({
queryKey: [EQueryKeys.GET_BOOKING_BY_ID + roomId],
queryFn: () =>
findBlockingBookingsForRoomInDates(
roomId,
{
checkInDate,
checkOutDate,
},
{
headers: { "X-PetHotel-User-Id": 1 },
}
),
enabled: !!roomId && !!checkInDate && !!checkOutDate,
})
Original file line number Diff line number Diff line change
@@ -12,8 +12,9 @@ import {
} from "@/modules/Booking/model/types/BookingValidationSchema"
import { useGetAllRooms } from "@/modules/Rooms/api/queries"
import { useGetCategories } from "@/modules/Categories/api/queries"
import { useMemo } from "react"
import { useEffect, useMemo } from "react"
import useBookingStore from "@/modules/Booking/store/BookingStore"
import { RoomDto } from "@/generated/bookings"

interface CategoryRoomsProps {
form: UseFormReturn<ICategoryAndRoom>
@@ -29,19 +30,24 @@ export const CategoryRoomsForm = (props: CategoryRoomsProps) => {
} = form

const setBookingData = useBookingStore(state => state.setBookingData)
const setRoom = useBookingStore(state => state.setRoom)

const { data: rooms, isError: isErrorRooms } = useGetAllRooms("booking")
const { data: categories, isError: isErrorCategories } = useGetCategories()
const { categories: categoryValue } = useWatch({ control })
const { categories: categoryValue, rooms: roomValue } = useWatch({ control })

const filteredRooms = useMemo(() => {
resetField("rooms")
setBookingData({ ...bookingData, rooms: "" })
const filtered = rooms?.filter(
room => room.categoryDto?.name === categoryValue
)

return filtered
}, [rooms, categoryValue, resetField])
}, [rooms, categoryValue])

useEffect(() => {
const curRoom = rooms?.find(room => room.number === roomValue) as RoomDto
if (curRoom) setRoom(curRoom)
}, [roomValue, rooms, setRoom])

return (
<section className="flex justify-between">
@@ -52,7 +58,11 @@ export const CategoryRoomsForm = (props: CategoryRoomsProps) => {
return (
<CategorySelect
className="w-64"
onChange={field.onChange}
onChange={args => {
resetField("rooms")
setBookingData({ ...bookingData, rooms: "" })
return field.onChange(args)
}}
value={field.value || bookingData.categories || ""}
error={errors?.categories?.message}
categories={categories || []}
@@ -71,7 +81,14 @@ export const CategoryRoomsForm = (props: CategoryRoomsProps) => {
return (
<RoomSelect
className="w-64"
onChange={field.onChange}
onChange={args => {
const curRoom = rooms?.find(
room => room.number === field.value
) as RoomDto
if (curRoom) setRoom(curRoom)
console.log(curRoom, rooms, field.value)
return field.onChange(args)
}}
value={field.value || bookingData.rooms || ""}
error={errors?.rooms?.message}
rooms={filteredRooms || []}
Original file line number Diff line number Diff line change
@@ -41,6 +41,7 @@ export const PetOwnerForm = (props: IPetOwnerFormProps) => {
const owner = storeOwner || client || null
const petIds = watch("petIds") ?? []

console.log(values, "values")
const availablePets =
owner?.petsDto?.filter(pet => !petIds.includes(pet.id ?? 0)) ?? []

@@ -57,7 +58,6 @@ export const PetOwnerForm = (props: IPetOwnerFormProps) => {
const updatedPetIds = petIds.filter(id => id !== petId)
setBookingData({ ...bookingData, ...values, petIds: updatedPetIds })
setValue("petIds", updatedPetIds)
console.log(bookingData)
}

const handleChooseClient = (value: OwnerDto) => {
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ import { CategoryDto } from "@/generated/bookings"

type TProps = {
onChange: (value: string) => void
onChangeCategory?: () => void
value: string
error?: string
className: string
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ type TProps = {
}

export const BookingModal: React.FC<TProps> = memo(props => {
const { children, onSubmit, isDirty, isReadyToSubmit } = props
const { children, onSubmit, isDirty, isReadyToSubmit = true } = props
const navigate = useNavigate()
const confirmationNotification = addConfirmationNotification()

@@ -132,7 +132,7 @@ export const BookingModal: React.FC<TProps> = memo(props => {
size={EButtonSize.Large}
fontSize={16}
fontWeight={700}
disabled={isReadyToSubmit}
disabled={!isReadyToSubmit}
>
{btnText[bookingStep - 1]}
</Button>
26 changes: 25 additions & 1 deletion src/modules/Booking/components/steps/BookingScreen3.tsx
Original file line number Diff line number Diff line change
@@ -10,9 +10,19 @@ import useBookingStore from "../../store/BookingStore"
import { CategoryRoomsForm } from "../form/blocks/CategoryForm/CategoryRoomsForm"
import { ScheduleForm } from "../form/blocks/ScheduleForm/ScheduleForm"
import { StepTitle } from "../typography/StepTitle/StepTitle"
import { useGetIsDatesAvailable } from "../../api/queries"
import ErrorBar from "@/shared/ui/ErrorBar/ErrorBar"
import { ErrorMessages } from "../../consts/errors"

const BookingScreen3 = () => {
const bookingData = useBookingStore(state => state.bookingData)
const room = useBookingStore(state => state.room)

const { failureReason } = useGetIsDatesAvailable(
Number(room?.id),
bookingData.dateFrom!,
bookingData.dateTo!
)

const form = useForm({
resolver: yupResolver(ScreenSchema3),
@@ -26,9 +36,14 @@ const BookingScreen3 = () => {
daysAmount: bookingData.daysAmount || 0,
},
})
const disabled = failureReason?.response?.data?.message

return (
<BookingModal isDirty={true} onSubmit={form.handleSubmit}>
<BookingModal
isDirty={true}
onSubmit={form.handleSubmit}
isReadyToSubmit={disabled}
>
<StepTitle title="Шаг 2: Категория и комната" />
<CategoryRoomsForm
form={form as unknown as UseFormReturn<ICategoryAndRoom>}
@@ -38,6 +53,15 @@ const BookingScreen3 = () => {
bookingData={bookingData}
form={form as unknown as UseFormReturn<IScheduleForm>}
/>
{disabled && (
<div className="py-4">
<ErrorBar
body={ErrorMessages.NO_ROOMS}
style="Red"
title={ErrorMessages.NO_ROOMS_TITLE}
/>
</div>
)}
</BookingModal>
)
}
20 changes: 18 additions & 2 deletions src/modules/Booking/components/steps/BookingScreen4.tsx
Original file line number Diff line number Diff line change
@@ -5,17 +5,20 @@ import {
ICategoryAndRoom,
IPet,
IScheduleForm,
ScreenSchema3,
ScreenSchema3WithPets,
} from "../../model/types/BookingValidationSchema"
import useBookingStore from "../../store/BookingStore"
import { PetOwnerForm } from "../form/blocks/PetOwnerForm/PetOwnerForm"
import { CategoryRoomsForm } from "../form/blocks/CategoryForm/CategoryRoomsForm"
import { ScheduleForm } from "../form/blocks/ScheduleForm/ScheduleForm"
import ErrorBar from "@/shared/ui/ErrorBar/ErrorBar"
import { ErrorMessages } from "../../consts/errors"

const BookingScreen4 = () => {
const bookingData = useBookingStore(state => state.bookingData)

const form = useForm({
resolver: yupResolver(ScreenSchema3),
resolver: yupResolver(ScreenSchema3WithPets),
defaultValues: {
categories: bookingData.categories || "",
rooms: bookingData.rooms || "",
@@ -24,12 +27,25 @@ const BookingScreen4 = () => {
timeFrom: bookingData.timeFrom || "",
timeTo: bookingData.timeTo || "",
daysAmount: bookingData.daysAmount || 0,
petIds: bookingData.petIds || [],
},
mode: "onSubmit",
})

const noPets = Boolean(form.formState.errors.petIds?.message)

return (
<BookingModal isDirty={true} onSubmit={form.handleSubmit}>
<div className="flex flex-col">
{noPets && (
<div className="py-4">
<ErrorBar
title={ErrorMessages.NO_PETS_TITLE}
body={ErrorMessages.NO_PETS}
style="Red"
/>
</div>
)}
<CategoryRoomsForm
bookingData={bookingData}
form={form as unknown as UseFormReturn<ICategoryAndRoom>}
7 changes: 7 additions & 0 deletions src/modules/Booking/consts/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const ErrorMessages = {
NO_ROOMS_TITLE: "Нет свободных комнат",
NO_ROOMS: "Комната недоступна. Выберите другие даты или категорию комнаты.",
NO_CATEGORIES: "Нет доступных категорий",
NO_PETS: "Переход на следующий шаг невозможен без добавленного питомца",
NO_PETS_TITLE: "Добавьте или создайте хотя бы одного питомца",
}
18 changes: 14 additions & 4 deletions src/modules/Booking/model/types/BookingValidationSchema.ts
Original file line number Diff line number Diff line change
@@ -15,6 +15,10 @@ export interface IScheduleForm {
daysAmount?: number
}

export interface IPet {
petIds: number[]
}

export interface IOwnerForm {
owner: OwnerShortDto
pets: PetDto[]
@@ -35,10 +39,6 @@ export interface IComment {
comment?: string
}

export interface IPet {
petIds: number[]
}

export interface IBookingForm
extends ICategoryAndRoom,
IScheduleForm,
@@ -91,8 +91,18 @@ export const ScreenSchema2 = yup.object<ICategoryAndRoom>().shape({
rooms: yup.string().required("Пожалуйста выберите комнату"),
})

export const PetSchema = yup.object<IPet>().shape({
petIds: yup
.array()
.of(yup.number())
.min(1)
.required("Пожалуйста выберите животных"),
})

export const ScreenSchema3 = ScreenSchema1.concat(ScreenSchema2)

export const ScreenSchema3WithPets = ScreenSchema3.concat(PetSchema)

export const ScreenSchema4 = yup.object<IBookingForm>().shape({
pricePerDay: yup
.number()
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Button, EButtonSize, EButtonVariant } from "@/shared/ui/Button/Button"
import { useNavigate } from "react-router-dom"

export const BookingFooter = () => {
const navigate = useNavigate()
return (
<>
<Button
variant={EButtonVariant.Secondary}
size={EButtonSize.Large}
fontSize={16}
fontWeight={700}
onClick={() => navigate("/")}
>
Отмена
</Button>
<Button
form="booking"
type="submit"
variant={EButtonVariant.Primary}
size={EButtonSize.Large}
fontSize={16}
fontWeight={700}
>
Сохранить
</Button>
</>
)
}
Original file line number Diff line number Diff line change
@@ -53,7 +53,7 @@ const defaultValues = {
export const CreateBookingPage = () => {
const data = useBookingStore(state => state.bookingData)
const setBookingData = useBookingStore(state => state.setBookingData)
const { mutate: createBooking, isSuccess, error } = useCreateBooking()
const { mutate: createBooking, isSuccess, error, status } = useCreateBooking()
const navigate = useNavigate()
const notificateError = addErrorNotification()
const notificateSuccess = addSuccessNotification()
@@ -65,15 +65,19 @@ export const CreateBookingPage = () => {
mode: "all",
})

const onSubmit = (bookingData: IBookingForm) => {
const onSubmit = async (bookingData: IBookingForm) => {
const data = mapperBookingFormDataToDTO(bookingData)
createBooking(data)
await createBooking(data)
console.log(status)
if (isSuccess) {
console.log(isSuccess, "Успех??")
notificateSuccess("Бронирование успешно создано")
setBookingData(defaultValues)
navigate("/bookings")
} else {
notificateError(error.response.data.message)
notificateError(
error.response.data.message || "Произошла ошибка, попробуйте ещё раз"
)
}
}

5 changes: 5 additions & 0 deletions src/modules/Booking/store/BookingStore.ts
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ import { create } from "zustand"
import { IBookingForm } from "../model/types/BookingValidationSchema"
import { DeepPartial } from "react-hook-form"
import { OwnerDto } from "@/generated/owners"
import { RoomDto } from "@/generated/bookings"

interface IBookingStore {
isCreateShortPet: boolean
@@ -10,12 +11,14 @@ interface IBookingStore {
bookingStep: number
bookingData: DeepPartial<IBookingForm>
owner: OwnerDto | null
room: RoomDto | null
setIsCreateShortClient: (value: boolean) => void
setIsBookingInProgress: (value: boolean) => void
setBookingStep: (value: number) => void
setSpecificBookingData: (value: string, setter: string) => void
setBookingData: (value: DeepPartial<IBookingForm>) => void
setOwner: (value: OwnerDto) => void
setRoom: (value: RoomDto) => void
setIsCreateShortPet: (value: boolean) => void
}

@@ -25,6 +28,7 @@ const useBookingStore = create<IBookingStore>(set => ({
isBookingInProgress: false,
bookingStep: 1,
owner: null,
room: null,
bookingData: {
dateFrom: "",
dateTo: "",
@@ -44,6 +48,7 @@ const useBookingStore = create<IBookingStore>(set => ({
setIsCreateShortPet: value => set({ isCreateShortPet: value }),
setIsBookingInProgress: value => set({ isBookingInProgress: value }),
setOwner: value => set({ owner: value }),
setRoom: value => set({ room: value }),
setBookingStep: value => set({ bookingStep: value }),
setSpecificBookingData: (value, setter) =>
set(state => ({
Loading