From fd79ab4e72c1ec1841f88b1f5d836fb81cc772d3 Mon Sep 17 00:00:00 2001 From: RustamRu Date: Sun, 19 Jan 2025 17:37:57 +0300 Subject: [PATCH] feat: create&view order api&model (#65) --- src/api/index.ts | 1 + src/api/landing.ts | 25 ----- src/api/landing.tsx | 107 +++++++++++++++++++ src/api/types.ts | 22 ++++ src/models/api/index.ts | 2 +- src/models/api/order-view.ts | 14 --- src/models/api/order.ts | 18 ++++ src/models/common.ts | 1 + src/models/landing/car.ts | 2 +- src/models/landing/order.ts | 14 ++- src/models/landing/washing.ts | 8 +- stubs/json/landing-order-view/1-success.json | 21 ++-- 12 files changed, 177 insertions(+), 58 deletions(-) create mode 100644 src/api/index.ts delete mode 100644 src/api/landing.ts create mode 100644 src/api/landing.tsx create mode 100644 src/api/types.ts delete mode 100644 src/models/api/order-view.ts create mode 100644 src/models/api/order.ts create mode 100644 src/models/common.ts diff --git a/src/api/index.ts b/src/api/index.ts new file mode 100644 index 0000000..c8f65fc --- /dev/null +++ b/src/api/index.ts @@ -0,0 +1 @@ +export * from './landing'; \ No newline at end of file diff --git a/src/api/landing.ts b/src/api/landing.ts deleted file mode 100644 index 1ea8bf4..0000000 --- a/src/api/landing.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { getConfigValue } from '@brojs/cli'; - -import { Order } from '../models/landing'; - -enum LandingEndpoints { - ORDER_VIEW = '/order' -} - -const LandingService = () => { - const endpoint = getConfigValue('dry-wash.api'); - - const fetchOrder = async (orderId: Order.Id) => { - const response = await fetch(`${endpoint}${LandingEndpoints.ORDER_VIEW}/${orderId}`); - - if (!response.ok) { - throw new Error(`Failed to fetch order: ${response.status}`); - } - - return await response.json(); - }; - - return { fetchOrder }; -}; - -export { LandingService, LandingEndpoints }; diff --git a/src/api/landing.tsx b/src/api/landing.tsx new file mode 100644 index 0000000..7348167 --- /dev/null +++ b/src/api/landing.tsx @@ -0,0 +1,107 @@ +import { getConfigValue } from '@brojs/cli'; +import { useEffect, useState } from 'react'; + +import { CreateOrder, GetOrder } from '../models/api'; + +import { QueryState, Trigger } from './types'; + +enum LandingEndpoints { + ORDER = '/order', + ORDER_CREATE = '/order/create', +} + +const endpoint = getConfigValue('dry-wash.api'); + +const useCreateOrderMutation = (): [ + Trigger['data']>, + QueryState, +] => { + const [isLoading, setIsLoading] = useState['isLoading']>(false); + const [isSuccess, setIsSuccess] = useState['isSuccess']>(); + const [data, setData] = useState['data']>(); + const [isError, setIsError] = useState['isError']>(); + const [error, setError] = useState['error']>(); + + const createOrder = async ({ body }: CreateOrder.Params) => { + setIsLoading(true); + + try { + const response = await fetch( + `${endpoint}${LandingEndpoints.ORDER_CREATE}`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }, + ); + + if (!response.ok) { + const errorResponseObject = + (await response.json()) as QueryState['error']; + setIsError(true); + setError(errorResponseObject); + throw errorResponseObject; + } + + const dataResponseObject = + (await response.json()) as QueryState['data']; + setIsSuccess(true); + setData(dataResponseObject); + + return dataResponseObject; + } catch (error) { + setIsError(true); + setError(error); + throw error; + } finally { + setIsLoading(false); + } + }; + + return [createOrder, { isLoading, isSuccess, data, isError, error }]; +}; + +const useGetOrderQuery = ({ + orderId, +}: GetOrder.Params): QueryState => { + const [isLoading, setIsLoading] = useState['isLoading']>(true); + const [isSuccess, setIsSuccess] = useState['isSuccess']>(); + const [data, setData] = useState['data']>(); + const [isError, setIsError] = useState['isError']>(); + const [error, setError] = useState['error']>(); + + useEffect(() => { + (async () => { + try { + const response = await fetch( + `${endpoint}${LandingEndpoints.ORDER}/${orderId}`, + ); + + if (!response.ok) { + const errorResponseObject = + (await response.json()) as QueryState['error']; + setIsError(true); + setError(errorResponseObject); + throw errorResponseObject; + } + + const dataResponseObject = + (await response.json()) as QueryState['data']; + setIsSuccess(true); + setData(dataResponseObject); + } catch (error) { + setIsError(true); + setError(error); + throw error; + } finally { + setIsLoading(false); + } + })(); + }, []); + + return { isLoading, isSuccess, data, isError, error }; +}; + +export { useCreateOrderMutation, useGetOrderQuery }; diff --git a/src/api/types.ts b/src/api/types.ts new file mode 100644 index 0000000..98d4848 --- /dev/null +++ b/src/api/types.ts @@ -0,0 +1,22 @@ +export type QueryData = { + success: true; + body: D; +}; + +export type QueryErrorData = { + success: false; + error: string; +}; + +export type QueryState = { + isLoading: boolean; + isSuccess: boolean; + data: QueryData; + isError: boolean; + error: { + status: number; + data: QueryErrorData; + }; +}; + +export type Trigger = (params: P) => Promise; diff --git a/src/models/api/index.ts b/src/models/api/index.ts index e27d5ad..f57351e 100644 --- a/src/models/api/index.ts +++ b/src/models/api/index.ts @@ -1 +1 @@ -export * from './order-view'; \ No newline at end of file +export * from './order'; \ No newline at end of file diff --git a/src/models/api/order-view.ts b/src/models/api/order-view.ts deleted file mode 100644 index 0c33f0c..0000000 --- a/src/models/api/order-view.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Order } from "../landing"; - -export type FetchOrderQueryResponse = { - id: string; - orderDate: string; - carNumber: string; - carBody: number; - carColor?: string; - startWashTime: string; - endWashTime: string; - status: Order.Status; - phone: string; - location: string; -}; \ No newline at end of file diff --git a/src/models/api/order.ts b/src/models/api/order.ts new file mode 100644 index 0000000..b22283b --- /dev/null +++ b/src/models/api/order.ts @@ -0,0 +1,18 @@ +/* eslint-disable @typescript-eslint/no-namespace */ +import { Order } from "../landing"; + +export namespace CreateOrder { + export type Response = { + id: Order.Id + }; + export type Params = { + body: Order.Create + }; +}; + +export namespace GetOrder { + export type Response = Order.View; + export type Params = { + orderId: Order.Id + }; +}; \ No newline at end of file diff --git a/src/models/common.ts b/src/models/common.ts new file mode 100644 index 0000000..99fb844 --- /dev/null +++ b/src/models/common.ts @@ -0,0 +1 @@ +export type IsoDate = string; // YYYY-MM-DDThh:mm:ss.mmmZ \ No newline at end of file diff --git a/src/models/landing/car.ts b/src/models/landing/car.ts index 9058a85..971cc78 100644 --- a/src/models/landing/car.ts +++ b/src/models/landing/car.ts @@ -1,4 +1,4 @@ -export type RegistrationNumber = string; // А012ВЕ +export type RegistrationNumber = string; // А012ВЕ16 export type Color = string; // #000000 diff --git a/src/models/landing/order.ts b/src/models/landing/order.ts index 8a8eeef..58db543 100644 --- a/src/models/landing/order.ts +++ b/src/models/landing/order.ts @@ -1,3 +1,5 @@ +import { IsoDate } from "../common"; + import { Car, Customer, Washing } from "."; export type Id = string; @@ -25,14 +27,16 @@ export type Create = { }; export type View = { - id: Id; - orderDate: string, - status: Status, phone: Customer.PhoneNumber; carNumber: Car.RegistrationNumber; carBody: Car.BodyStyle; carColor?: Car.Color; location: Washing.Location; - datetimeBegin: Washing.AvailableBeginDateTime; - datetimeEnd: Washing.AvailableEndDateTime; + startWashTime: Washing.AvailableBeginDateTime; + endWashTime: Washing.AvailableEndDateTime; + status: Status, + notes: string; + created: IsoDate; + updated: IsoDate; + id: Id; }; \ No newline at end of file diff --git a/src/models/landing/washing.ts b/src/models/landing/washing.ts index c61c638..61c8b7a 100644 --- a/src/models/landing/washing.ts +++ b/src/models/landing/washing.ts @@ -1,5 +1,7 @@ -export type Location = string; // ? +import { IsoDate } from "../common"; -export type AvailableBeginDateTime = string; // YYYY-MM-DDThh:mm +export type Location = string; // 55.754364, 48.743295 Университетская улица, 1, Иннополис, Верхнеуслонский район, Республика Татарстан (Татарстан), 420500 -export type AvailableEndDateTime = string; // YYYY-MM-DDThh:mm \ No newline at end of file +export type AvailableBeginDateTime = IsoDate; + +export type AvailableEndDateTime = IsoDate; \ No newline at end of file diff --git a/stubs/json/landing-order-view/1-success.json b/stubs/json/landing-order-view/1-success.json index 4e09761..bd5f926 100644 --- a/stubs/json/landing-order-view/1-success.json +++ b/stubs/json/landing-order-view/1-success.json @@ -1,14 +1,17 @@ { "success": true, "body": { - "id": "order1", - "orderDate": "2024-11-24T08:41:46.366Z", - "status": "progress", - "carNumber": "A123BC", - "carBody": 1, - "startWashTime": "2024-11-24T10:30:00.000Z", - "endWashTime": "2024-11-24T16:30:00.000Z", - "phone": "79001234563", - "location": "55.754364, 48.743295 Университетская улица, 1, Иннополис, Верхнеуслонский район, Республика Татарстан (Татарстан), 420500" + "phone": "+79876543210", + "carNumber": "А123АА16", + "carBody": 2, + "carColor": "#ffffff", + "startWashTime": "2025-01-19T14:03:00.000Z", + "endWashTime": "2025-01-19T14:03:00.000Z", + "location": "55.793833888711006,49.19037910644527 Республика Татарстан (Татарстан), Казань, жилой район Седьмое Небо", + "status": "progress", + "notes": "", + "created": "2025-01-19T14:04:02.985Z", + "updated": "2025-01-19T14:04:02.987Z", + "id": "678d06527d78ec30be2679d8" } } \ No newline at end of file