ebanutsya
This commit is contained in:
@@ -14,6 +14,8 @@
|
||||
"dependencies": {
|
||||
"@expo/webpack-config": "^19.0.0",
|
||||
"@gorhom/bottom-sheet": "^4",
|
||||
"@react-native-community/datetimepicker": "7.2.0",
|
||||
"@react-native-picker/picker": "^2.5.1",
|
||||
"@react-navigation/bottom-tabs": "^6.5.8",
|
||||
"@react-navigation/native": "^6.1.7",
|
||||
"@reduxjs/toolkit": "^1.9.5",
|
||||
|
||||
@@ -16,7 +16,6 @@ export default function App() {
|
||||
})
|
||||
if (!fontsLoading)
|
||||
return <View><Text>Loading...</Text></View>
|
||||
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<GestureHandlerRootView style={{flex: 1}}>
|
||||
|
||||
@@ -4,8 +4,16 @@ import {Order} from "../types/order";
|
||||
const router = '/orders';
|
||||
|
||||
const ordersApi = {
|
||||
getOrders: async (lastId: number) => {
|
||||
|
||||
getOrders: async (page: number, orderBy: string, desc: boolean, shipmentDate: string, status: number): Promise<Order[]> => {
|
||||
const params = {
|
||||
page: page,
|
||||
orderBy: orderBy,
|
||||
desc: Number(desc), // Преобразование boolean в Number (0 или 1)
|
||||
status: status,
|
||||
shipmentDate: shipmentDate
|
||||
};
|
||||
let response = await apiClient.get(`${router}/getOrders`, {params})
|
||||
return response.data;
|
||||
},
|
||||
getOrdersBySupplierProduct: async (supplierProductId: number): Promise<Order[]> => {
|
||||
let response = await apiClient.get(`${router}/getBySupplierProductId?supplierProductId=${supplierProductId}`);
|
||||
|
||||
@@ -21,6 +21,7 @@ const ReprintModal: FC = () => {
|
||||
dispatch(closeReprintModal());
|
||||
dispatch(openLoadingModal());
|
||||
PrintingApi.getLabel(order.databaseId).then(pdfBytes => {
|
||||
|
||||
PrintingService.getInstance().printPdf(printerName, pdfBytes).then(r => {
|
||||
dispatch(closeLoadingModal());
|
||||
if (r) dispatch(closeReprintModal());
|
||||
|
||||
@@ -3,11 +3,22 @@ import {BottomSheetModal} from "@gorhom/bottom-sheet";
|
||||
import {disableDim, enableDim} from "../../../features/interface/interfaceSlice";
|
||||
import {StyleSheet, View} from "react-native";
|
||||
import BasicButton from "../../BasicButton/BasicButton";
|
||||
import {useDispatch} from "react-redux";
|
||||
import {useDispatch, useSelector} from "react-redux";
|
||||
import {RFPercentage} from "react-native-responsive-fontsize";
|
||||
import RadioGroup from 'react-native-radio-buttons-group';
|
||||
import {responsiveHeight, responsiveWidth} from "react-native-responsive-dimensions";
|
||||
import {blue} from "../../../css/colors";
|
||||
import {Picker} from "@react-native-picker/picker";
|
||||
import DateTimePicker from '@react-native-community/datetimepicker';
|
||||
import {RootState} from "../../../redux/store";
|
||||
import {
|
||||
closeOrdersFilterModal,
|
||||
OrderStatus,
|
||||
orderStatuses,
|
||||
setDesc,
|
||||
setOrderBy, setShipmentDate, setStatus
|
||||
} from "../../../features/ordersFilter/ordersFilterSlice";
|
||||
import {retry} from "@reduxjs/toolkit/query";
|
||||
|
||||
export type SortingModalHandles = {
|
||||
present: () => void;
|
||||
@@ -19,12 +30,7 @@ export type SortingModalElement = {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
type Props = {
|
||||
onChange: (sortingValue: string) => void;
|
||||
onClose: () => void;
|
||||
elements: SortingModalElement[];
|
||||
defaultElementId?: string;
|
||||
};
|
||||
|
||||
|
||||
const createRadioButton = (element: SortingModalElement) => {
|
||||
return {
|
||||
@@ -41,55 +47,101 @@ const createRadioButton = (element: SortingModalElement) => {
|
||||
};
|
||||
}
|
||||
|
||||
const SortingModal = forwardRef<SortingModalHandles, Props>((props: Props, ref) => {
|
||||
const {elements, onChange, onClose, defaultElementId = ""} = props;
|
||||
const snapPoints = useMemo(() => ['40%', '40%'], []);
|
||||
const SortingModal = () => {
|
||||
const state = useSelector((state: RootState) => state.ordersFilter);
|
||||
const elements = [
|
||||
{id: 'createdOnAsc', value: 'createdOnAsc', label: 'Дата создания по возрастанию'},
|
||||
{id: 'createdOnDesc', value: 'createdOnDesc', label: 'Дата создания по убыванию'},
|
||||
{id: 'shipmentDateAsc', value: 'shipmentDateAsc', label: 'Дата отгрузки по возрастанию'},
|
||||
{id: 'shipmentDateDesc', value: 'shipmentDateDesc', label: 'Дата отгрузки по убыванию'},
|
||||
];
|
||||
const [showShipmentPicker, setShowShipmentPicker] = useState(false);
|
||||
const snapPoints = useMemo(() => ['60%', '60%'], []);
|
||||
const dispatch = useDispatch();
|
||||
const modalRef = useRef<BottomSheetModal>(null);
|
||||
const dismiss = () => {
|
||||
if (!modalRef.current) return;
|
||||
dispatch(disableDim());
|
||||
modalRef.current.dismiss();
|
||||
onClose();
|
||||
}
|
||||
const present = () => {
|
||||
if (!modalRef.current) return;
|
||||
modalRef.current.present();
|
||||
dispatch(enableDim());
|
||||
}
|
||||
useImperativeHandle(ref, () => ({
|
||||
present: present,
|
||||
dismiss: dismiss
|
||||
}));
|
||||
const [selectedId, setSelectedId] = useState<string>(defaultElementId);
|
||||
useEffect(() => {
|
||||
onChange(selectedId);
|
||||
}, [selectedId]);
|
||||
if (state.isVisible) present();
|
||||
else dismiss();
|
||||
}, [state.isVisible]);
|
||||
return (
|
||||
<BottomSheetModal
|
||||
ref={modalRef}
|
||||
snapPoints={snapPoints}
|
||||
onDismiss={() => {
|
||||
dispatch(disableDim());
|
||||
dispatch(closeOrdersFilterModal())
|
||||
}}>
|
||||
<View style={styles.container}>
|
||||
<View style={styles.content}>
|
||||
<RadioGroup selectedId={selectedId}
|
||||
onPress={setSelectedId}
|
||||
<RadioGroup selectedId={state.orderBy + (state.desc ? "Desc" : "Asc")}
|
||||
onPress={(event) => {
|
||||
const orderRegex = /(Asc|Desc)$/;
|
||||
const orderMatch = event.match(orderRegex);
|
||||
if (!orderMatch) return;
|
||||
const isDesc = orderMatch[0] === 'Desc';
|
||||
const orderByField = event.replace(orderRegex, '');
|
||||
if (!["createdOn", "shipmentDate"].includes(orderByField)) return
|
||||
dispatch(setDesc(isDesc));
|
||||
dispatch(setOrderBy(orderByField as "createdOn" | "shipmentDate"));
|
||||
|
||||
}}
|
||||
containerStyle={styles.radioButtons}
|
||||
radioButtons={elements.map(createRadioButton)}/>
|
||||
|
||||
|
||||
<View style={{
|
||||
borderWidth: responsiveWidth(0.1),
|
||||
borderRadius: responsiveWidth(1)
|
||||
}}>
|
||||
|
||||
<Picker selectedValue={state.status}
|
||||
onValueChange={(value, event) => dispatch(setStatus(value))}>
|
||||
{orderStatuses.map((status) => {
|
||||
|
||||
return (
|
||||
<Picker.Item
|
||||
key={status.key}
|
||||
label={status.label}
|
||||
value={status.key}
|
||||
style={{fontSize: responsiveWidth(3)}}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</Picker>
|
||||
</View>
|
||||
<BasicButton onPress={() => setShowShipmentPicker(oldValue => !oldValue)}
|
||||
label={"Выбрать дату отгрузки"}/>
|
||||
{showShipmentPicker &&
|
||||
<DateTimePicker value={new Date(state.shipmentDate)}
|
||||
onChange={(event) => {
|
||||
if (!event.nativeEvent.timestamp) return;
|
||||
setShowShipmentPicker(false);
|
||||
if (event.type === 'set') {
|
||||
const selectedDate = new Date(event.nativeEvent.timestamp);
|
||||
dispatch(setShipmentDate(selectedDate.toISOString()));
|
||||
}
|
||||
}}/>}
|
||||
</View>
|
||||
|
||||
|
||||
<BasicButton label={"Закрыть"} style={styles.button} onPress={() => {
|
||||
dismiss();
|
||||
dispatch(closeOrdersFilterModal());
|
||||
}}/>
|
||||
|
||||
</View>
|
||||
</BottomSheetModal>
|
||||
);
|
||||
});
|
||||
|
||||
)
|
||||
};
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
width: "100%",
|
||||
@@ -97,13 +149,17 @@ const styles = StyleSheet.create({
|
||||
flex: 1,
|
||||
padding: RFPercentage(3),
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-between"
|
||||
justifyContent: "space-between",
|
||||
rowGap: responsiveHeight(1)
|
||||
},
|
||||
radioButtons: {
|
||||
alignItems: "flex-start"
|
||||
},
|
||||
content: {},
|
||||
content: {
|
||||
rowGap: responsiveHeight(1)
|
||||
},
|
||||
button: {
|
||||
|
||||
marginTop: "auto"
|
||||
},
|
||||
|
||||
|
||||
@@ -4,10 +4,11 @@ import {RFPercentage} from "react-native-responsive-fontsize";
|
||||
import DText from "../DText/DText";
|
||||
import {responsiveHeight, responsiveWidth} from "react-native-responsive-dimensions";
|
||||
import DTitle from "../DTitle/DTitle";
|
||||
import {Order} from "../../types/order";
|
||||
import {BaseMarketplace, Order} from "../../types/order";
|
||||
import OrderProductsList from "./OrderProductsList";
|
||||
import {useDispatch} from "react-redux";
|
||||
import {setOrder} from "../../features/assembly/assemblySlice";
|
||||
import {OrderStatus, OrderStatusDictionary} from "../../features/ordersFilter/ordersFilterSlice";
|
||||
|
||||
type Props = {
|
||||
onPress?: (event: GestureResponderEvent) => void
|
||||
@@ -15,9 +16,17 @@ type Props = {
|
||||
onSelect: (order: Order) => void
|
||||
|
||||
}
|
||||
|
||||
|
||||
const BaseMarketplaceIconDict = {
|
||||
[BaseMarketplace.Wildberries]: require('assets/icons/marketplaces/wildberries.png'),
|
||||
[BaseMarketplace.Ozon]: require('assets/icons/marketplaces/ozon.png'),
|
||||
[BaseMarketplace.YandexMarket]: require('assets/icons/marketplaces/yandex_market.png'),
|
||||
};
|
||||
const OrderCard: FC<Props> = ({onPress, onSelect, order}) => {
|
||||
|
||||
return (
|
||||
<TouchableOpacity onPress={()=>{
|
||||
<TouchableOpacity onPress={() => {
|
||||
if (onSelect) onSelect(order);
|
||||
|
||||
}}>
|
||||
@@ -26,7 +35,7 @@ const OrderCard: FC<Props> = ({onPress, onSelect, order}) => {
|
||||
<View style={styles.title}>
|
||||
|
||||
<DTitle>{order.orderNumber}</DTitle>
|
||||
<Image source={require('assets/icons/marketplaces/ozon.png')} style={styles.titleImage}/>
|
||||
<Image source={BaseMarketplaceIconDict[order.baseMarketplace]} style={styles.titleImage}/>
|
||||
</View>
|
||||
<DText>Селлер: {order.sellerName}</DText>
|
||||
<DText>Маркетплейс: {order.marketplaceName}</DText>
|
||||
@@ -34,7 +43,7 @@ const OrderCard: FC<Props> = ({onPress, onSelect, order}) => {
|
||||
</View>
|
||||
<View style={styles.descriptionStatus}>
|
||||
<DText>
|
||||
Ожидает сборки
|
||||
{OrderStatusDictionary[order.status as OrderStatus]}
|
||||
</DText>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -9,16 +9,16 @@ import {background} from "../../css/colors";
|
||||
|
||||
type ListProps = {
|
||||
products: OrderProduct[];
|
||||
onSelected: (product: OrderProduct) => void;
|
||||
onSelected: (product: number) => void;
|
||||
}
|
||||
type CardProps = {
|
||||
product: OrderProduct;
|
||||
onPress: (product: OrderProduct) => void;
|
||||
onPress: (product: number) => void;
|
||||
id: number;
|
||||
}
|
||||
const OrderProductCard: FC<CardProps> = ({product, onPress, id}) => {
|
||||
return (
|
||||
<TouchableOpacity onPress={() => onPress(product)}>
|
||||
<TouchableOpacity onPress={() => onPress(id)}>
|
||||
<View style={cardStyles.container}>
|
||||
<View style={cardStyles.content}>
|
||||
<DText>{id + 1}) {product.productName}</DText>
|
||||
|
||||
@@ -7,16 +7,15 @@ import SelectProductModal from "../Modals/SelectProductModal/SelectProductModal"
|
||||
import {SupplierProduct} from "../../types/supplierProduct";
|
||||
import barcodeApi from "../../api/barcodeApi";
|
||||
import {useDispatch, useSelector} from "react-redux";
|
||||
import {openScanModal} from "../../features/scanModal/scanModalSlice";
|
||||
import {openScanModal, setScannedData} from "../../features/scanModal/scanModalSlice";
|
||||
import {RootState} from "../../redux/store";
|
||||
|
||||
type Props = {
|
||||
onSearch: (text: string) => void;
|
||||
onSearch?: (text: string) => void;
|
||||
onSupplierProductSelected?: (supplierProduct: SupplierProduct) => void
|
||||
}
|
||||
const SearchBar: FC<Props> = ({onSearch, onSupplierProductSelected}) => {
|
||||
// const [isScanModalVisible, setIsScanModalVisible] = useState<boolean>(false);
|
||||
// const [, setSelectProductModalVisible] = useState(false);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const [searchInput, setSearchInput] = useState<string>("");
|
||||
const textInputRef = useRef<TextInput>(null);
|
||||
@@ -26,19 +25,19 @@ const SearchBar: FC<Props> = ({onSearch, onSupplierProductSelected}) => {
|
||||
useEffect(() => {
|
||||
if (!scannedData) return;
|
||||
barcodeApi.searchProducts(scannedData).then((response) => {
|
||||
console.log("Response: " + response);
|
||||
setProducts(response)
|
||||
});
|
||||
}, [scannedData]);
|
||||
const selectProductModalVisible = products.length > 0;
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
|
||||
<SelectProductModal visible={selectProductModalVisible} products={products} onSelected={(product) => {
|
||||
if (onSupplierProductSelected) onSupplierProductSelected(product);
|
||||
setProducts([]);
|
||||
dispatch(setScannedData(undefined));
|
||||
}}/>
|
||||
<BasicButton onPress={() => {
|
||||
if (!onSearch) return;
|
||||
onSearch(searchInput);
|
||||
if (textInputRef.current) {
|
||||
textInputRef.current.clear();
|
||||
@@ -77,7 +76,7 @@ const styles = StyleSheet.create({
|
||||
borderTopRightRadius: responsiveWidth(1),
|
||||
borderBottomRightRadius: responsiveWidth(1),
|
||||
paddingHorizontal: responsiveWidth(5),
|
||||
height:'100%'
|
||||
height: '100%'
|
||||
},
|
||||
scanImageWrapper: {
|
||||
paddingHorizontal: responsiveWidth(1),
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import {createSlice, PayloadAction} from "@reduxjs/toolkit";
|
||||
import {Order} from "../../types/order";
|
||||
import {Order, OrderProduct} from "../../types/order";
|
||||
import {Assembly, ASSEMBLY_STATE} from "../../types/assembly";
|
||||
import selectProductElement from "../../components/Modals/SelectProductModal/SelectProductElement";
|
||||
|
||||
|
||||
export interface AssemblyState {
|
||||
order?: Order;
|
||||
initialOrder?: Order;
|
||||
assembly?: Assembly;
|
||||
localState?: ASSEMBLY_STATE
|
||||
selectedProductId?: number;
|
||||
localState?: ASSEMBLY_STATE;
|
||||
selectedProduct?: OrderProduct
|
||||
}
|
||||
|
||||
const initialState: AssemblyState = {
|
||||
@@ -18,7 +22,9 @@ export const assembly = createSlice({
|
||||
initialState,
|
||||
reducers: {
|
||||
setOrder: (state, action) => {
|
||||
state.initialOrder = action.payload;
|
||||
state.order = action.payload;
|
||||
state.selectedProductId = 0;
|
||||
},
|
||||
setAssembly: (state, action: PayloadAction<Assembly>) => {
|
||||
state.assembly = action.payload;
|
||||
@@ -56,6 +62,7 @@ export const assembly = createSlice({
|
||||
product.assembled = true;
|
||||
}
|
||||
});
|
||||
state.selectedProduct = state.order.products.find(product => product.databaseId == action.payload.orderProductId);
|
||||
// If all products is assembled
|
||||
if (!state.order.products.find(product => !product.assembled)) {
|
||||
state.assembly.state = ASSEMBLY_STATE.ALL_PRODUCTS_ASSEMBLED;
|
||||
@@ -72,9 +79,23 @@ export const assembly = createSlice({
|
||||
product.shipped = true;
|
||||
}
|
||||
});
|
||||
state.selectedProduct = state.order.products.find(product => product.databaseId == action.payload.orderProductId);
|
||||
|
||||
},
|
||||
selectProduct: (state, action: PayloadAction<number>) => {
|
||||
if (!state.order) return;
|
||||
state.selectedProduct = state.order.products[action.payload];
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export const {setOrder, setAssembled, setAssembly, startAssembly, endAssembly, confirmAssembly} = assembly.actions;
|
||||
export const {
|
||||
setOrder,
|
||||
setAssembled,
|
||||
selectProduct,
|
||||
setAssembly,
|
||||
startAssembly,
|
||||
endAssembly,
|
||||
confirmAssembly
|
||||
} = assembly.actions;
|
||||
export default assembly.reducer;
|
||||
100
src/features/ordersFilter/ordersFilterSlice.ts
Normal file
100
src/features/ordersFilter/ordersFilterSlice.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import {createSlice, PayloadAction} from "@reduxjs/toolkit";
|
||||
|
||||
export enum OrderStatus {
|
||||
ALL = -1,
|
||||
AWAITING_PACKAGING,
|
||||
AWAITING_DELIVER,
|
||||
DELIVERING,
|
||||
DELIVERED,
|
||||
CANCELLED,
|
||||
PARTIALLY_RETURNED,
|
||||
FULLY_RETURNED,
|
||||
DISPUTED
|
||||
}
|
||||
|
||||
export const orderStatuses = [
|
||||
{key: OrderStatus.ALL, label: 'Все'},
|
||||
{key: OrderStatus.AWAITING_PACKAGING, label: 'Ожидает сборки'},
|
||||
{key: OrderStatus.AWAITING_DELIVER, label: 'Ожидает отгрузки'},
|
||||
{key: OrderStatus.DELIVERING, label: 'Доставляется'},
|
||||
{key: OrderStatus.DELIVERED, label: 'Доставлен'},
|
||||
{key: OrderStatus.CANCELLED, label: 'Отменен'},
|
||||
{key: OrderStatus.PARTIALLY_RETURNED, label: 'Частичный возврат'},
|
||||
{key: OrderStatus.FULLY_RETURNED, label: 'Полный возврат'},
|
||||
{key: OrderStatus.DISPUTED, label: 'Спорный'},
|
||||
];
|
||||
export const OrderStatusDictionary: { [key in OrderStatus]: string } = {
|
||||
[OrderStatus.ALL]: 'Все',
|
||||
[OrderStatus.AWAITING_PACKAGING]: 'Ожидает сборки',
|
||||
[OrderStatus.AWAITING_DELIVER]: 'Ожидает отгрузки',
|
||||
[OrderStatus.DELIVERING]: 'Доставляется',
|
||||
[OrderStatus.DELIVERED]: 'Доставлен',
|
||||
[OrderStatus.CANCELLED]: 'Отменен',
|
||||
[OrderStatus.PARTIALLY_RETURNED]: 'Частичный возврат',
|
||||
[OrderStatus.FULLY_RETURNED]: 'Полный возврат',
|
||||
[OrderStatus.DISPUTED]: 'Спорный',
|
||||
};
|
||||
|
||||
export interface OrdersFilterState {
|
||||
isVisible: boolean;
|
||||
orderBy: "createdOn" | "shipmentDate";
|
||||
desc: boolean;
|
||||
status: OrderStatus;
|
||||
shipmentDate: string;
|
||||
page: number;
|
||||
}
|
||||
|
||||
const initialState: OrdersFilterState = {
|
||||
isVisible: false,
|
||||
orderBy: "createdOn",
|
||||
desc: true,
|
||||
shipmentDate: (new Date()).toISOString(),
|
||||
status: OrderStatus.AWAITING_PACKAGING,
|
||||
page: 0
|
||||
}
|
||||
|
||||
export const ordersFilterSlice = createSlice({
|
||||
name: 'ordersFilter',
|
||||
initialState,
|
||||
reducers: {
|
||||
openOrdersFilterModal: (state) => {
|
||||
state.isVisible = true
|
||||
},
|
||||
closeOrdersFilterModal: (state) => {
|
||||
state.isVisible = false
|
||||
},
|
||||
setOrderBy: (state, action: PayloadAction<"createdOn" | "shipmentDate">) => {
|
||||
state.orderBy = action.payload;
|
||||
},
|
||||
setDesc: (state, action: PayloadAction<boolean>) => {
|
||||
state.desc = action.payload;
|
||||
},
|
||||
setStatus: (state, action: PayloadAction<OrderStatus>) => {
|
||||
state.status = action.payload;
|
||||
},
|
||||
setShipmentDate: (state, action: PayloadAction<string>) => {
|
||||
state.shipmentDate = action.payload;
|
||||
},
|
||||
setPage: (state, action: PayloadAction<number>) => {
|
||||
state.page = action.payload;
|
||||
},
|
||||
nextPage: (state) => {
|
||||
state.page = state.page + 1
|
||||
},
|
||||
refreshPagination: (state) => {
|
||||
state.page = 0
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export const {
|
||||
openOrdersFilterModal,
|
||||
closeOrdersFilterModal,
|
||||
refreshPagination,
|
||||
nextPage,
|
||||
setOrderBy,
|
||||
setDesc,
|
||||
setStatus,
|
||||
setShipmentDate
|
||||
} = ordersFilterSlice.actions;
|
||||
export default ordersFilterSlice.reducer;
|
||||
@@ -8,6 +8,7 @@ import imageZoomModalReducer from 'features/imageZoomModal/loadingModalSlice';
|
||||
import assemblyReducer from 'features/assembly/assemblySlice';
|
||||
import printingReducer from 'features/printing/printingSlice';
|
||||
import reprintModalReducer from 'features/reprintModal/reprintModalSlice';
|
||||
import ordersFilterReducer from 'features/ordersFilter/ordersFilterSlice';
|
||||
import {useDispatch} from "react-redux";
|
||||
|
||||
export const store = configureStore({
|
||||
@@ -19,7 +20,8 @@ export const store = configureStore({
|
||||
assembly: assemblyReducer,
|
||||
printing: printingReducer,
|
||||
reprintModal: reprintModalReducer,
|
||||
imageZoomModal: imageZoomModalReducer
|
||||
imageZoomModal: imageZoomModalReducer,
|
||||
ordersFilter: ordersFilterReducer
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,15 +1,68 @@
|
||||
import {Button, Text, View} from "react-native";
|
||||
import {Button, StyleSheet, Text, View} from "react-native";
|
||||
import {useAppDispatch} from "../../redux/store";
|
||||
import * as process from "process";
|
||||
import {responsiveHeight, responsiveWidth} from "react-native-responsive-dimensions";
|
||||
import SearchBar from "../../components/SearchBar/SearchBar";
|
||||
import {useDispatch} from "react-redux";
|
||||
import {NavigationProp, useNavigation} from "@react-navigation/native";
|
||||
import {TabNavigatorParamList} from "../MainScreen/MainScreen";
|
||||
import {FlashList} from "@shopify/flash-list";
|
||||
import {Order} from "../../types/order";
|
||||
import OrderCard from "../../components/OrderCard/OrderCard";
|
||||
import {setOrder} from "../../features/assembly/assemblySlice";
|
||||
import flashListSeparator from "../../components/FlashListSeparator/FlashListSeparator";
|
||||
import {useEffect, useState} from "react";
|
||||
import {openScanModal} from "../../features/scanModal/scanModalSlice";
|
||||
import ordersApi from "../../api/ordersApi";
|
||||
|
||||
function BarcodeScreen() {
|
||||
const dispatch = useDispatch();
|
||||
const navigator = useNavigation<NavigationProp<TabNavigatorParamList, 'Home'>>();
|
||||
const [orders, setOrders] = useState<Order[]>([]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(openScanModal());
|
||||
}, []);
|
||||
return (
|
||||
<View>
|
||||
<Text style={{fontSize: 36}}>Barcode</Text>
|
||||
<View style={styles.container}>
|
||||
<SearchBar onSupplierProductSelected={product => {
|
||||
ordersApi.getOrdersBySupplierProduct(product.supplierProductId).then(setOrders)
|
||||
}}/>
|
||||
<View style={styles.content}>
|
||||
<FlashList
|
||||
|
||||
keyboardShouldPersistTaps={"never"}
|
||||
data={orders}
|
||||
keyExtractor={(item: Order) => item.orderNumber.toString()}
|
||||
renderItem={({item}) =>
|
||||
<OrderCard onSelect={(order) => {
|
||||
dispatch(setOrder(order));
|
||||
navigator.navigate("Home");
|
||||
}} order={item}/>}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
showsVerticalScrollIndicator={true}
|
||||
onEndReachedThreshold={0.1}
|
||||
estimatedItemSize={720}
|
||||
ItemSeparatorComponent={flashListSeparator}
|
||||
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
paddingHorizontal: responsiveWidth(5),
|
||||
rowGap: responsiveHeight(2),
|
||||
fontWeight: '500'
|
||||
},
|
||||
content: {
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
export default BarcodeScreen;
|
||||
@@ -18,6 +18,7 @@ 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";
|
||||
import SortingModal from "../../components/Modals/SortingModal/SortingModal";
|
||||
|
||||
function CommonPage() {
|
||||
const dim = useSelector((state: RootState) => state.interface.dim);
|
||||
@@ -62,6 +63,7 @@ function CommonPage() {
|
||||
<ScanModal/>
|
||||
<ReprintModal/>
|
||||
<ImageZoomModal/>
|
||||
<SortingModal/>
|
||||
<Toast config={toastConfig}/>
|
||||
</View>
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import printingApi from "../../api/printingApi";
|
||||
import PrintingService from "../../utils/PrintingService";
|
||||
import OrderProductsList from "../../components/OrderCard/OrderProductsList";
|
||||
import {
|
||||
confirmAssembly, endAssembly,
|
||||
confirmAssembly, endAssembly, selectProduct,
|
||||
setAssembled,
|
||||
setAssembly,
|
||||
setOrder,
|
||||
@@ -82,9 +82,9 @@ const OrderScreen: FC<OrderScreenProps> = ({order}) => {
|
||||
const dispatch = useDispatch();
|
||||
const assembly = useSelector((state: RootState) => state.assembly.assembly);
|
||||
const assemblyState = useSelector((state: RootState) => state.assembly.localState);
|
||||
const initialOrder = useSelector((state: RootState) => state.assembly.initialOrder);
|
||||
|
||||
const [selectedProduct, setSelectedProduct] = useState(order.products[0]);
|
||||
|
||||
const selectedProduct = useSelector((state: RootState) => state.assembly.selectedProduct);
|
||||
const [acceptModalVisible, setAcceptModalVisible] = useState(false);
|
||||
const [skipConfirmButtonVisible, setSkipConfirmButtonVisible] = useState(false);
|
||||
const _confirmAssembly = async () => {
|
||||
@@ -113,11 +113,13 @@ const OrderScreen: FC<OrderScreenProps> = ({order}) => {
|
||||
onPress={() => setAcceptModalVisible(true)}
|
||||
label={"Начать сборку"}/>)
|
||||
case ASSEMBLY_STATE.ASSEMBLING_PRODUCTS:
|
||||
|
||||
return (<BasicButton
|
||||
containerStyle={styles.buttonContainer}
|
||||
label={"Отметить как собранный"}
|
||||
disabled={selectedProduct.assembled}
|
||||
disabled={selectedProduct?.assembled}
|
||||
onPress={() => {
|
||||
if (!selectedProduct) return;
|
||||
dispatch(setAssembled({orderProductId: selectedProduct.databaseId}))
|
||||
}}
|
||||
/>)
|
||||
@@ -183,10 +185,10 @@ const OrderScreen: FC<OrderScreenProps> = ({order}) => {
|
||||
dispatch(setLoadingText('Идет печать этикетки...'))
|
||||
dispatch(openLoadingModal())
|
||||
printingApi.getLabel(order.databaseId).then(pdfData => {
|
||||
printingService.getInstance().printPdf('ozon', pdfData).then((response) => {
|
||||
printingService.getInstance().printPdf('wildberries', pdfData).then((response) => {
|
||||
dispatch(closeLoadingModal());
|
||||
if (response) return;
|
||||
dispatch(setPrinterName({printerName: 'ozon'}));
|
||||
dispatch(setPrinterName({printerName: 'wildberries'}));
|
||||
dispatch(openReprintModal());
|
||||
});
|
||||
})
|
||||
@@ -204,23 +206,27 @@ const OrderScreen: FC<OrderScreenProps> = ({order}) => {
|
||||
});
|
||||
|
||||
}, [assemblyState]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!order) return;
|
||||
dispatch(selectProduct(0));
|
||||
}, [initialOrder]);
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={styles.productsContainer}>
|
||||
<View style={styles.orderProductsListWrapper}>
|
||||
<OrderProductsList products={order.products} onSelected={(product) => {
|
||||
<OrderProductsList products={order.products} onSelected={(productId) => {
|
||||
if (!order) return;
|
||||
setSelectedProduct(product)
|
||||
dispatch(selectProduct(productId))
|
||||
}}/>
|
||||
</View>
|
||||
|
||||
<TouchableOpacity style={styles.imageWrapper} onPress={()=>{
|
||||
<TouchableOpacity style={styles.imageWrapper} onPress={() => {
|
||||
if (!selectedProduct) return;
|
||||
dispatch(setImages([selectedProduct.imageUrl]));
|
||||
dispatch(openImageZoomModal());
|
||||
}}>
|
||||
<View style={{flex: 1}}>
|
||||
<Image style={styles.image} source={{uri: selectedProduct.imageUrl}}/>
|
||||
<Image style={styles.image} source={{uri: selectedProduct?.imageUrl}}/>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
|
||||
@@ -233,11 +239,11 @@ const OrderScreen: FC<OrderScreenProps> = ({order}) => {
|
||||
<DText>Селлер: {order.sellerName}</DText>
|
||||
<DText>{}</DText>
|
||||
<DTitle style={styles.contentTitle}>Товар</DTitle>
|
||||
<DText>Артикул DENCO: {selectedProduct.dencoArticle}</DText>
|
||||
<DText>Поставщик: {selectedProduct.supplierName}</DText>
|
||||
<DText>Артикул DENCO: {selectedProduct?.dencoArticle}</DText>
|
||||
<DText>Поставщик: {selectedProduct?.supplierName}</DText>
|
||||
<DText>Номер товара: {0}</DText>
|
||||
<DText>{}</DText>
|
||||
<DTitle style={styles.contentTitle}>Сборка</DTitle>
|
||||
{/*<DText>{}</DText>*/}
|
||||
{/*<DTitle style={styles.contentTitle}>Сборка</DTitle>*/}
|
||||
|
||||
</View>
|
||||
<View style={styles.buttonsContainer}>
|
||||
@@ -249,7 +255,6 @@ const OrderScreen: FC<OrderScreenProps> = ({order}) => {
|
||||
onAccepted={() => {
|
||||
setAcceptModalVisible(false);
|
||||
assemblyApi.create(order.databaseId).then(creationResult => {
|
||||
console.log(creationResult.message)
|
||||
Toast.show({
|
||||
type: creationResult.ok ? 'success' : 'error',
|
||||
text1: 'Создание сборки',
|
||||
@@ -273,7 +278,6 @@ const OrderScreen: FC<OrderScreenProps> = ({order}) => {
|
||||
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}/>
|
||||
}
|
||||
|
||||
|
||||
41
src/screens/OrderScreen/useOrders.tsx
Normal file
41
src/screens/OrderScreen/useOrders.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import {Order} from "../../types/order";
|
||||
import ordersApi from "../../api/ordersApi";
|
||||
import {setOrder} from "../../features/assembly/assemblySlice";
|
||||
import {RootState} from "../../redux/store";
|
||||
import {useDispatch, useSelector} from "react-redux";
|
||||
import {refreshPagination} from "../../features/ordersFilter/ordersFilterSlice";
|
||||
|
||||
|
||||
const useOrders = () => {
|
||||
const dispatch = useDispatch();
|
||||
const [orders, setOrders] = useState<Order[]>([]);
|
||||
const [isRefreshing, setIsRefreshing] = useState(false);
|
||||
const filterState = useSelector((state: RootState) => state.ordersFilter);
|
||||
const refresh = () => {
|
||||
if (filterState.page == 0) return;
|
||||
setIsRefreshing(true);
|
||||
setOrders([]);
|
||||
dispatch(refreshPagination());
|
||||
}
|
||||
useEffect(() => {
|
||||
if (filterState.isVisible) return;
|
||||
|
||||
ordersApi.getOrders(filterState.page, filterState.orderBy, filterState.desc, filterState.shipmentDate, filterState.status).then(response => {
|
||||
setOrders(oldOrders => [...oldOrders, ...response]);
|
||||
setIsRefreshing(false);
|
||||
});
|
||||
}, [filterState.page]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (filterState.isVisible) return;
|
||||
setOrders([]);
|
||||
setIsRefreshing(true);
|
||||
dispatch(refreshPagination());
|
||||
}, [filterState.orderBy, filterState.desc, filterState.status, filterState.shipmentDate, filterState.isVisible]);
|
||||
|
||||
return {refresh, isRefreshing, orders};
|
||||
}
|
||||
|
||||
export default useOrders;
|
||||
@@ -1,5 +1,4 @@
|
||||
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";
|
||||
@@ -9,66 +8,50 @@ 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 flashListSeparator from "../../components/FlashListSeparator/FlashListSeparator";
|
||||
import ordersApi from "../../api/ordersApi";
|
||||
import {setOrder} from "../../features/assembly/assemblySlice";
|
||||
import {NavigationProp, useNavigation} from "@react-navigation/native";
|
||||
import {TabNavigatorParamList} from "../MainScreen/MainScreen";
|
||||
import useOrders from "../OrderScreen/useOrders";
|
||||
import {nextPage, openOrdersFilterModal} from "../../features/ordersFilter/ordersFilterSlice";
|
||||
|
||||
function OrdersScreen() {
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const navigator = useNavigation<NavigationProp<TabNavigatorParamList, 'Home'>>();
|
||||
const {refresh, isRefreshing, orders} = useOrders();
|
||||
|
||||
const [orders, setOrders] = useState<Order[]>([]);
|
||||
const sortingModalRef = useRef<SortingModalHandles | null>(null);
|
||||
const defaultSortingValue = 'createdOnAsc';
|
||||
const sortingModalElements: SortingModalElement[] = [
|
||||
{id: 'createdOnAsc', value: 'createdOnAsc', label: 'Дата создания по убыванию'},
|
||||
{id: 'createdOnDesc', value: 'createdOnDesc', label: 'Дата создания по возрастанию'},
|
||||
{id: 'shipmentDateAsc', value: 'shipmentDateAsc', label: 'Дата отгрузки по убыванию'},
|
||||
{id: 'shipmentDateDesc', value: 'shipmentDateDesc', label: 'Дата отгрузки по возрастанию'},
|
||||
];
|
||||
|
||||
const [sortingValue, setSortingValue] = useState(defaultSortingValue);
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<SearchBar onSearch={() => {
|
||||
}} onSupplierProductSelected={product => {
|
||||
ordersApi.getOrdersBySupplierProduct(product.supplierProductId).then(setOrders)
|
||||
}}/>
|
||||
|
||||
<View style={styles.sortingButtonWrapper}>
|
||||
<SortingButton onPress={() => {
|
||||
if (!sortingModalRef.current) return;
|
||||
sortingModalRef.current.present();
|
||||
dispatch(openOrdersFilterModal());
|
||||
}}/>
|
||||
</View>
|
||||
<View style={styles.content}>
|
||||
<FlashList
|
||||
<FlashList onRefresh={refresh}
|
||||
refreshing={isRefreshing}
|
||||
keyboardShouldPersistTaps={"never"}
|
||||
data={orders}
|
||||
keyExtractor={(item: Order) => item.orderNumber.toString()}
|
||||
keyExtractor={(item: Order) => item.databaseId.toString()}
|
||||
renderItem={({item}) =>
|
||||
<OrderCard onSelect={(order) => dispatch(setOrder(order))} order={item}/>}
|
||||
<OrderCard onSelect={(order) => {
|
||||
dispatch(setOrder(order));
|
||||
navigator.navigate("Home");
|
||||
}} order={item}/>}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
showsVerticalScrollIndicator={true}
|
||||
onEndReachedThreshold={0.1}
|
||||
onEndReachedThreshold={0.5}
|
||||
estimatedItemSize={720}
|
||||
onEndReached={() => {
|
||||
if (!orders.length) return;
|
||||
dispatch(nextPage());
|
||||
}}
|
||||
ItemSeparatorComponent={flashListSeparator}
|
||||
|
||||
/>
|
||||
<SortingModal onChange={setSortingValue}
|
||||
onClose={() => {
|
||||
}}
|
||||
ref={sortingModalRef}
|
||||
elements={sortingModalElements}
|
||||
defaultElementId={defaultSortingValue}
|
||||
/>
|
||||
</View>
|
||||
|
||||
</View>
|
||||
|
||||
3
src/screens/OrdersScreen/useOrdersFilter.tsx
Normal file
3
src/screens/OrdersScreen/useOrdersFilter.tsx
Normal file
@@ -0,0 +1,3 @@
|
||||
const useOrdersFilter = () => {
|
||||
|
||||
}
|
||||
@@ -10,10 +10,19 @@ export type OrderProduct = {
|
||||
|
||||
};
|
||||
|
||||
export enum BaseMarketplace {
|
||||
Wildberries,
|
||||
Ozon,
|
||||
YandexMarket
|
||||
}
|
||||
|
||||
export type Order = {
|
||||
databaseId: number;
|
||||
orderNumber: string;
|
||||
marketplaceName: string;
|
||||
baseMarketplace: BaseMarketplace;
|
||||
sellerName: string;
|
||||
products: OrderProduct[];
|
||||
status: number;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user