From 2a4479faac8f3e24ba842bff99aad75506325587 Mon Sep 17 00:00:00 2001 From: fakz9 Date: Mon, 30 Oct 2023 09:16:25 +0300 Subject: [PATCH] ebanutsya --- package.json | 1 + src/api/assemblyApi.ts | 38 +++ src/api/ordersApi.ts | 4 + src/assets/icons/cancel.png | Bin 0 -> 9094 bytes .../Modals/ImageZoomModal/ImageZoomModal.tsx | 69 ++++++ .../OrderCard/OrderProductsList.tsx | 2 +- src/components/SearchBar/SearchBar.tsx | 3 +- src/features/assembly/assemblySlice.ts | 45 +++- .../imageZoomModal/loadingModalSlice.ts | 30 +++ src/redux/store.ts | 4 +- src/screens/CommonPage/CommonPage.tsx | 31 ++- src/screens/OrderScreen/OrderScreen.tsx | 230 ++++++++++++------ src/types/assembly.ts | 16 ++ 13 files changed, 393 insertions(+), 80 deletions(-) create mode 100644 src/api/assemblyApi.ts create mode 100644 src/assets/icons/cancel.png create mode 100644 src/components/Modals/ImageZoomModal/ImageZoomModal.tsx create mode 100644 src/features/imageZoomModal/loadingModalSlice.ts create mode 100644 src/types/assembly.ts diff --git a/package.json b/package.json index e4d9adc..c5eb363 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "react-dom": "18.2.0", "react-native": "0.72.6", "react-native-gesture-handler": "~2.12.0", + "react-native-image-zoom-viewer": "^3.0.1", "react-native-keyevent": "^0.3.1", "react-native-keyevent-expo-config-plugin": "^1.0.49", "react-native-modal": "^13.0.1", diff --git a/src/api/assemblyApi.ts b/src/api/assemblyApi.ts new file mode 100644 index 0000000..1f6c28a --- /dev/null +++ b/src/api/assemblyApi.ts @@ -0,0 +1,38 @@ +import apiClient from "./apiClient"; +import {Assembly} from "../types/assembly"; + +const router = '/assembly'; + +const assemblyApi = { + create: async (orderId: number): Promise<{ + ok: boolean, + message: string, + assemblyId: number, + statusCode: string + }> => { + let response = await apiClient.post(`${router}/create`, {orderId}); + return response.data; + }, + close: async (assemblyId: number): Promise<{ ok: boolean, message: string }> => { + let response = await apiClient.post(`${router}/close`, {assemblyId}); + return response.data; + }, + getActive: async (): Promise => { + let response = await apiClient.get(`${router}/getActive`); + return response.data; + }, + hasActive: async (): Promise<{ has: boolean }> => { + let response = await apiClient.get(`${router}/hasActive`); + return response.data; + }, + updateState: async (assemblyId: number, state: number): Promise<{ ok: boolean }> => { + let response = await apiClient.post(`${router}/updateState`, {assemblyId, state}); + return response.data; + }, + confirm: async (assemblyId: number): Promise<{ ok: boolean, message: string }> => { + let response = await apiClient.post(`${router}/confirm`, {assemblyId}); + return response.data; + } +} + +export default assemblyApi; \ No newline at end of file diff --git a/src/api/ordersApi.ts b/src/api/ordersApi.ts index f530cc9..b09ba4b 100644 --- a/src/api/ordersApi.ts +++ b/src/api/ordersApi.ts @@ -10,6 +10,10 @@ const ordersApi = { getOrdersBySupplierProduct: async (supplierProductId: number): Promise => { let response = await apiClient.get(`${router}/getBySupplierProductId?supplierProductId=${supplierProductId}`); return response.data; + }, + getOrderById: async (orderId: number): Promise => { + let response = await apiClient.get(`${router}/getOrderById?orderId=${orderId}`); + return response.data; } } diff --git a/src/assets/icons/cancel.png b/src/assets/icons/cancel.png new file mode 100644 index 0000000000000000000000000000000000000000..9a11f1147a7cb9949792a0906a9b6c1e89700ca3 GIT binary patch literal 9094 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 BmOcOg literal 0 HcmV?d00001 diff --git a/src/components/Modals/ImageZoomModal/ImageZoomModal.tsx b/src/components/Modals/ImageZoomModal/ImageZoomModal.tsx new file mode 100644 index 0000000..09f982d --- /dev/null +++ b/src/components/Modals/ImageZoomModal/ImageZoomModal.tsx @@ -0,0 +1,69 @@ +import {FC} from "react"; +import {Props} from "react-native-paper"; +import {StyleSheet, View, Image, TouchableOpacity} from "react-native"; +import {BottomSheetModalProvider} from "@gorhom/bottom-sheet"; +import {useDispatch, useSelector} from "react-redux"; +import {RootState} from "../../../redux/store"; +import Modal from "react-native-modal"; +import {background, blue} from "../../../css/colors"; +import {responsiveHeight, responsiveWidth} from "react-native-responsive-dimensions"; +import * as Progress from 'react-native-progress'; +import ImageViewer from "react-native-image-zoom-viewer"; +import {RFPercentage} from "react-native-responsive-fontsize"; +import {closeImageZoomModal} from "../../../features/imageZoomModal/loadingModalSlice"; + +const ImageZoomModal: FC = () => { + const isVisible = useSelector((state: RootState) => state.imageZoomModal.isVisible); + const imageUrls = useSelector((state: RootState) => state.imageZoomModal.imageUrls); + const dispatch = useDispatch(); + return ( + + + + { + dispatch(closeImageZoomModal()) + }}> + + + + + + + { + console.log(imageUrl) + return { + url: imageUrl + } + })}/> + + + ) +} + +const styles = StyleSheet.create({ + container: { + alignSelf: "center", + backgroundColor: background, + borderRadius: responsiveWidth(2), + paddingBottom: responsiveHeight(5), + paddingHorizontal: responsiveWidth(5), + height: "95%", + width: "95%" + }, + header: { + flexDirection: "row", + justifyContent: "flex-end", + padding: RFPercentage(2) + }, + imageWrapper: { + height: responsiveHeight(4), + width: responsiveHeight(4), + }, + image: { + height: "100%", + width: "100%", + resizeMode: "center" + } +}) +export default ImageZoomModal; \ No newline at end of file diff --git a/src/components/OrderCard/OrderProductsList.tsx b/src/components/OrderCard/OrderProductsList.tsx index a2e957b..8b5be46 100644 --- a/src/components/OrderCard/OrderProductsList.tsx +++ b/src/components/OrderCard/OrderProductsList.tsx @@ -56,7 +56,7 @@ const OrderProductsList: FC = ({products, onSelected}) => { const listStyles = StyleSheet.create({ container: { - backgroundColor: "white", + backgroundColor: 'white', flex: 1 }, title: { diff --git a/src/components/SearchBar/SearchBar.tsx b/src/components/SearchBar/SearchBar.tsx index c0cc417..73fd591 100644 --- a/src/components/SearchBar/SearchBar.tsx +++ b/src/components/SearchBar/SearchBar.tsx @@ -76,7 +76,8 @@ const styles = StyleSheet.create({ borderRadius: 0, borderTopRightRadius: responsiveWidth(1), borderBottomRightRadius: responsiveWidth(1), - paddingHorizontal: responsiveWidth(5) + paddingHorizontal: responsiveWidth(5), + height:'100%' }, scanImageWrapper: { paddingHorizontal: responsiveWidth(1), diff --git a/src/features/assembly/assemblySlice.ts b/src/features/assembly/assemblySlice.ts index 6d96e96..d9b2778 100644 --- a/src/features/assembly/assemblySlice.ts +++ b/src/features/assembly/assemblySlice.ts @@ -1,12 +1,16 @@ import {createSlice, PayloadAction} from "@reduxjs/toolkit"; import {Order} from "../../types/order"; +import {Assembly, ASSEMBLY_STATE} from "../../types/assembly"; + export interface AssemblyState { order?: Order; + assembly?: Assembly; + localState?: ASSEMBLY_STATE } const initialState: AssemblyState = { - order: undefined, + localState: ASSEMBLY_STATE.NOT_STARTED } export const assembly = createSlice({ @@ -16,16 +20,47 @@ export const assembly = createSlice({ setOrder: (state, action) => { state.order = action.payload; }, + setAssembly: (state, action: PayloadAction) => { + state.assembly = action.payload; + state.localState = action.payload.state; + }, + startAssembly: (state) => { + if (!state.assembly) return; + state.assembly.createdAt = (new Date()).toDateString(); + + state.assembly.state = ASSEMBLY_STATE.ASSEMBLING_PRODUCTS; + state.localState = ASSEMBLY_STATE.ASSEMBLING_PRODUCTS; + }, + endAssembly: (state) => { + if (!state.assembly) return; + state.assembly.endedAt = (new Date()).toDateString(); + + state.assembly.state = ASSEMBLY_STATE.ENDED; + state.localState = ASSEMBLY_STATE.ENDED; + + state.assembly = undefined; + state.localState = ASSEMBLY_STATE.NOT_STARTED; + state.order = undefined; + }, + confirmAssembly: (state) => { + if (!state.assembly) return; + + state.assembly.state = ASSEMBLY_STATE.CONFIRMED; + state.localState = ASSEMBLY_STATE.CONFIRMED; + }, setAssembled: (state, action: PayloadAction<{ orderProductId: number }>) => { - if (!state.order || !state.order.products) return; - + if (!state.order || !state.order.products || !state.assembly) return; const orderProductId = action.payload.orderProductId; - state.order.products.forEach(product => { if (product.databaseId === orderProductId) { product.assembled = true; } }); + // If all products is assembled + if (!state.order.products.find(product => !product.assembled)) { + state.assembly.state = ASSEMBLY_STATE.ALL_PRODUCTS_ASSEMBLED; + state.localState = ASSEMBLY_STATE.ALL_PRODUCTS_ASSEMBLED; + } }, setShipped: (state, action: PayloadAction<{ orderProductId: number }>) => { if (!state.order || !state.order.products) return; @@ -41,5 +76,5 @@ export const assembly = createSlice({ } }) -export const {setOrder, setAssembled, setShipped} = assembly.actions; +export const {setOrder, setAssembled, setAssembly, startAssembly, endAssembly, confirmAssembly} = assembly.actions; export default assembly.reducer; \ No newline at end of file diff --git a/src/features/imageZoomModal/loadingModalSlice.ts b/src/features/imageZoomModal/loadingModalSlice.ts new file mode 100644 index 0000000..b77a87a --- /dev/null +++ b/src/features/imageZoomModal/loadingModalSlice.ts @@ -0,0 +1,30 @@ +import {createSlice, PayloadAction} from "@reduxjs/toolkit"; + +export interface ImageZoomModalState { + isVisible: boolean; + imageUrls: string[] +} + +const initialState: ImageZoomModalState = { + isVisible: false, + imageUrls: [] +} + +export const imageZoomModalSlice = createSlice({ + name: 'loadingModal', + initialState, + reducers: { + openImageZoomModal: (state) => { + state.isVisible = true + }, + closeImageZoomModal: (state) => { + state.isVisible = false + }, + setImages: (state, action: PayloadAction) => { + state.imageUrls = action.payload; + } + } +}) + +export const {closeImageZoomModal, openImageZoomModal, setImages} = imageZoomModalSlice.actions; +export default imageZoomModalSlice.reducer; \ No newline at end of file diff --git a/src/redux/store.ts b/src/redux/store.ts index c5593c9..3e2fb30 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -4,6 +4,7 @@ import authReducer from 'features/auth/authSlice'; import interfaceReducer from 'features/interface/interfaceSlice'; import scanModalReducer from 'features/scanModal/scanModalSlice'; import loadingModalReducer from 'features/loadingModal/loadingModalSlice'; +import imageZoomModalReducer from 'features/imageZoomModal/loadingModalSlice'; import assemblyReducer from 'features/assembly/assemblySlice'; import printingReducer from 'features/printing/printingSlice'; import reprintModalReducer from 'features/reprintModal/reprintModalSlice'; @@ -17,7 +18,8 @@ export const store = configureStore({ loadingModal: loadingModalReducer, assembly: assemblyReducer, printing: printingReducer, - reprintModal: reprintModalReducer + reprintModal: reprintModalReducer, + imageZoomModal: imageZoomModalReducer }, }); diff --git a/src/screens/CommonPage/CommonPage.tsx b/src/screens/CommonPage/CommonPage.tsx index 52d1faf..235e783 100644 --- a/src/screens/CommonPage/CommonPage.tsx +++ b/src/screens/CommonPage/CommonPage.tsx @@ -14,21 +14,43 @@ import ScanModal from "../../components/SearchBar/ScanModal"; import {closeScanModal, setScannedData} from "../../features/scanModal/scanModalSlice"; import LoadingModal from "../../components/Modals/LoadingModal/LoadingModal"; import ReprintModal from "../../components/Modals/ReprintModal/ReprintModal"; +import assemblyApi from "../../api/assemblyApi"; +import {assembly, setAssembly, setOrder} from "../../features/assembly/assemblySlice"; +import ordersApi from "../../api/ordersApi"; +import ImageZoomModal from "../../components/Modals/ImageZoomModal/ImageZoomModal"; function CommonPage() { const dim = useSelector((state: RootState) => state.interface.dim); const isAuthorized = useSelector((state: RootState) => state.auth.isAuthorized); const dispatch = useDispatch(); - - const loadSettings = async () => { + const loadSettings = async (): Promise => { const token = await SecureStore.getItemAsync('accessToken'); - if (!token) return; + if (!token) return false; dispatch(login({accessToken: token})); + return true; + } + const loadAssembly = async () => { + assemblyApi.hasActive().then(({has}) => { + if (!has) return; + assemblyApi.getActive().then(assembly => { + ordersApi.getOrderById(assembly.orderId).then(order => { + dispatch(setAssembly(assembly)); + dispatch(setOrder(order)); + }) + }) + }) + } + const initialize = () => { + loadSettings().then(isSettingsLoaded => { + if (!isSettingsLoaded) return; + loadAssembly(); + }) } + useEffect(() => { - loadSettings(); + initialize(); }, []); return ( @@ -39,6 +61,7 @@ function CommonPage() { + diff --git a/src/screens/OrderScreen/OrderScreen.tsx b/src/screens/OrderScreen/OrderScreen.tsx index 77c5f04..5877992 100644 --- a/src/screens/OrderScreen/OrderScreen.tsx +++ b/src/screens/OrderScreen/OrderScreen.tsx @@ -1,4 +1,4 @@ -import {View, Text, Image, StyleSheet, Button} from "react-native"; +import {View, Text, Image, StyleSheet, Button, TouchableOpacity} from "react-native"; import {responsiveHeight, responsiveWidth} from "react-native-responsive-dimensions"; import DText from "../../components/DText/DText"; import {RFPercentage} from "react-native-responsive-fontsize"; @@ -16,14 +16,29 @@ import {useNavigation} from "@react-navigation/native"; import printingApi from "../../api/printingApi"; import PrintingService from "../../utils/PrintingService"; import OrderProductsList from "../../components/OrderCard/OrderProductsList"; -import {setAssembled, setOrder} from "../../features/assembly/assemblySlice"; +import { + confirmAssembly, endAssembly, + setAssembled, + setAssembly, + setOrder, + startAssembly +} from "../../features/assembly/assemblySlice"; import AcceptModal from "../../components/Modals/AcceptModal/AcceptModal"; import {Order} from "../../types/order"; import acceptModal from "../../components/Modals/AcceptModal/AcceptModal"; import printingService from "../../utils/PrintingService"; import ReprintModal from "../../components/Modals/ReprintModal/ReprintModal"; -import {setData, setPrinterName} from "../../features/printing/printingSlice"; +import {setPrinterName} from "../../features/printing/printingSlice"; import {openReprintModal} from "../../features/reprintModal/reprintModalSlice"; +import {ASSEMBLY_STATE} from "../../types/assembly"; +import {RenderTargetOptions} from "@shopify/flash-list"; +import {createOnShouldStartLoadWithRequest} from "react-native-webview/lib/WebViewShared"; +import assemblyApi from "../../api/assemblyApi"; +import Toast from "react-native-toast-message"; +import mainScreen from "../MainScreen/MainScreen"; +import toast from "../../components/Toast/Toast"; +import {openImageZoomModal, setImages} from "../../features/imageZoomModal/loadingModalSlice"; + type AssemblyPeriod = { started: Date; @@ -65,39 +80,131 @@ type OrderScreenProps = { const OrderScreen: FC = ({order}) => { const navigator = useNavigation(); const dispatch = useDispatch(); + const assembly = useSelector((state: RootState) => state.assembly.assembly); + const assemblyState = useSelector((state: RootState) => state.assembly.localState); + const [selectedProduct, setSelectedProduct] = useState(order.products[0]); - const [assemblyPeriod, setAssemblyPeriod] = useState({started: new Date(), ended: new Date()}) + const [acceptModalVisible, setAcceptModalVisible] = useState(false); - const [reprintModalVisible, setReprintModalVisible] = useState(false); + const [skipConfirmButtonVisible, setSkipConfirmButtonVisible] = useState(false); + const _confirmAssembly = async () => { + if (!assembly) return; + dispatch(setLoadingText('Подтверждение сборки...')) + dispatch(openLoadingModal()); + assemblyApi.confirm(assembly.databaseId).then(({ok, message}) => { + dispatch(closeLoadingModal()); + Toast.show({ + type: ok ? 'success' : 'error', + text1: 'Подтверждение сборки', + text2: message + }) + if (ok) { + dispatch(confirmAssembly()); - const [assemblyStarted, setAssemblyStarted] = useState(false); - const [assemblyIntervalId, setAssemblyIntervalId] = useState(0); - const prettyPrintMilliseconds = (milliseconds: number) => { - const totalSeconds = Math.floor(milliseconds / 1000); - const minutes = Math.floor(totalSeconds / 60); - const seconds = totalSeconds % 60; - - // Форматируем минуты и секунды, чтобы они всегда были двузначными - const formattedMinutes = String(minutes).padStart(2, '0'); - const formattedSeconds = String(seconds).padStart(2, '0'); - - return `${formattedMinutes}:${formattedSeconds}`; + } else { + setSkipConfirmButtonVisible(true); + } + }) } - const startAssembly = async () => { - setAssemblyStarted(true); - let intervalId = setInterval(() => { - setAssemblyPeriod(oldValue => ({...oldValue, ended: new Date()})) - }, 1000); - setAssemblyIntervalId(Number(intervalId)) - }; + const getButtons = () => { + switch (assemblyState) { + case ASSEMBLY_STATE.NOT_STARTED: + return ( setAcceptModalVisible(true)} + label={"Начать сборку"}/>) + case ASSEMBLY_STATE.ASSEMBLING_PRODUCTS: + return ( { + dispatch(setAssembled({orderProductId: selectedProduct.databaseId})) + }} + />) + case ASSEMBLY_STATE.ALL_PRODUCTS_ASSEMBLED: + return ( + <> + { + _confirmAssembly(); + }} + containerStyle={styles.buttonContainer} + label={"Подтвердить сборку"} + /> + {skipConfirmButtonVisible && { + dispatch(confirmAssembly()); + }} + style={{backgroundColor: 'orange'}} + containerStyle={styles.buttonContainer} + label={"Пропустить подтверждение сборки"} + />} + + ) + case ASSEMBLY_STATE.CONFIRMED: + return ( + <> + + printLabel()} + containerStyle={styles.buttonContainer} + label={"Печать этикетки"} + /> + { + if (!assembly) return; + assemblyApi.close(assembly.databaseId).then(({ok, message}) => { + Toast.show({ + type: ok ? 'success' : 'error', + text1: 'Завершение сборки', + text2: message + }); + dispatch(endAssembly()); + }) + }}/> + + ) + case ASSEMBLY_STATE.ENDED: + return ( + { + }}/> + ) + } + } + const printLabel = () => { + if (!order) return; + dispatch(setLoadingText('Идет печать этикетки...')) + dispatch(openLoadingModal()) + printingApi.getLabel(order.databaseId).then(pdfData => { + printingService.getInstance().printPdf('ozon', pdfData).then((response) => { + dispatch(closeLoadingModal()); + if (response) return; + dispatch(setPrinterName({printerName: 'ozon'})); + dispatch(openReprintModal()); + }); + }) + } + useEffect(() => { - let newSelectedProduct = order.products.find(o => o.databaseId == selectedProduct.databaseId); - if (!newSelectedProduct) return; - setSelectedProduct(newSelectedProduct); - }, [order]); - useEffect(() => { - dispatch(closeLoadingModal()) - }, []); + if (!assembly) return; + assemblyApi.updateState(assembly.databaseId, Number(assemblyState)).then(ok => { + if (ok) return; + Toast.show({ + type: 'error', + text1: 'Обновление состояния', + text2: 'Неудалось обновить состояние текущей сборки на сервере' + }) + }); + + }, [assemblyState]); + return ( @@ -107,9 +214,16 @@ const OrderScreen: FC = ({order}) => { setSelectedProduct(product) }}/> - - - + + { + dispatch(setImages([selectedProduct.imageUrl])); + dispatch(openImageZoomModal()); + }}> + + + + + @@ -124,49 +238,29 @@ const OrderScreen: FC = ({order}) => { Номер товара: {0} {} Сборка - Затрачено - времени: {prettyPrintMilliseconds(assemblyPeriod.ended.getTime() - assemblyPeriod.started.getTime())} - {assemblyStarted ? <> - { - dispatch(setAssembled({orderProductId: selectedProduct.databaseId})) - }} - /> - { - if (!order) return; - dispatch(setLoadingText('Идет печать этикетки...')) - dispatch(openLoadingModal()) - printingApi.getLabel(order.databaseId).then(pdfData => { - printingService.getInstance().printPdf('ozon', pdfData).then((response) => { - dispatch(closeLoadingModal()); - if (response) return; - dispatch(setPrinterName({printerName: 'ozon'})); - dispatch(openReprintModal()); - }); - }) - }} - containerStyle={styles.buttonContainer} - label={"Печать этикетки"} - disabled={Boolean(order.products.find(product => !product.assembled))}/> - - : setAcceptModalVisible(true)} - label={"Начать сборку"}/>} - + {getButtons()} { setAcceptModalVisible(false); - startAssembly(); + assemblyApi.create(order.databaseId).then(creationResult => { + console.log(creationResult.message) + Toast.show({ + type: creationResult.ok ? 'success' : 'error', + text1: 'Создание сборки', + text2: creationResult.message + }); + if (!creationResult.ok) return; + assemblyApi.getActive().then(activeAssembly => { + dispatch(setAssembly(activeAssembly)); + dispatch(startAssembly()) + }) + }) }} onRefused={() => { setAcceptModalVisible(false); diff --git a/src/types/assembly.ts b/src/types/assembly.ts new file mode 100644 index 0000000..a2a0ed6 --- /dev/null +++ b/src/types/assembly.ts @@ -0,0 +1,16 @@ +export enum ASSEMBLY_STATE { + NOT_STARTED, + ASSEMBLING_PRODUCTS, + ALL_PRODUCTS_ASSEMBLED, + CONFIRMED, + ENDED +} + +export type Assembly = { + databaseId: number; + createdAt: string | null; + endedAt: string | null; + orderId: number; + isActive: boolean; + state: ASSEMBLY_STATE +} \ No newline at end of file