feat: implement filter state management and enhance order fetching in BarcodeScreen
This commit is contained in:
@@ -17,7 +17,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@expo/webpack-config": "^19.0.1",
|
"@expo/webpack-config": "^19.0.1",
|
||||||
"@gorhom/bottom-sheet": "^4.6.4",
|
"@gorhom/bottom-sheet": "^5.1.6",
|
||||||
"@react-native-community/datetimepicker": "8.3.0",
|
"@react-native-community/datetimepicker": "8.3.0",
|
||||||
"@react-native-picker/picker": "2.11.0",
|
"@react-native-picker/picker": "2.11.0",
|
||||||
"@react-navigation/bottom-tabs": "^6.6.1",
|
"@react-navigation/bottom-tabs": "^6.6.1",
|
||||||
@@ -43,6 +43,7 @@
|
|||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"react": "19.0.0",
|
"react": "19.0.0",
|
||||||
"react-dom": "19.0.0",
|
"react-dom": "19.0.0",
|
||||||
|
"react-hook-form": "^7.57.0",
|
||||||
"react-native": "0.79.2",
|
"react-native": "0.79.2",
|
||||||
"react-native-animatable": "^1.4.0",
|
"react-native-animatable": "^1.4.0",
|
||||||
"react-native-gesture-handler": "~2.24.0",
|
"react-native-gesture-handler": "~2.24.0",
|
||||||
@@ -50,6 +51,7 @@
|
|||||||
"react-native-image-zoom-viewer": "^3.0.1",
|
"react-native-image-zoom-viewer": "^3.0.1",
|
||||||
"react-native-modal": "^13.0.1",
|
"react-native-modal": "^13.0.1",
|
||||||
"react-native-paper": "^5.12.5",
|
"react-native-paper": "^5.12.5",
|
||||||
|
"react-native-picker-select": "^9.3.1",
|
||||||
"react-native-progress": "^5.0.1",
|
"react-native-progress": "^5.0.1",
|
||||||
"react-native-radio-buttons-group": "^3.1.0",
|
"react-native-radio-buttons-group": "^3.1.0",
|
||||||
"react-native-reanimated": "~3.17.4",
|
"react-native-reanimated": "~3.17.4",
|
||||||
|
|||||||
@@ -12,10 +12,9 @@ export default function App() {
|
|||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<GestureHandlerRootView style={{flex: 1}}>
|
<GestureHandlerRootView style={{flex: 1}}>
|
||||||
<BottomSheetModalProvider>
|
<BottomSheetModalProvider>
|
||||||
<CommonPage/>
|
<CommonPage/>
|
||||||
</BottomSheetModalProvider>
|
</BottomSheetModalProvider>
|
||||||
</GestureHandlerRootView>
|
</GestureHandlerRootView>
|
||||||
|
|
||||||
</Provider>
|
</Provider>
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
import apiClient from "./apiClient";
|
import apiClient from "./apiClient";
|
||||||
import {Order} from "../types/order";
|
import {Order} from "../types/order";
|
||||||
import * as inspector from "inspector";
|
|
||||||
import {OrderStatus} from "../features/ordersFilter/ordersFilterSlice";
|
|
||||||
import {ShippingWarehouse} from "../types/shippingWarehouse";
|
|
||||||
import {City} from "../types/city";
|
|
||||||
|
|
||||||
const router = '/orders';
|
const router = '/orders';
|
||||||
|
|
||||||
const ordersApi = {
|
const ordersApi = {
|
||||||
getOrders: async (page: number, orderBy: string, desc: boolean, shipmentDate: string, status: number, shipmentWarehouseId: number): Promise<Order[]> => {
|
getOrders: async ({page, orderBy, desc, status, shipmentDate, shipmentWarehouseId}: {
|
||||||
|
page: number,
|
||||||
|
orderBy: string,
|
||||||
|
desc: boolean,
|
||||||
|
shipmentDate: string,
|
||||||
|
status: number,
|
||||||
|
shipmentWarehouseId: number
|
||||||
|
}): Promise<Order[]> => {
|
||||||
const params = {
|
const params = {
|
||||||
page: page,
|
page: page,
|
||||||
orderBy: orderBy,
|
orderBy: orderBy,
|
||||||
@@ -22,18 +25,14 @@ const ordersApi = {
|
|||||||
},
|
},
|
||||||
getOrdersByProduct: async (params: {
|
getOrdersByProduct: async (params: {
|
||||||
productId: number,
|
productId: number,
|
||||||
orderBy: "createdOn" | "shipmentDate",
|
orderBy: string,
|
||||||
desc: boolean,
|
desc: boolean,
|
||||||
status: OrderStatus,
|
shippingWarehouseId: number,
|
||||||
shipmentDate: string,
|
|
||||||
shippingWarehouse: ShippingWarehouse,
|
|
||||||
city: City
|
|
||||||
}): Promise<Order[]> => {
|
}): Promise<Order[]> => {
|
||||||
let response = await apiClient.get(`${router}/getByProductId`, {
|
let response = await apiClient.get(`${router}/getByProductId`, {
|
||||||
params: {
|
params: {
|
||||||
...params,
|
...params,
|
||||||
shippingWarehouse: params.shippingWarehouse.id,
|
shippingWarehouse: params.shippingWarehouseId,
|
||||||
city: params.city.id
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return response.data;
|
return response.data;
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ const Hyperlink: React.FC<HyperlinkProps> = ({url, children}) => {
|
|||||||
if (supported) {
|
if (supported) {
|
||||||
Linking.openURL(url);
|
Linking.openURL(url);
|
||||||
} else {
|
} else {
|
||||||
console.log("Don't know how to open URI: " + url);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ const ImageZoomModal: FC = () => {
|
|||||||
</View>
|
</View>
|
||||||
<ImageViewer
|
<ImageViewer
|
||||||
imageUrls={imageUrls.map(imageUrl => {
|
imageUrls={imageUrls.map(imageUrl => {
|
||||||
console.log(imageUrl)
|
|
||||||
return {
|
return {
|
||||||
url: imageUrl
|
url: imageUrl
|
||||||
}
|
}
|
||||||
|
|||||||
19
src/components/Modals/SortingModal/SortingButton.tsx
Normal file
19
src/components/Modals/SortingModal/SortingButton.tsx
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import {View} from "react-native";
|
||||||
|
import {RadioButton} from "react-native-paper";
|
||||||
|
import DText from "../../DText/DText";
|
||||||
|
import {FC} from "react";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SortingButton: FC<Props> = ({value, label}) => {
|
||||||
|
return (<View style={{display: "flex", flexDirection: "row", alignItems: "center"}}>
|
||||||
|
<RadioButton value={value}/>
|
||||||
|
<DText>{label}</DText>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SortingButton;
|
||||||
24
src/components/Modals/SortingModal/SortingButtons.tsx
Normal file
24
src/components/Modals/SortingModal/SortingButtons.tsx
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import {ControllerRenderProps} from 'react-hook-form';
|
||||||
|
import {FilterState} from "../../../features/filterSlice/filterSlice";
|
||||||
|
import {FC} from "react";
|
||||||
|
import {RadioButton} from "react-native-paper";
|
||||||
|
import SortingButton from "./SortingButton";
|
||||||
|
import {View} from 'react-native';
|
||||||
|
|
||||||
|
type Props = ControllerRenderProps<FilterState, "sorting">;
|
||||||
|
|
||||||
|
|
||||||
|
const SortingButtons: FC<Props> = ({value, onChange, disabled}) => {
|
||||||
|
return (
|
||||||
|
<RadioButton.Group value={value} onValueChange={onChange}>
|
||||||
|
<View style={{display: "flex", flexDirection: "column"}}>
|
||||||
|
<SortingButton value={"shipment_date_desc"} label={"Дата отгрузки по возрастанию"}/>
|
||||||
|
<SortingButton value={"shipment_date_asc"} label={"Дата отгрузки по убыванию"}/>
|
||||||
|
<SortingButton value={"created_date_desc"} label={"Дата создание по возрастанию"}/>
|
||||||
|
<SortingButton value={"created_date_asc"} label={"Дата создания по убыванию"}/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
</RadioButton.Group>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default SortingButtons;
|
||||||
@@ -1,185 +1,37 @@
|
|||||||
import {FC, forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState} from "react";
|
import {useEffect, useRef} from "react";
|
||||||
import {BottomSheetModal} from "@gorhom/bottom-sheet";
|
import {BottomSheetModal, BottomSheetView} from "@gorhom/bottom-sheet";
|
||||||
import {disableDim, enableDim} from "../../../features/interface/interfaceSlice";
|
import {useSelector} from "react-redux";
|
||||||
import {StyleSheet, View} from "react-native";
|
import {RootState, useAppDispatch} from "../../../redux/store";
|
||||||
import BasicButton from "../../BasicButton/BasicButton";
|
import {closeFilter} from "../../../features/filterSlice/filterSlice";
|
||||||
import {useDispatch, useSelector} from "react-redux";
|
import SortingModalBody from "./SortingModalBody";
|
||||||
import {RFPercentage} from "react-native-responsive-fontsize";
|
import {View} from "react-native";
|
||||||
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,
|
|
||||||
orderStatuses, setCity,
|
|
||||||
setDesc,
|
|
||||||
setOrderBy, setShipmentDate, setShippingWarehouse, setStatus
|
|
||||||
} from "../../../features/ordersFilter/ordersFilterSlice";
|
|
||||||
import ShippingWarehouseSelect from "../../ShippingWarehouseSelect/ShippingWarehouseSelect";
|
|
||||||
import CitySelect from "../../CitySelect/CitySelect";
|
|
||||||
|
|
||||||
export type SortingModalHandles = {
|
export const SortingModal = () => {
|
||||||
present: () => void;
|
const dispatch = useAppDispatch();
|
||||||
dismiss: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SortingModalElement = {
|
|
||||||
id: string;
|
|
||||||
label: string;
|
|
||||||
value: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const createRadioButton = (element: SortingModalElement) => {
|
|
||||||
return {
|
|
||||||
id: element.id,
|
|
||||||
label: element.label,
|
|
||||||
value: element.value,
|
|
||||||
size: RFPercentage(5),
|
|
||||||
color: blue,
|
|
||||||
labelStyle: {
|
|
||||||
fontSize: responsiveWidth(3),
|
|
||||||
fontWeight: "500" as const
|
|
||||||
},
|
|
||||||
borderSize: RFPercentage(0.5)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const SortingModal = () => {
|
|
||||||
const state = useSelector((state: RootState) => state.ordersFilter);
|
|
||||||
const shipmentWarehouseSelectorState = useSelector((state: RootState) => state.shippingWarehouseSelect);
|
|
||||||
const citySelectorState = useSelector((state: RootState) => state.citySelect);
|
|
||||||
|
|
||||||
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 modalRef = useRef<BottomSheetModal>(null);
|
||||||
const dismiss = () => {
|
const visible = useSelector((state: RootState) => state.filter.visible);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
if (!modalRef.current) return;
|
if (!modalRef.current) return;
|
||||||
dispatch(disableDim());
|
if (visible) {
|
||||||
modalRef.current.dismiss();
|
modalRef.current.present();
|
||||||
}
|
} else {
|
||||||
const present = () => {
|
modalRef.current.dismiss();
|
||||||
if (!modalRef.current) return;
|
}
|
||||||
modalRef.current.present();
|
}, [visible]);
|
||||||
dispatch(enableDim());
|
|
||||||
}
|
|
||||||
useEffect(() => {
|
|
||||||
if (state.isVisible) present();
|
|
||||||
else dismiss();
|
|
||||||
}, [state.isVisible]);
|
|
||||||
useEffect(() => {
|
|
||||||
dispatch(setShippingWarehouse(shipmentWarehouseSelectorState.selectedShippingWarehouse));
|
|
||||||
}, [shipmentWarehouseSelectorState.selectedShippingWarehouse]);
|
|
||||||
useEffect(() => {
|
|
||||||
dispatch(setCity(citySelectorState.selectedCity));
|
|
||||||
}, [citySelectorState.selectedCity]);
|
|
||||||
return (
|
return (
|
||||||
<BottomSheetModal
|
<BottomSheetModal
|
||||||
|
onDismiss={() => dispatch(closeFilter())}
|
||||||
ref={modalRef}
|
ref={modalRef}
|
||||||
snapPoints={snapPoints}
|
>
|
||||||
onDismiss={() => {
|
<BottomSheetView>
|
||||||
dispatch(disableDim());
|
<View>
|
||||||
dispatch(closeOrdersFilterModal())
|
|
||||||
}}>
|
|
||||||
<View style={styles.container}>
|
|
||||||
<View style={styles.content}>
|
|
||||||
<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"));
|
|
||||||
|
|
||||||
}}
|
<SortingModalBody/>
|
||||||
containerStyle={styles.radioButtons}
|
|
||||||
radioButtons={elements.map(createRadioButton)}/>
|
|
||||||
|
|
||||||
|
|
||||||
<View style={styles.selectors}>
|
|
||||||
{/*<View style={styles.selector}>*/}
|
|
||||||
{/* <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>*/}
|
|
||||||
|
|
||||||
<ShippingWarehouseSelect/>
|
|
||||||
{/*<CitySelect/>*/}
|
|
||||||
</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>
|
</View>
|
||||||
|
|
||||||
|
</BottomSheetView>
|
||||||
<BasicButton label={"Закрыть"} style={styles.button} onPress={() => {
|
|
||||||
dispatch(closeOrdersFilterModal());
|
|
||||||
}}/>
|
|
||||||
|
|
||||||
</View>
|
|
||||||
</BottomSheetModal>
|
</BottomSheetModal>
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
const styles = StyleSheet.create({
|
|
||||||
container: {
|
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
flex: 1,
|
|
||||||
padding: RFPercentage(3),
|
|
||||||
flexDirection: "column",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
rowGap: responsiveHeight(1)
|
|
||||||
},
|
|
||||||
radioButtons: {
|
|
||||||
alignItems: "flex-start"
|
|
||||||
},
|
|
||||||
content: {
|
|
||||||
rowGap: responsiveHeight(1)
|
|
||||||
},
|
|
||||||
button: {
|
|
||||||
|
|
||||||
marginTop: "auto"
|
|
||||||
},
|
|
||||||
selectors: {
|
|
||||||
rowGap: responsiveHeight(1)
|
|
||||||
},
|
|
||||||
selector: {
|
|
||||||
borderWidth: responsiveWidth(0.1),
|
|
||||||
borderRadius: responsiveWidth(1)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
export default SortingModal;
|
|
||||||
|
|||||||
43
src/components/Modals/SortingModal/SortingModalBody.tsx
Normal file
43
src/components/Modals/SortingModal/SortingModalBody.tsx
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import {closeFilter, FilterState, setFilterState} from "../../../features/filterSlice/filterSlice";
|
||||||
|
import {Controller, useForm} from "react-hook-form";
|
||||||
|
import {Button, View} from "react-native";
|
||||||
|
import ShippingWarehouseSelect from "../../ShippingWarehouseSelect/ShippingWarehouseSelect";
|
||||||
|
import {RFPercentage} from "react-native-responsive-fontsize";
|
||||||
|
import SortingButtons from "./SortingButtons";
|
||||||
|
import {useSelector} from "react-redux";
|
||||||
|
import {RootState, useAppDispatch} from "../../../redux/store";
|
||||||
|
|
||||||
|
const SortingModalBody = () => {
|
||||||
|
const currentState = useSelector((state: RootState) => state.filter.state);
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const {control, handleSubmit} = useForm<FilterState>({
|
||||||
|
defaultValues: currentState,
|
||||||
|
})
|
||||||
|
const onSubmit = (data: FilterState) => {
|
||||||
|
dispatch(setFilterState(data));
|
||||||
|
dispatch(closeFilter())
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<View style={{
|
||||||
|
padding: RFPercentage(1),
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: RFPercentage(1),
|
||||||
|
}}>
|
||||||
|
|
||||||
|
<Controller
|
||||||
|
name={"sorting"}
|
||||||
|
control={control}
|
||||||
|
render={({field}) => (<SortingButtons {...field}/>)}
|
||||||
|
/>
|
||||||
|
<Controller control={control} name={"shippingWarehouseId"}
|
||||||
|
render={({field}) => (
|
||||||
|
<ShippingWarehouseSelect
|
||||||
|
controllerProps={field}/>)}/>
|
||||||
|
<Button title={"Применить"} onPress={handleSubmit(onSubmit)}/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SortingModalBody;
|
||||||
@@ -1,43 +1,40 @@
|
|||||||
import {FC, useEffect} from "react";
|
import {FC, useEffect, useState} from "react";
|
||||||
import {useDispatch, useSelector} from "react-redux";
|
import RNPickerSelect, {PickerSelectProps} from 'react-native-picker-select';
|
||||||
import {RootState} from "../../redux/store";
|
import {ControllerRenderProps} from "react-hook-form";
|
||||||
import {Picker} from "@react-native-picker/picker";
|
import {FilterState} from "../../features/filterSlice/filterSlice";
|
||||||
|
import {ShippingWarehouse} from "../../types/shippingWarehouse";
|
||||||
import generalApi from "../../api/generalApi";
|
import generalApi from "../../api/generalApi";
|
||||||
import {
|
|
||||||
initializeShippingWarehouseSelect,
|
|
||||||
selectShippingWarehouse
|
|
||||||
} from "../../features/shippingWarehouseSelect/shippingWarehouseSelectSlice";
|
|
||||||
import {responsiveWidth} from "react-native-responsive-dimensions";
|
|
||||||
import {View} from "react-native";
|
|
||||||
|
|
||||||
const ShippingWarehouseSelect: FC = () => {
|
type Props = {
|
||||||
const state = useSelector((state: RootState) => state.shippingWarehouseSelect);
|
selectProps?: Omit<PickerSelectProps, "items" | "onValueChange">;
|
||||||
const dispatch = useDispatch();
|
controllerProps?: ControllerRenderProps<FilterState, "shippingWarehouseId">
|
||||||
|
}
|
||||||
|
const ShippingWarehouseSelect: FC<Props> = (props) => {
|
||||||
|
const [items, setItems] = useState<ShippingWarehouse[]>([]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (state.initialized) return;
|
generalApi.getShippingWarehouses().then(response => {
|
||||||
generalApi.getShippingWarehouses().then(shippingWarehouses =>
|
setItems(response);
|
||||||
dispatch(initializeShippingWarehouseSelect(shippingWarehouses)))
|
})
|
||||||
}, []);
|
}, []);
|
||||||
return (
|
return (
|
||||||
<View style={{
|
<RNPickerSelect
|
||||||
borderWidth: responsiveWidth(0.1),
|
useNativeAndroidPickerStyle={true}
|
||||||
borderRadius: responsiveWidth(1)
|
style={{
|
||||||
}}>
|
viewContainer: {
|
||||||
<Picker
|
borderWidth: 1,
|
||||||
selectedValue={state.selectedShippingWarehouse?.id}
|
borderColor: "#9EA0A4",
|
||||||
onValueChange={(value, event) => dispatch(selectShippingWarehouse({shippingWarehouseId: value}))}
|
borderRadius: "1%"
|
||||||
>
|
}
|
||||||
{state.shippingWarehouses.map(shippingWarehouse => (
|
}}
|
||||||
<Picker.Item
|
placeholder={{label: "Все склады", value: -1, color: "#9EA0A4"}}
|
||||||
key={shippingWarehouse.id}
|
{...props.selectProps}
|
||||||
label={shippingWarehouse.name}
|
onValueChange={(value, _) => {
|
||||||
value={shippingWarehouse.id}
|
if (typeof value !== 'number') return;
|
||||||
style={{fontSize: responsiveWidth(3)}}
|
props.controllerProps?.onChange(value);
|
||||||
/>
|
}}
|
||||||
))}
|
value={props.controllerProps?.value}
|
||||||
</Picker>
|
itemKey={"value"}
|
||||||
</View>
|
items={items.map(sw => ({label: sw.name, value: sw.id, color:"black"}))}/>
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
import {FC} from "react";
|
import {FC} from "react";
|
||||||
import {StyleSheet, View, Image, TouchableOpacity, GestureResponderEvent} from "react-native";
|
import {GestureResponderEvent, Image, StyleSheet, TouchableOpacity, View} from "react-native";
|
||||||
import DText from "../DText/DText";
|
import DText from "../DText/DText";
|
||||||
import {RFPercentage} from "react-native-responsive-fontsize";
|
import {RFPercentage} from "react-native-responsive-fontsize";
|
||||||
import {responsiveWidth} from "react-native-responsive-dimensions";
|
import {responsiveWidth} from "react-native-responsive-dimensions";
|
||||||
|
import {useAppDispatch} from "../../redux/store";
|
||||||
|
import {openFilter} from "../../features/filterSlice/filterSlice";
|
||||||
|
|
||||||
type Props = {
|
const SortingButton: FC = () => {
|
||||||
onPress?: (event: GestureResponderEvent) => void
|
const dispatch = useAppDispatch();
|
||||||
|
const onPress = (event: GestureResponderEvent) => {
|
||||||
};
|
dispatch(openFilter())
|
||||||
const SortingButton: FC<Props> = ({onPress}) => {
|
}
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity onPress={onPress}>
|
<TouchableOpacity onPress={onPress}>
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
|
|||||||
38
src/features/filterSlice/filterSlice.ts
Normal file
38
src/features/filterSlice/filterSlice.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import {createSlice} from "@reduxjs/toolkit";
|
||||||
|
|
||||||
|
export type FilterState = {
|
||||||
|
sorting: string;
|
||||||
|
shippingWarehouseId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FilterSliceState {
|
||||||
|
visible: boolean;
|
||||||
|
state: FilterState
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: FilterSliceState = {
|
||||||
|
visible: true,
|
||||||
|
state: {
|
||||||
|
sorting: 'shipment_date_desc',
|
||||||
|
shippingWarehouseId: -1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const filterSlice = createSlice({
|
||||||
|
name: 'filter',
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
openFilter: (state) => {
|
||||||
|
state.visible = true;
|
||||||
|
},
|
||||||
|
closeFilter: (state) => {
|
||||||
|
state.visible = false;
|
||||||
|
},
|
||||||
|
setFilterState: (state, action) => {
|
||||||
|
state.state = action.payload;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export const {openFilter, closeFilter, setFilterState} = filterSlice.actions;
|
||||||
|
export default filterSlice.reducer;
|
||||||
@@ -89,7 +89,6 @@ export const ordersFilterSlice = createSlice({
|
|||||||
state.city = action.payload;
|
state.city = action.payload;
|
||||||
},
|
},
|
||||||
setPage: (state, action: PayloadAction<number>) => {
|
setPage: (state, action: PayloadAction<number>) => {
|
||||||
console.log(action)
|
|
||||||
state.page = action.payload;
|
state.page = action.payload;
|
||||||
},
|
},
|
||||||
nextPage: (state) => {
|
nextPage: (state) => {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import citySelectReducer from 'features/citySelect/citySelectSlice';
|
|||||||
import cancelAssemblyModal from 'features/cancelAssemblyModal/cancelAssemblyModalSlice';
|
import cancelAssemblyModal from 'features/cancelAssemblyModal/cancelAssemblyModalSlice';
|
||||||
import balanceReducer from 'features/balance/balanceSlice';
|
import balanceReducer from 'features/balance/balanceSlice';
|
||||||
import animationsReducer from 'features/animations/animationsSlice';
|
import animationsReducer from 'features/animations/animationsSlice';
|
||||||
|
import filterReducer from 'features/filterSlice/filterSlice';
|
||||||
import {useDispatch} from "react-redux";
|
import {useDispatch} from "react-redux";
|
||||||
|
|
||||||
export const store = configureStore({
|
export const store = configureStore({
|
||||||
@@ -31,7 +32,8 @@ export const store = configureStore({
|
|||||||
citySelect: citySelectReducer,
|
citySelect: citySelectReducer,
|
||||||
cancelAssemblyModal: cancelAssemblyModal,
|
cancelAssemblyModal: cancelAssemblyModal,
|
||||||
balance: balanceReducer,
|
balance: balanceReducer,
|
||||||
animations: animationsReducer
|
animations: animationsReducer,
|
||||||
|
filter:filterReducer
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -15,12 +15,12 @@ import {openScanModal} from "../../features/scanModal/scanModalSlice";
|
|||||||
import DTitle from "../../components/DTitle/DTitle";
|
import DTitle from "../../components/DTitle/DTitle";
|
||||||
import SortingButton from "../../components/SortingButton/SortingButton";
|
import SortingButton from "../../components/SortingButton/SortingButton";
|
||||||
import useBarcodeOrders from "./useBarcodeOrders";
|
import useBarcodeOrders from "./useBarcodeOrders";
|
||||||
import {openOrdersFilterModal} from "../../features/ordersFilter/ordersFilterSlice";
|
|
||||||
|
|
||||||
function BarcodeScreen() {
|
function BarcodeScreen() {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
const filterState = useSelector((state: RootState) => state.filter.state);
|
||||||
const [productId, setProductId] = useState(-1);
|
const [productId, setProductId] = useState(-1);
|
||||||
const {orders, isUpdating,refresh} = useBarcodeOrders({productId});
|
const {orders, isUpdating, refresh} = useBarcodeOrders({productId, filterState});
|
||||||
|
|
||||||
const isScanModalVisible = useSelector((state: RootState) => state.scanModal.isVisible);
|
const isScanModalVisible = useSelector((state: RootState) => state.scanModal.isVisible);
|
||||||
const navigator = useNavigation<NavigationProp<TabNavigatorParamList, 'Home'>>();
|
const navigator = useNavigation<NavigationProp<TabNavigatorParamList, 'Home'>>();
|
||||||
@@ -29,7 +29,6 @@ function BarcodeScreen() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!navigationState.history) return;
|
if (!navigationState.history) return;
|
||||||
if (productId < 0) return;
|
if (productId < 0) return;
|
||||||
console.log('refreshing orders for productId:', productId);
|
|
||||||
refresh();
|
refresh();
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
let currentTabKey: string = navigationState.history[navigationState.history.length - 1].key;
|
let currentTabKey: string = navigationState.history[navigationState.history.length - 1].key;
|
||||||
@@ -40,15 +39,13 @@ function BarcodeScreen() {
|
|||||||
<SearchBar onProductSelected={product => {
|
<SearchBar onProductSelected={product => {
|
||||||
setProductId(product.productId);
|
setProductId(product.productId);
|
||||||
}}/>
|
}}/>
|
||||||
<SortingButton onPress={() => {
|
<SortingButton
|
||||||
dispatch(openOrdersFilterModal());
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<View style={styles.content}>
|
<View style={styles.content}>
|
||||||
{orders.length > 0 || isUpdating ? <FlashList
|
{orders.length > 0 || isUpdating ? <FlashList
|
||||||
refreshing={isUpdating}
|
refreshing={isUpdating}
|
||||||
onRefresh={() => {
|
onRefresh={() => {
|
||||||
refresh()
|
refresh()
|
||||||
}}
|
}}
|
||||||
keyboardShouldPersistTaps={"never"}
|
keyboardShouldPersistTaps={"never"}
|
||||||
data={orders}
|
data={orders}
|
||||||
|
|||||||
@@ -1,27 +1,29 @@
|
|||||||
import {useSelector} from "react-redux";
|
|
||||||
import {RootState} from "../../redux/store";
|
|
||||||
import {useEffect, useState} from "react";
|
import {useEffect, useState} from "react";
|
||||||
import {Order} from "../../types/order";
|
import {Order} from "../../types/order";
|
||||||
import ordersApi from "../../api/ordersApi";
|
import ordersApi from "../../api/ordersApi";
|
||||||
|
import {FilterState} from "../../features/filterSlice/filterSlice";
|
||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
productId: number;
|
productId: number;
|
||||||
|
filterState: FilterState;
|
||||||
}
|
}
|
||||||
const useBarcodeOrders = (props: Props) => {
|
export const parseOrderBy = (orderBy: string): { orderBy: string, desc: boolean } => {
|
||||||
|
const ascDesc = orderBy.split('_').at(-1);
|
||||||
|
const field = orderBy.split('_').slice(0, -1).join('_');
|
||||||
|
if (ascDesc === 'asc') return {orderBy: field, desc: false};
|
||||||
|
if (ascDesc === 'desc') return {orderBy: field, desc: true};
|
||||||
|
return {orderBy: field, desc: false};
|
||||||
|
}
|
||||||
|
const useBarcodeOrders = ({filterState, productId}: Props) => {
|
||||||
const [orders, setOrders] = useState<Order[]>([]);
|
const [orders, setOrders] = useState<Order[]>([]);
|
||||||
const [isUpdating, setIsUpdating] = useState(false);
|
const [isUpdating, setIsUpdating] = useState(false);
|
||||||
const {productId} = props;
|
|
||||||
const {
|
|
||||||
orderBy,
|
|
||||||
isVisible,
|
|
||||||
desc,
|
|
||||||
shipmentDate,
|
|
||||||
status,
|
|
||||||
shippingWarehouse,
|
|
||||||
city
|
|
||||||
} = useSelector((state: RootState) => state.ordersFilter);
|
|
||||||
const fetchOrders = async (): Promise<Order[]> => {
|
const fetchOrders = async (): Promise<Order[]> => {
|
||||||
return ordersApi.getOrdersByProduct({productId, orderBy, shipmentDate, status, shippingWarehouse, city, desc});
|
return ordersApi.getOrdersByProduct({
|
||||||
|
...parseOrderBy(filterState.sorting),
|
||||||
|
productId: productId,
|
||||||
|
shippingWarehouseId: filterState.shippingWarehouseId,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
const refresh = () => {
|
const refresh = () => {
|
||||||
setOrders([]);
|
setOrders([]);
|
||||||
@@ -32,16 +34,11 @@ const useBarcodeOrders = (props: Props) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isVisible || productId < 0) return;
|
if (productId < 0) return;
|
||||||
refresh()
|
refresh()
|
||||||
}, [
|
}, [
|
||||||
productId,
|
productId,
|
||||||
isVisible,
|
filterState
|
||||||
desc,
|
|
||||||
orderBy,
|
|
||||||
shipmentDate,
|
|
||||||
status,
|
|
||||||
shippingWarehouse,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import assemblyApi from "../../api/assemblyApi";
|
|||||||
import {setAssembly, setLocalState, setOrder} from "../../features/assembly/assemblySlice";
|
import {setAssembly, setLocalState, setOrder} from "../../features/assembly/assemblySlice";
|
||||||
import ordersApi from "../../api/ordersApi";
|
import ordersApi from "../../api/ordersApi";
|
||||||
import ImageZoomModal from "../../components/Modals/ImageZoomModal/ImageZoomModal";
|
import ImageZoomModal from "../../components/Modals/ImageZoomModal/ImageZoomModal";
|
||||||
import SortingModal from "../../components/Modals/SortingModal/SortingModal";
|
import {SortingModal} from "../../components/Modals/SortingModal/SortingModal";
|
||||||
import {ASSEMBLY_STATE} from "../../types/assembly";
|
import {ASSEMBLY_STATE} from "../../types/assembly";
|
||||||
import * as FileSystem from 'expo-file-system';
|
import * as FileSystem from 'expo-file-system';
|
||||||
import applicationApi from "../../api/applicationApi";
|
import applicationApi from "../../api/applicationApi";
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ function HomeScreen() {
|
|||||||
return (
|
return (
|
||||||
<View style={{backgroundColor: "white"}}>
|
<View style={{backgroundColor: "white"}}>
|
||||||
<SearchBar onSearch={(text) => {
|
<SearchBar onSearch={(text) => {
|
||||||
console.log(`From scanner: ${text}`)
|
|
||||||
}}/>
|
}}/>
|
||||||
<DText>Хуй</DText>
|
<DText>Хуй</DText>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ function MoneyScreen() {
|
|||||||
const onPress = () => {
|
const onPress = () => {
|
||||||
printingApi.getLabel(391845).then(async result => {
|
printingApi.getLabel(391845).then(async result => {
|
||||||
PrintingService.getInstance().printPdf("", result).then(() => {
|
PrintingService.getInstance().printPdf("", result).then(() => {
|
||||||
console.log("Printed successfully");
|
|
||||||
}
|
}
|
||||||
).catch(error => {
|
).catch(error => {
|
||||||
console.error("Error printing:", error);
|
console.error("Error printing:", error);
|
||||||
|
|||||||
@@ -1,15 +1,10 @@
|
|||||||
import {useEffect, useState} from "react";
|
import {useEffect, useState} from "react";
|
||||||
import {Order} from "../../types/order";
|
import {Order} from "../../types/order";
|
||||||
import ordersApi from "../../api/ordersApi";
|
import ordersApi from "../../api/ordersApi";
|
||||||
import {setOrder} from "../../features/assembly/assemblySlice";
|
|
||||||
import {RootState} from "../../redux/store";
|
import {RootState} from "../../redux/store";
|
||||||
import {useDispatch, useSelector} from "react-redux";
|
import {useDispatch, useSelector} from "react-redux";
|
||||||
import {
|
import {setPage} from "../../features/ordersFilter/ordersFilterSlice";
|
||||||
refreshPagination,
|
import {parseOrderBy} from "../BarcodeScreen/useBarcodeOrders";
|
||||||
setOrderBy,
|
|
||||||
setPage,
|
|
||||||
setShippingWarehouse
|
|
||||||
} from "../../features/ordersFilter/ordersFilterSlice";
|
|
||||||
|
|
||||||
|
|
||||||
const useOrders = () => {
|
const useOrders = () => {
|
||||||
@@ -18,15 +13,16 @@ const useOrders = () => {
|
|||||||
const [isInitialized, setIsInitialized] = useState(false);
|
const [isInitialized, setIsInitialized] = useState(false);
|
||||||
const [isRefreshing, setIsRefreshing] = useState(false);
|
const [isRefreshing, setIsRefreshing] = useState(false);
|
||||||
const filterState = useSelector((state: RootState) => state.ordersFilter);
|
const filterState = useSelector((state: RootState) => state.ordersFilter);
|
||||||
|
const filter2state = useSelector((state: RootState) => state.filter.state);
|
||||||
const fetchOrders = async (): Promise<Order[]> => {
|
const fetchOrders = async (): Promise<Order[]> => {
|
||||||
return ordersApi.getOrders(
|
return ordersApi.getOrders({
|
||||||
filterState.page,
|
...parseOrderBy(filter2state.sorting),
|
||||||
filterState.orderBy,
|
page: filterState.page,
|
||||||
filterState.desc,
|
shipmentDate: "",
|
||||||
filterState.shipmentDate,
|
shipmentWarehouseId: filter2state.shippingWarehouseId,
|
||||||
filterState.status,
|
status: -1
|
||||||
filterState.shippingWarehouse.id
|
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const appendOrders = (newOrders: Order[]) => {
|
const appendOrders = (newOrders: Order[]) => {
|
||||||
@@ -51,11 +47,12 @@ const useOrders = () => {
|
|||||||
filterState.status,
|
filterState.status,
|
||||||
filterState.shipmentDate,
|
filterState.shipmentDate,
|
||||||
filterState.isVisible,
|
filterState.isVisible,
|
||||||
filterState.shippingWarehouse
|
filterState.shippingWarehouse,
|
||||||
|
filter2state
|
||||||
]);
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchOrders().then(newOrders=>{
|
fetchOrders().then(newOrders => {
|
||||||
setOrders(newOrders);
|
setOrders(newOrders);
|
||||||
setIsInitialized(true);
|
setIsInitialized(true);
|
||||||
|
|
||||||
|
|||||||
@@ -4,18 +4,14 @@ import {RFPercentage} from "react-native-responsive-fontsize";
|
|||||||
import {responsiveHeight, responsiveWidth} from "react-native-responsive-dimensions";
|
import {responsiveHeight, responsiveWidth} from "react-native-responsive-dimensions";
|
||||||
import SortingButton from "../../components/SortingButton/SortingButton";
|
import SortingButton from "../../components/SortingButton/SortingButton";
|
||||||
import {useDispatch} from "react-redux";
|
import {useDispatch} from "react-redux";
|
||||||
import {useEffect, useRef, useState} from "react";
|
|
||||||
import {Order} from "../../types/order";
|
import {Order} from "../../types/order";
|
||||||
import {FlashList} from "@shopify/flash-list";
|
import {FlashList} from "@shopify/flash-list";
|
||||||
import SortingModal, {
|
|
||||||
SortingModalHandles
|
|
||||||
} from "../../components/Modals/SortingModal/SortingModal";
|
|
||||||
import flashListSeparator from "../../components/FlashListSeparator/FlashListSeparator";
|
import flashListSeparator from "../../components/FlashListSeparator/FlashListSeparator";
|
||||||
import {setOrder} from "../../features/assembly/assemblySlice";
|
import {setOrder} from "../../features/assembly/assemblySlice";
|
||||||
import {NavigationProp, useNavigation} from "@react-navigation/native";
|
import {NavigationProp, useNavigation} from "@react-navigation/native";
|
||||||
import {TabNavigatorParamList} from "../MainScreen/MainScreen";
|
import {TabNavigatorParamList} from "../MainScreen/MainScreen";
|
||||||
import useOrders from "../OrderScreen/useOrders";
|
import useOrders from "../OrderScreen/useOrders";
|
||||||
import {nextPage, openOrdersFilterModal} from "../../features/ordersFilter/ordersFilterSlice";
|
import {nextPage} from "../../features/ordersFilter/ordersFilterSlice";
|
||||||
import DTitle from "../../components/DTitle/DTitle";
|
import DTitle from "../../components/DTitle/DTitle";
|
||||||
|
|
||||||
function OrdersScreen() {
|
function OrdersScreen() {
|
||||||
@@ -27,9 +23,7 @@ function OrdersScreen() {
|
|||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
|
|
||||||
<View style={styles.sortingButtonWrapper}>
|
<View style={styles.sortingButtonWrapper}>
|
||||||
<SortingButton onPress={() => {
|
<SortingButton/>
|
||||||
dispatch(openOrdersFilterModal());
|
|
||||||
}}/>
|
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.content}>
|
<View style={styles.content}>
|
||||||
<FlashList onRefresh={refresh}
|
<FlashList onRefresh={refresh}
|
||||||
|
|||||||
@@ -57,7 +57,6 @@ function ProfileScreen() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (printerIpDebounce.length > 0) {
|
if (printerIpDebounce.length > 0) {
|
||||||
SecureStore.setItemAsync("printerIp", printerIpDebounce).then(() => {
|
SecureStore.setItemAsync("printerIp", printerIpDebounce).then(() => {
|
||||||
console.log("Printer IP saved to secure store");
|
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
console.error("Error saving printer IP to secure store:", error);
|
console.error("Error saving printer IP to secure store:", error);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ class PrintingService {
|
|||||||
await this.printLabelAsync(bytes);
|
await this.printLabelAsync(bytes);
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user