ebanutsya

This commit is contained in:
2023-10-29 06:54:42 +03:00
parent 0bc1835405
commit cd89a70b17
15 changed files with 346 additions and 88 deletions

View File

@@ -3,6 +3,7 @@ import {StyleSheet, TouchableOpacity, Text, View, StyleProp, ViewStyle, GestureR
import {responsiveHeight, responsiveWidth} from "react-native-responsive-dimensions";
import {RFPercentage} from "react-native-responsive-fontsize";
import DText from "../DText/DText";
import DTitle from "../DTitle/DTitle";
type Props = {
label: string;
@@ -15,11 +16,12 @@ type Props = {
const BasicButton: FC<Props> = ({label, onPress, containerStyle, style, isUnset = false, disabled = false}) => {
return (
<TouchableOpacity style={{flex: 1}} disabled={disabled} onPress={onPress}>
<View style={[styles.container, style, disabled ? {backgroundColor: "#A0A0A0"} : {}]}>
<TouchableOpacity style={containerStyle} disabled={disabled} onPress={onPress}>
<View style={[styles.container, style, disabled ? {backgroundColor: "#A0A0A0"} : {}, containerStyle]}>
<View style={styles.textWrapper}>
<DText style={styles.text}>{label}</DText>
</View>
</View>
</TouchableOpacity>
);
@@ -33,15 +35,15 @@ const styles = StyleSheet.create({
alignContent: "center",
backgroundColor: '#2478F8',
borderRadius: responsiveWidth(1),
padding: responsiveWidth(2),
flex: 1
padding: responsiveWidth(3),
},
text: {
color: "white",
fontSize: RFPercentage(2),
textAlignVertical: "center",
textAlign: "center"
}
textAlign: "center",
},
textWrapper: {}
});
export default BasicButton;

View File

@@ -0,0 +1,65 @@
import {FC} from "react";
import {StyleSheet, View} from "react-native";
import {BottomSheetModalProvider} from "@gorhom/bottom-sheet";
import {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 DTitle from "../../DTitle/DTitle";
import {RFPercentage} from "react-native-responsive-fontsize";
import BasicButton from "../../BasicButton/BasicButton";
type Props = {
visible: boolean;
text: string;
onAccepted: () => void;
onRefused: () => void;
}
const AcceptModal: FC<Props> = ({visible, text, onAccepted, onRefused}) => {
return (
<Modal isVisible={visible}>
<View style={styles.container}>
<DTitle style={{textAlign: "center"}}>{text}</DTitle>
<View style={styles.buttonsWrapper}>
<BasicButton style={[styles.button, styles.acceptButton]} label={"Да"}
onPress={() => onAccepted()}/>
<BasicButton style={[styles.button, styles.refuseButton]} label={"Нет"}
onPress={() => onRefused()}/>
</View>
</View>
</Modal>
)
}
const styles = StyleSheet.create({
container: {
alignSelf: "center",
backgroundColor: background,
width: responsiveWidth(80),
height: responsiveHeight(50),
borderRadius: responsiveWidth(2),
paddingHorizontal: responsiveWidth(15),
paddingVertical: responsiveHeight(10),
justifyContent: "center",
rowGap: responsiveHeight(5)
},
buttonsWrapper: {
flexDirection: "row",
justifyContent: "space-between",
},
progressBar: {
justifyContent: "center"
},
button: {
paddingHorizontal: responsiveWidth(8)
},
acceptButton: {},
refuseButton: {
backgroundColor: "red"
},
})
export default AcceptModal;

View File

@@ -0,0 +1,37 @@
import {FC, useState} from "react";
import AcceptModal from "../AcceptModal/AcceptModal";
import PrintingService from "../../../utils/PrintingService";
import {useDispatch, useSelector} from "react-redux";
import {RootState} from "../../../redux/store";
import {closeReprintModal, openReprintModal} from "../../../features/reprintModal/reprintModalSlice";
import PrintingApi from "../../../api/printingApi";
import {closeLoadingModal, openLoadingModal} from "../../../features/loadingModal/loadingModalSlice";
const ReprintModal: FC = () => {
const printerName = useSelector((state: RootState) => state.printing.printerName);
const order = useSelector((state: RootState) => state.assembly.order);
const isVisible = useSelector((state: RootState) => state.reprintModal.isVisible);
const dispatch = useDispatch();
return (<AcceptModal
visible={isVisible}
text={"Неудалось распечатать этикетку, повторить попытку?"}
onAccepted={() => {
if (!printerName || !order) return;
dispatch(closeReprintModal());
dispatch(openLoadingModal());
PrintingApi.getLabel(order.databaseId).then(pdfBytes => {
PrintingService.getInstance().printPdf(printerName, pdfBytes).then(r => {
dispatch(closeLoadingModal());
if (r) dispatch(closeReprintModal());
else dispatch(openReprintModal());
});
})
}}
onRefused={() => {
dispatch(closeReprintModal())
dispatch(closeLoadingModal());
}}/>)
}
export default ReprintModal;

View File

@@ -6,18 +6,20 @@ import {responsiveHeight, responsiveWidth} from "react-native-responsive-dimensi
import DTitle from "../DTitle/DTitle";
import {Order} from "../../types/order";
import OrderProductsList from "./OrderProductsList";
import {useDispatch} from "react-redux";
import {setOrder} from "../../features/assembly/assemblySlice";
type Props = {
onPress?: (event: GestureResponderEvent) => void
onSelect: (order: Order) => void
order: Order
onSelect: (order: Order) => void
}
const OrderCard: FC<Props> = React.memo(({onPress, onSelect, order}) => {
const OrderCard: FC<Props> = ({onPress, onSelect, order}) => {
return (
<TouchableOpacity onPress={() => {
onSelect(order);
<TouchableOpacity onPress={()=>{
if (onSelect) onSelect(order);
}}>
<View style={styles.container}><View style={styles.description}>
<View style={styles.descriptionContent}>
@@ -28,6 +30,7 @@ const OrderCard: FC<Props> = React.memo(({onPress, onSelect, order}) => {
</View>
<DText>Селлер: {order.sellerName}</DText>
<DText>Маркетплейс: {order.marketplaceName}</DText>
<DText>Товаров в заказе: {order.products.length}</DText>
</View>
<View style={styles.descriptionStatus}>
<DText>
@@ -40,13 +43,12 @@ const OrderCard: FC<Props> = React.memo(({onPress, onSelect, order}) => {
</TouchableOpacity>
)
})
}
const styles = StyleSheet.create({
container: {
backgroundColor: "white",
display: "flex",
borderRadius: RFPercentage(3),
height: responsiveHeight(15),
flexDirection: "row",
padding: RFPercentage(2),
flex: 1
@@ -80,7 +82,6 @@ const styles = StyleSheet.create({
marginLeft: responsiveHeight(1)
},
descriptionContent: {
// backgroundColor: "green",
flex: 1
},
descriptionStatus: {

View File

@@ -14,9 +14,12 @@ const ScanModal: FC = () => {
const visible = useSelector((state: RootState) => state.scanModal.isVisible);
const dispatch = useDispatch();
useEffect(() => {
if (visible) inputRef.current?.focus();
// if (visible) inputRef.current?.focus();
if (visible){
dispatch(setScannedData('4750735280715'));
dispatch(closeScanModal())
}
}, [visible]);
return (
<Modal isVisible={visible}>
<View style={styles.container}>

View File

@@ -25,10 +25,12 @@ const SearchBar: FC<Props> = ({onSearch, onSupplierProductSelected}) => {
const scannedData = useSelector((state: RootState) => state.scanModal.scannedData);
useEffect(() => {
if (!scannedData) return;
barcodeApi.searchProducts(scannedData).then(setProducts);
barcodeApi.searchProducts(scannedData).then((response) => {
console.log("Response: " + response);
setProducts(response)
});
}, [scannedData]);
const selectProductModalVisible = products.length > 0 && false;
const selectProductModalVisible = products.length > 0;
return (
<View style={styles.container}>
@@ -74,6 +76,7 @@ const styles = StyleSheet.create({
borderRadius: 0,
borderTopRightRadius: responsiveWidth(1),
borderBottomRightRadius: responsiveWidth(1),
paddingHorizontal: responsiveWidth(5)
},
scanImageWrapper: {
paddingHorizontal: responsiveWidth(1),
@@ -85,14 +88,13 @@ const styles = StyleSheet.create({
},
textInput: {
height: responsiveHeight(height),
flex: 2,
flex: 1,
borderWidth: 1,
borderColor: "#A5A5A5",
borderTopLeftRadius: responsiveWidth(1),
borderBottomLeftRadius: responsiveWidth(1),
paddingLeft: responsiveHeight(2),
fontSize: RFPercentage(2),
// fontFamily: 'SF Pro Text'
}
})

View File

@@ -1,4 +1,4 @@
import {createSlice} from "@reduxjs/toolkit";
import {createSlice, PayloadAction} from "@reduxjs/toolkit";
import {Order} from "../../types/order";
export interface AssemblyState {
@@ -16,13 +16,30 @@ export const assembly = createSlice({
setOrder: (state, action) => {
state.order = action.payload;
},
setAssembled: (state, payload) => {
state.order = ({...state.order, orderNumber:"2282"})
setAssembled: (state, action: PayloadAction<{ orderProductId: number }>) => {
if (!state.order || !state.order.products) return;
const orderProductId = action.payload.orderProductId;
state.order.products.forEach(product => {
if (product.databaseId === orderProductId) {
product.assembled = true;
}
});
},
setShipped: (state) => {
setShipped: (state, action: PayloadAction<{ orderProductId: number }>) => {
if (!state.order || !state.order.products) return;
const orderProductId = action.payload.orderProductId;
state.order.products.forEach(product => {
if (product.databaseId === orderProductId) {
product.shipped = true;
}
});
}
}
})
export const {setOrder} = assembly.actions;
export const {setOrder, setAssembled, setShipped} = assembly.actions;
export default assembly.reducer;

View File

@@ -0,0 +1,22 @@
import {createSlice, PayloadAction} from "@reduxjs/toolkit";
export interface PrintingState {
printerName?: string;
}
const initialState: PrintingState = {
printerName: undefined,
}
export const printingSlice = createSlice({
name: 'printing',
initialState,
reducers: {
setPrinterName: (state, action: PayloadAction<{ printerName: string }>) => {
state.printerName = action.payload.printerName;
}
}
});
export const {setPrinterName} = printingSlice.actions;
export default printingSlice.reducer;

View File

@@ -0,0 +1,25 @@
import {createSlice} from "@reduxjs/toolkit";
export interface ReprintModalState {
isVisible: boolean;
}
const initialState: ReprintModalState = {
isVisible: false,
}
export const reprintModalSlice = createSlice({
name: 'reprintModal',
initialState,
reducers: {
openReprintModal: (state) => {
state.isVisible = true
},
closeReprintModal: (state) => {
state.isVisible = false
},
}
})
export const {openReprintModal, closeReprintModal} = reprintModalSlice.actions;
export default reprintModalSlice.reducer;

View File

@@ -5,6 +5,8 @@ import interfaceReducer from 'features/interface/interfaceSlice';
import scanModalReducer from 'features/scanModal/scanModalSlice';
import loadingModalReducer from 'features/loadingModal/loadingModalSlice';
import assemblyReducer from 'features/assembly/assemblySlice';
import printingReducer from 'features/printing/printingSlice';
import reprintModalReducer from 'features/reprintModal/reprintModalSlice';
import {useDispatch} from "react-redux";
export const store = configureStore({
@@ -13,7 +15,9 @@ export const store = configureStore({
interface: interfaceReducer,
scanModal: scanModalReducer,
loadingModal: loadingModalReducer,
assembly: assemblyReducer
assembly: assemblyReducer,
printing: printingReducer,
reprintModal: reprintModalReducer
},
});

View File

@@ -13,6 +13,7 @@ import toastConfig from "../../components/Toast/Toast";
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";
function CommonPage() {
const dim = useSelector((state: RootState) => state.interface.dim);
@@ -37,6 +38,7 @@ function CommonPage() {
<View style={[styles.overlay, {display: dim ? 'flex' : 'none'}]}/>
<LoadingModal/>
<ScanModal/>
<ReprintModal/>
<Toast config={toastConfig}/>
</View>

View File

@@ -6,7 +6,7 @@ import moneyScreen from "../MoneyScreen/MoneyScreen";
import profileScreen from "../ProfileScreen/ProfileScreen";
import {responsiveHeight, responsiveWidth} from "react-native-responsive-dimensions";
import {RFPercentage} from "react-native-responsive-fontsize";
import OrderScreen from "../OrderScreen/OrderScreen";
import OrderScreen, {OrderScreenController} from "../OrderScreen/OrderScreen";
import OrdersScreen from "../OrdersScreen/OrdersScreen";
import {background} from "../../css/colors";
import * as fs from "fs";
@@ -50,7 +50,7 @@ function MainScreen() {
const tabScreens: CustomTabProps[] = [
{
name: "Home",
component: OrderScreen,
component: OrderScreenController,
icon: require('assets/icons/home.png'),
hidden: false
},

View File

@@ -5,7 +5,7 @@ import {RFPercentage} from "react-native-responsive-fontsize";
import DTitle from "../../components/DTitle/DTitle";
import BasicButton from "../../components/BasicButton/BasicButton";
import Hyperlink from "../../components/Hyperlink/Hyperlink";
import React, {useEffect, useState} from "react";
import React, {FC, useEffect, useState} from "react";
import userApi from "../../api/userApi";
import {StatusBar} from "expo-status-bar";
import * as Progress from 'react-native-progress';
@@ -16,17 +16,25 @@ import {useNavigation} from "@react-navigation/native";
import printingApi from "../../api/printingApi";
import PrintingService from "../../utils/PrintingService";
import OrderProductsList from "../../components/OrderCard/OrderProductsList";
import {setOrder} from "../../features/assembly/assemblySlice";
import {setAssembled, setOrder} 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 {openReprintModal} from "../../features/reprintModal/reprintModalSlice";
type AssemblyPeriod = {
started: Date;
ended: Date;
}
function OrderScreen() {
const order = useSelector((state: RootState) => state.assembly.order);
if (!order) return (
const NoOrderScreen: FC = () => {
return (
<View style={noOrderStyles.container}>
<DText style={noOrderStyles.title}>Заказ не выбран!</DText>
<View style={noOrderStyles.buttonWrapper}>
<BasicButton onPress={() => {
// @ts-ignore
navigator.navigate("Box");
@@ -35,16 +43,68 @@ function OrderScreen() {
</View>
)
const dispatch = useDispatch();
const assembled = useSelector((state: RootState) => state.assembly.assembled);
const shipped = useSelector((state: RootState) => state.assembly.shipped);
}
const noOrderStyles = StyleSheet.create({
container: {
justifyContent: "center",
flex: 1,
rowGap: responsiveHeight(5)
},
title: {
textAlign: "center",
fontSize: responsiveWidth(5)
},
buttonWrapper: {
paddingHorizontal: responsiveWidth(20),
}
})
type OrderScreenProps = {
order: Order;
}
const OrderScreen: FC<OrderScreenProps> = ({order}) => {
const navigator = useNavigation();
const dispatch = useDispatch();
const [selectedProduct, setSelectedProduct] = useState(order.products[0]);
const [assemblyPeriod, setAssemblyPeriod] = useState<AssemblyPeriod>({started: new Date(), ended: new Date()})
const [acceptModalVisible, setAcceptModalVisible] = useState(false);
const [reprintModalVisible, setReprintModalVisible] = useState(false);
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}`;
}
const startAssembly = async () => {
setAssemblyStarted(true);
let intervalId = setInterval(() => {
setAssemblyPeriod(oldValue => ({...oldValue, ended: new Date()}))
}, 1000);
setAssemblyIntervalId(Number(intervalId))
};
useEffect(() => {
let newSelectedProduct = order.products.find(o => o.databaseId == selectedProduct.databaseId);
if (!newSelectedProduct) return;
setSelectedProduct(newSelectedProduct);
}, [order]);
useEffect(() => {
dispatch(closeLoadingModal())
}, []);
return (
<View style={styles.container}>
<View style={styles.productsContainer}>
<View style={styles.orderProductsListWrapper}>
<OrderProductsList products={order.products} onSelected={() => {
<OrderProductsList products={order.products} onSelected={(product) => {
if (!order) return;
setSelectedProduct(product)
}}/>
</View>
<View style={styles.imageWrapper}>
@@ -62,37 +122,68 @@ function OrderScreen() {
<DText>Артикул DENCO: {selectedProduct.dencoArticle}</DText>
<DText>Поставщик: {selectedProduct.supplierName}</DText>
<DText>Номер товара: {0}</DText>
</View>
<View style={styles.buttonsContainer}>
<BasicButton label={"Отметить как собранный"}
disabled={!selectedProduct.assembled}
onPress={()=>{
setOrder(oldValue => ({ ...oldValue }));
}}
/>
<BasicButton label={"Печать этикетки"}
disabled={Boolean(order.products.find(product => product.assembled))}/>
</View>
</View>
<DText>{}</DText>
<DTitle style={styles.contentTitle}>Сборка</DTitle>
<DText>Затрачено
времени: {prettyPrintMilliseconds(assemblyPeriod.ended.getTime() - assemblyPeriod.started.getTime())}</DText>
</View>
<View style={styles.buttonsContainer}>
{assemblyStarted ? <>
<BasicButton
containerStyle={styles.buttonContainer}
label={"Отметить как собранный"}
disabled={selectedProduct.assembled}
onPress={() => {
dispatch(setAssembled({orderProductId: selectedProduct.databaseId}))
}}
/>
<BasicButton
onPress={() => {
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))}/>
</>
: <BasicButton containerStyle={styles.buttonContainer}
onPress={() => setAcceptModalVisible(true)}
label={"Начать сборку"}/>}
</View>
</View>
<AcceptModal visible={acceptModalVisible}
text={`Вы уверены что хотите начать сборку заказа ${order.orderNumber}`}
onAccepted={() => {
setAcceptModalVisible(false);
startAssembly();
}}
onRefused={() => {
setAcceptModalVisible(false);
}}/>
</View>
)
}
const noOrderStyles = StyleSheet.create({
container: {
justifyContent: "center",
flex: 1,
rowGap: responsiveHeight(5)
},
title: {
textAlign: "center",
fontSize: responsiveWidth(5)
},
buttonWrapper: {
paddingHorizontal: responsiveWidth(20)
}
})
export const OrderScreenController: FC = () => {
const order: Order | undefined = useSelector((state: RootState) => state.assembly.order);
if (!order) return <NoOrderScreen/>
console.log(order)
return <OrderScreen order={order}/>
}
const styles = StyleSheet.create({
orderProductsListWrapper: {
flex: 0.5,
@@ -100,9 +191,8 @@ const styles = StyleSheet.create({
borderRadius: RFPercentage(3),
padding: RFPercentage(2),
},
buttonWrapper: {
buttonContainer: {
flex: 1,
backgroundColor: "red"
},
dataContainer: {
backgroundColor: "white",

View File

@@ -1,27 +1,18 @@
import {ActivityIndicator, FlatList, ScrollView, StyleSheet, View} from "react-native";
import {StyleSheet, View} from "react-native";
import SearchBar from "../../components/SearchBar/SearchBar";
import OrderCard from "../../components/OrderCard/OrderCard";
import {RFPercentage} from "react-native-responsive-fontsize";
import {responsiveHeight, responsiveWidth} from "react-native-responsive-dimensions";
import SortingButton from "../../components/SortingButton/SortingButton";
import {useDispatch, useSelector} from "react-redux";
import {enableDim, disableDim} from "../../features/interface/interfaceSlice";
import {BottomSheetModal} from "@gorhom/bottom-sheet";
import {useEffect, useMemo, useRef, useState} from "react";
import {RadioButton, Button} from "react-native-paper";
import DText from "../../components/DText/DText";
import {background, blue} from "../../css/colors";
import DTitle from "../../components/DTitle/DTitle";
import BasicButton from "../../components/BasicButton/BasicButton";
import {generateRandomOrders, Order} from "../../types/order";
import {useDispatch} from "react-redux";
import {useEffect, useRef, useState} from "react";
import {Order} from "../../types/order";
import {FlashList} from "@shopify/flash-list";
import SortingModal, {
SortingModalElement,
SortingModalHandles
} from "../../components/Modals/SortingModal/SortingModal";
import sortingModal from "../../components/Modals/SortingModal/SortingModal";
import flashListSeparator from "../../components/FlashListSeparator/FlashListSeparator";
import {RootState} from "../../redux/store";
import ordersApi from "../../api/ordersApi";
import {setOrder} from "../../features/assembly/assemblySlice";
import {NavigationProp, useNavigation} from "@react-navigation/native";
@@ -61,10 +52,7 @@ function OrdersScreen() {
data={orders}
keyExtractor={(item: Order) => item.orderNumber.toString()}
renderItem={({item}) =>
<OrderCard order={item} onSelect={(order) => {
dispatch(setOrder(order));
navigator.navigate("Home");
}}/>}
<OrderCard onSelect={(order) => dispatch(setOrder(order))} order={item}/>}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={true}
onEndReachedThreshold={0.1}

View File

@@ -17,16 +17,16 @@ class PrintingService {
this.port = port;
}
private async print(printer: string, type: string, bytes: Uint8Array): Promise<string> {
private async print(printer: string, type: string, bytes: Uint8Array): Promise<boolean> {
let response = await axios.post(`http://${this.apiUrl}:${this.port}/print/${printer}/${type}`, bytes.buffer);
return response.data;
return response.data.ok;
}
public async printPdf(printer: string, pdfBytes: Uint8Array): Promise<string> {
public async printPdf(printer: string, pdfBytes: Uint8Array): Promise<boolean> {
return this.print(printer, "pdf", pdfBytes);
}
public async printImage(printer: string, imageBytes: Uint8Array): Promise<string> {
public async printImage(printer: string, imageBytes: Uint8Array): Promise<boolean> {
return this.print(printer, "image", imageBytes);
}
}