diff --git a/backend/src/packages/orders/libs/types/types.ts b/backend/src/packages/orders/libs/types/types.ts index 1fc88bb09..0828b5ca8 100644 --- a/backend/src/packages/orders/libs/types/types.ts +++ b/backend/src/packages/orders/libs/types/types.ts @@ -14,6 +14,7 @@ export { type OrderFindByIdResponseDto, type OrderQueryParameters, type OrderResponseDto, + type OrderResponseWithAvatarDto, type OrdersListResponseDto, type OrderStatusValues, type OrderUpdateAcceptStatusRequestDto, diff --git a/backend/src/packages/orders/order.repository.ts b/backend/src/packages/orders/order.repository.ts index 087a6ab18..92c63ae9a 100644 --- a/backend/src/packages/orders/order.repository.ts +++ b/backend/src/packages/orders/order.repository.ts @@ -5,6 +5,7 @@ import { type IRepository } from '~/libs/interfaces/interfaces.js'; import { type IDatabase } from '~/libs/packages/database/database.js'; import { type DatabaseSchema } from '~/libs/packages/database/schema/schema.js'; +import { type FileEntityT } from '../files/libs/types/types.js'; import { type UserEntityT } from '../users/users.js'; import { combineFilters } from './libs/helpers/combine-filters.js'; import { @@ -26,6 +27,8 @@ class OrderRepository implements Omit { private driversSchema: DatabaseSchema['drivers']; + private filesSchema: DatabaseSchema['files']; + public constructor( database: Pick, { @@ -34,9 +37,10 @@ class OrderRepository implements Omit { users, shifts, drivers, + files, }: Pick< DatabaseSchema, - 'orders' | 'users' | 'trucks' | 'shifts' | 'drivers' + 'orders' | 'users' | 'trucks' | 'shifts' | 'drivers' | 'files' >, ) { this.db = database; @@ -45,9 +49,12 @@ class OrderRepository implements Omit { this.usersSchema = users; this.shiftsSchema = shifts; this.driversSchema = drivers; + this.filesSchema = files; } - public async findById(id: OrderEntityT['id']): Promise { + public async findById( + id: OrderEntityT['id'], + ): Promise<{ order: OrderEntityT | null; avatarFile: FileEntityT | null }> { const [order = null] = await this.db .driver() .select({ @@ -71,6 +78,14 @@ class OrderRepository implements Omit { phone: this.usersSchema.phone, driverLicenseNumber: this.driversSchema.driverLicenseNumber, }, + avatar: { + id: this.filesSchema.id, + key: this.filesSchema.key, + name: this.filesSchema.name, + contentType: this.filesSchema.contentType, + createdAt: this.filesSchema.createdAt, + updatedAt: this.filesSchema.updatedAt, + }, truck: { id: this.shiftsSchema.truckId, licensePlateNumber: this.trucksSchema.licensePlateNumber, @@ -89,13 +104,27 @@ class OrderRepository implements Omit { this.driversSchema, eq(this.driversSchema.userId, this.shiftsSchema.driverId), ) + .leftJoin( + this.filesSchema, + eq(this.driversSchema.avatarId, this.filesSchema.id), + ) .innerJoin( this.trucksSchema, eq(this.trucksSchema.id, this.shiftsSchema.truckId), ) .where(eq(this.ordersSchema.id, id)); - return order; + let result = null; + let avatarFile = null; + + if (order) { + const { avatar, ...pureOrder } = order; + avatarFile = avatar; + result = pureOrder as OrderEntityT; + result.driver = result.driver === null ? null : { ...pureOrder.driver }; + } + + return { order: result, avatarFile }; } public async findAllBusinessOrders( diff --git a/backend/src/packages/orders/order.service.ts b/backend/src/packages/orders/order.service.ts index 3f50b7802..ef7b23628 100644 --- a/backend/src/packages/orders/order.service.ts +++ b/backend/src/packages/orders/order.service.ts @@ -5,6 +5,7 @@ import { type SocketService } from '~/libs/packages/socket/socket.service.js'; import { type BusinessService } from '../business/business.service.js'; import { type DriverService } from '../drivers/driver.service.js'; +import { getAvatarUrl } from '../drivers/libs/helpers/get-avatar-url.helper.js'; import { type MapService } from '../map/map.service.js'; import { type ShiftService } from '../shifts/shift.service.js'; import { type TruckService } from '../trucks/truck.service.js'; @@ -17,6 +18,7 @@ import { type OrderEntity as OrderEntityT, type OrderQueryParameters, type OrderResponseDto, + type OrderResponseWithAvatarDto, type OrdersListResponseDto, type OrderStatusValues, type OrderUpdateAcceptStatusRequestDto, @@ -168,8 +170,8 @@ class OrderService implements Omit { }: { id: OrderEntityT['id']; user: UserEntityObjectWithGroupT | null; - }): Promise { - const order = await this.orderRepository.findById(id); + }): Promise { + const { order, avatarFile } = await this.orderRepository.findById(id); if (!order) { throw new NotFoundError({ @@ -177,6 +179,21 @@ class OrderService implements Omit { }); } + const avatarUrl = getAvatarUrl(avatarFile); + const pureOrder = OrderEntity.initialize(order).toObject(); + const shift = pureOrder.shift; + const driver = + shift.driver === null ? null : { ...shift.driver, avatarUrl }; + + const newShift = { + ...shift, + driver, + }; + const orderWithDriverAvatar = { + ...pureOrder, + shift: newShift, + }; + if (user?.group.key === UserGroupKey.BUSINESS) { const business = await this.businessService.findByOwnerId(user.id); @@ -187,14 +204,14 @@ class OrderService implements Omit { } this.verifyOrderBelongsToBusiness(order, business.id); - return OrderEntity.initialize(order).toObject(); + return orderWithDriverAvatar; } if (user) { this.verifyOrderBelongsToUser(order, user); } - return OrderEntity.initialize(order).toObject(); + return orderWithDriverAvatar; } public async update(parameters: { @@ -210,7 +227,9 @@ class OrderService implements Omit { }); } - const foundOrder = await this.orderRepository.findById(parameters.id); + const { order: foundOrder } = await this.orderRepository.findById( + parameters.id, + ); if (!foundOrder?.driver) { throw new NotFoundError({ diff --git a/frontend/src/libs/hooks/use-app-map/use-app-map.hook.ts b/frontend/src/libs/hooks/use-app-map/use-app-map.hook.ts index 4aa6d2e43..276694265 100644 --- a/frontend/src/libs/hooks/use-app-map/use-app-map.hook.ts +++ b/frontend/src/libs/hooks/use-app-map/use-app-map.hook.ts @@ -22,7 +22,6 @@ type Properties = { onPriceChange?: (price: number) => void; mapReference: React.RefObject; shownRoute?: PlaceLatLng; - onMapLoad?: () => void; }; const useAppMap = ({ @@ -35,7 +34,6 @@ const useAppMap = ({ endAddress, mapReference, shownRoute, - onMapLoad, }: Properties): void => { const mapService = useRef(null); const dispatch = useAppDispatch(); @@ -57,7 +55,7 @@ const useAppMap = ({ } }; void configMap(); - }, [center, destination, mapReference, onMapLoad, points, zoom]); + }, [center, destination, mapReference, points, zoom]); useEffect(() => { if (mapService.current && points && points.length > 0) { diff --git a/frontend/src/libs/packages/map/map.package.ts b/frontend/src/libs/packages/map/map.package.ts index 81bd849df..79cb4f3d4 100644 --- a/frontend/src/libs/packages/map/map.package.ts +++ b/frontend/src/libs/packages/map/map.package.ts @@ -136,9 +136,7 @@ class MapService implements IMapService { private throwIfMapNotInitialized(): void { if (!this.map) { - throw new ApplicationError({ - message: 'Map is not initialized', - }); + return; } } diff --git a/frontend/src/packages/orders/libs/types/types.ts b/frontend/src/packages/orders/libs/types/types.ts index a49f17ab6..99189a69b 100644 --- a/frontend/src/packages/orders/libs/types/types.ts +++ b/frontend/src/packages/orders/libs/types/types.ts @@ -6,6 +6,7 @@ export { type OrderEntity, type OrderFindByIdResponseDto, type OrderResponseDto, + type OrderResponseWithAvatarDto, type OrderStatusValues, type OrderUpdateAcceptStatusRequestDto, type OrderUpdateAcceptStatusResponseDto, diff --git a/frontend/src/packages/orders/orders.ts b/frontend/src/packages/orders/orders.ts index 94572e6c9..56dab4ab7 100644 --- a/frontend/src/packages/orders/orders.ts +++ b/frontend/src/packages/orders/orders.ts @@ -10,6 +10,7 @@ export { type OrderCreateRequestDto, type OrderEntity, type OrderResponseDto, + type OrderResponseWithAvatarDto, type OrderStatusValues, } from './libs/types/types.js'; export { orderCreateForm } from './libs/validation-schemas/validation-schemas.js'; diff --git a/frontend/src/pages/driver-order/driver-order.tsx b/frontend/src/pages/driver-order/driver-order.tsx index 1859c0234..4349cd04b 100644 --- a/frontend/src/pages/driver-order/driver-order.tsx +++ b/frontend/src/pages/driver-order/driver-order.tsx @@ -31,7 +31,6 @@ const DriverOrder = (): JSX.Element => { center: order?.startPoint as google.maps.LatLngLiteral, destination: order?.endPoint as google.maps.LatLngLiteral, mapReference: mapReference, - onMapLoad: () => true, }); useSubscribeUpdates(`${orderId as string}`); diff --git a/frontend/src/pages/order-status/libs/hooks/use-get-order-data.hook.ts b/frontend/src/pages/order-status/libs/hooks/use-get-order-data.hook.ts index 87e6dfecc..a1effec94 100644 --- a/frontend/src/pages/order-status/libs/hooks/use-get-order-data.hook.ts +++ b/frontend/src/pages/order-status/libs/hooks/use-get-order-data.hook.ts @@ -1,19 +1,27 @@ -import { type OrderResponseDto } from '~/packages/orders/libs/types/types.js'; +import { + type OrderResponseDto, + type OrderResponseWithAvatarDto, +} from '~/packages/orders/libs/types/types.js'; const useGetOrderData = ( - order: OrderResponseDto | null, + order: (OrderResponseDto | OrderResponseWithAvatarDto) | null, ): { firstName: string; lastName: string; licensePlate: string; price: number | undefined; + avatarUrl: string | null; } => { const { shift, price } = order ?? {}; const { truck, driver } = shift ?? {}; const { licensePlateNumber: licensePlate = '' } = truck ?? {}; - const { firstName: firstName = '', lastName: lastName = '' } = driver ?? {}; + const { + firstName: firstName = '', + lastName: lastName = '', + avatarUrl: avatarUrl = null, + } = (driver as OrderResponseWithAvatarDto['shift']['driver']) ?? {}; - return { firstName, lastName, licensePlate, price }; + return { firstName, lastName, licensePlate, price, avatarUrl }; }; export { useGetOrderData }; diff --git a/frontend/src/pages/order-status/order-status.tsx b/frontend/src/pages/order-status/order-status.tsx index 2052afa23..868a37eaa 100644 --- a/frontend/src/pages/order-status/order-status.tsx +++ b/frontend/src/pages/order-status/order-status.tsx @@ -1,4 +1,4 @@ -import { OrderCard, Spinner } from '~/libs/components/components.js'; +import { OrderCard } from '~/libs/components/components.js'; import { OrderStatus } from '~/libs/components/orders-status/order-status.js'; import { AppRoute } from '~/libs/enums/app-route.enum.js'; import { DataStatus } from '~/libs/enums/data-status.enum.js'; @@ -30,6 +30,7 @@ import { useSubscribeUpdates } from './libs/hooks/use-subscribe-updates.hook.js' import styles from './styles.module.scss'; const OrderStatusPage: React.FC = () => { + const avatarURL = useRef(null); const { orderId } = useParams(); const navigate = useNavigate(); const mapReference = useRef(null); @@ -62,7 +63,6 @@ const OrderStatusPage: React.FC = () => { center: truckLocation ?? DEFAULT_CENTER, destination: order ? order.startPoint : null, mapReference: mapReference, - onMapLoad: () => true, }); const handleHomepageClick = useCallback(() => { @@ -92,11 +92,20 @@ const OrderStatusPage: React.FC = () => { isConfirmScreenOpen || isPickingUpScreenOpen || isDoneScreenOpen; const isMapShown = !isCancelScreenOpen && !isDoneScreenOpen; - const { firstName, lastName, licensePlate, price } = useGetOrderData(order); + const { + firstName, + lastName, + licensePlate, + price, + avatarUrl: newAvatarUrl, + } = useGetOrderData(order); + + if (!avatarURL.current) { + avatarURL.current = newAvatarUrl; + } const { distanceLeft, timespanLeft, startLocation, endLocation } = useGetRouteData(order); - const profileURL = null; const isOrderCardShown = !isCancelScreenOpen && @@ -109,10 +118,6 @@ const OrderStatusPage: React.FC = () => { return ; } - if (dataStatus !== DataStatus.FULFILLED) { - return ; - } - return (
{ isDriverShown={isDriverShown} className={styles.card} cardData={{ - profileURL, + profileURL: avatarURL.current, firstName, lastName, licensePlate, diff --git a/frontend/src/slices/orders/order.slice.ts b/frontend/src/slices/orders/order.slice.ts index 315c3a378..c1b812b4a 100644 --- a/frontend/src/slices/orders/order.slice.ts +++ b/frontend/src/slices/orders/order.slice.ts @@ -2,7 +2,10 @@ import { createSlice, isAnyOf } from '@reduxjs/toolkit'; import { DataStatus } from '~/libs/enums/enums.js'; import { type ValueOf } from '~/libs/types/types.js'; -import { type OrderResponseDto } from '~/packages/orders/orders.js'; +import { + type OrderResponseDto, + type OrderResponseWithAvatarDto, +} from '~/packages/orders/orders.js'; import { calculateOrderPrice, @@ -24,7 +27,7 @@ type State = { price: number; dataStatus: ValueOf; routeData: RouteData | null; - currentOrder: OrderResponseDto | null; + currentOrder: (OrderResponseDto | OrderResponseWithAvatarDto) | null; }; const initialState: State = { diff --git a/shared/src/index.ts b/shared/src/index.ts index ebbc1de51..f2bb43982 100644 --- a/shared/src/index.ts +++ b/shared/src/index.ts @@ -141,6 +141,7 @@ export { type OrderFindByIdResponseDto, type OrderQueryParameters, type OrderResponseDto, + type OrderResponseWithAvatarDto, type OrdersListResponseDto, type OrderStatusValues, type OrderUpdateAcceptStatusRequestDto, diff --git a/shared/src/packages/orders/libs/types/order-response-with-avatar-dto.type.ts b/shared/src/packages/orders/libs/types/order-response-with-avatar-dto.type.ts new file mode 100644 index 000000000..237474b7e --- /dev/null +++ b/shared/src/packages/orders/libs/types/order-response-with-avatar-dto.type.ts @@ -0,0 +1,8 @@ +import { type DriverInfo } from './order-entity.type.js'; +import { type OrderResponseDto } from './order-response-dto.type.js'; + +type OrderResponseWithAvatarDto = OrderResponseDto & { + shift: { driver: (DriverInfo & { avatarUrl: string | null }) | null }; +}; + +export { type OrderResponseWithAvatarDto }; diff --git a/shared/src/packages/orders/libs/types/types.ts b/shared/src/packages/orders/libs/types/types.ts index 2832d597e..94eb273f7 100644 --- a/shared/src/packages/orders/libs/types/types.ts +++ b/shared/src/packages/orders/libs/types/types.ts @@ -6,6 +6,7 @@ export { type DriverInfo, type OrderEntity } from './order-entity.type.js'; export { type OrderFindByIdResponseDto } from './order-find-by-id-response-dto.type.js'; export { type OrderQueryParameters } from './order-query-parameters.type.js'; export { type OrderResponseDto } from './order-response-dto.type.js'; +export { type OrderResponseWithAvatarDto } from './order-response-with-avatar-dto.type.js'; export { type OrderStatusValues } from './order-status-values.type.js'; export { type OrderUpdateAcceptStatusRequestDto } from './order-update-accept-status-request-dto.type.js'; export { type OrderUpdateAcceptStatusRequestParameter } from './order-update-accept-status-request-parameter.type.js'; diff --git a/shared/src/packages/orders/orders.ts b/shared/src/packages/orders/orders.ts index 79777987e..32deda137 100644 --- a/shared/src/packages/orders/orders.ts +++ b/shared/src/packages/orders/orders.ts @@ -14,6 +14,7 @@ export { type OrderFindByIdResponseDto, type OrderQueryParameters, type OrderResponseDto, + type OrderResponseWithAvatarDto, type OrdersListResponseDto, type OrderStatusValues, type OrderUpdateAcceptStatusRequestDto,