crpt
This commit is contained in:
		@@ -87,7 +87,7 @@ android {
 | 
			
		||||
        minSdkVersion rootProject.ext.minSdkVersion
 | 
			
		||||
        targetSdkVersion rootProject.ext.targetSdkVersion
 | 
			
		||||
        versionCode 1
 | 
			
		||||
        versionName "1.1.7"
 | 
			
		||||
        versionName "1.3.4"
 | 
			
		||||
 | 
			
		||||
        buildConfigField("boolean", "REACT_NATIVE_UNSTABLE_USE_RUNTIME_SCHEDULER_ALWAYS", (findProperty("reactNative.unstable_useRuntimeSchedulerAlways") ?: true).toString())
 | 
			
		||||
    }
 | 
			
		||||
@@ -168,6 +168,7 @@ dependencies {
 | 
			
		||||
        exclude group:'com.squareup.okhttp3', module:'okhttp'
 | 
			
		||||
    }
 | 
			
		||||
    debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}")
 | 
			
		||||
    implementation(files("libs/printer-lib-2.2.4.aar"))
 | 
			
		||||
 | 
			
		||||
    if (hermesEnabled.toBoolean()) {
 | 
			
		||||
        implementation("com.facebook.react:hermes-android")
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,8 @@
 | 
			
		||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
 | 
			
		||||
  <uses-permission android:name="android.permission.INTERNET"/>
 | 
			
		||||
  <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
 | 
			
		||||
  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
 | 
			
		||||
  <uses-permission android:name="android.permission.RECORD_AUDIO"/>
 | 
			
		||||
  <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
 | 
			
		||||
  <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
 | 
			
		||||
  <uses-permission android:name="android.permission.VIBRATE"/>
 | 
			
		||||
@@ -27,6 +29,7 @@
 | 
			
		||||
        <category android:name="android.intent.category.DEFAULT"/>
 | 
			
		||||
        <category android:name="android.intent.category.BROWSABLE"/>
 | 
			
		||||
        <data android:scheme="com.anonymous.Assemblr"/>
 | 
			
		||||
        <data android:scheme="exp+assemblr"/>
 | 
			
		||||
      </intent-filter>
 | 
			
		||||
    </activity>
 | 
			
		||||
    <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" android:exported="false"/>
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,41 @@
 | 
			
		||||
package com.anonymous.Assemblr;
 | 
			
		||||
 | 
			
		||||
import android.util.Log;
 | 
			
		||||
 | 
			
		||||
import androidx.annotation.NonNull;
 | 
			
		||||
 | 
			
		||||
import com.facebook.react.bridge.ReactApplicationContext;
 | 
			
		||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
 | 
			
		||||
import com.facebook.react.bridge.ReactMethod;
 | 
			
		||||
 | 
			
		||||
import net.posprinter.IDeviceConnection;
 | 
			
		||||
import net.posprinter.POSConnect;
 | 
			
		||||
import net.posprinter.TSCPrinter;
 | 
			
		||||
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
public class AwesomeModule extends ReactContextBaseJavaModule {
 | 
			
		||||
    private final ReactApplicationContext context;
 | 
			
		||||
    private final Map<String, IDeviceConnection> printersMap = new HashMap<>();
 | 
			
		||||
    AwesomeModule(ReactApplicationContext context) {
 | 
			
		||||
        super(context);
 | 
			
		||||
        this.context = context;
 | 
			
		||||
        POSConnect.init(this.context);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void initPrinters(){
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @NonNull
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getName() {
 | 
			
		||||
        return "AwesomeModule";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ReactMethod
 | 
			
		||||
    public void test() {
 | 
			
		||||
        Log.d("AwesomeModule", "AwesomeModule test");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,4 +1,8 @@
 | 
			
		||||
package com.anonymous.Assemblr;
 | 
			
		||||
// @generated begin react-native-keyevent-import - expo prebuild (DO NOT MODIFY) sync-6d2345dd84c398e4e99d7d44eb792294628c7e34
 | 
			
		||||
import android.view.KeyEvent;
 | 
			
		||||
import com.github.kevinejohn.keyevent.KeyEventModule;
 | 
			
		||||
// @generated end react-native-keyevent-import
 | 
			
		||||
 | 
			
		||||
import android.os.Build;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
@@ -11,6 +15,49 @@ import com.facebook.react.defaults.DefaultReactActivityDelegate;
 | 
			
		||||
import expo.modules.ReactActivityDelegateWrapper;
 | 
			
		||||
 | 
			
		||||
public class MainActivity extends ReactActivity {
 | 
			
		||||
// @generated begin react-native-keyevent-body - expo prebuild (DO NOT MODIFY) sync-4ba28f58cc0b4a775aa231e380b7f40e8f34382a
 | 
			
		||||
@Override
 | 
			
		||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
 | 
			
		||||
 | 
			
		||||
  // // Uncomment this is key events should only trigger once when key is held down
 | 
			
		||||
  // if (event.getRepeatCount() == 0) {
 | 
			
		||||
  //   KeyEventModule.getInstance().onKeyDownEvent(keyCode, event);
 | 
			
		||||
  // }
 | 
			
		||||
 | 
			
		||||
  // // This will trigger the key repeat if the key is held down
 | 
			
		||||
  // // Comment this out if uncommenting the above
 | 
			
		||||
  KeyEventModule.getInstance().onKeyDownEvent(keyCode, event);
 | 
			
		||||
 | 
			
		||||
  // // Uncomment this if you want the default keyboard behavior
 | 
			
		||||
  // return super.onKeyDown(keyCode, event);
 | 
			
		||||
 | 
			
		||||
  // // The default keyboard behaviour wll be overridden
 | 
			
		||||
  // // This is similar to what e.preventDefault() does in a browser
 | 
			
		||||
  // // comment this if uncommenting the above
 | 
			
		||||
  super.onKeyDown(keyCode, event);
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Override
 | 
			
		||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
 | 
			
		||||
  KeyEventModule.getInstance().onKeyUpEvent(keyCode, event);
 | 
			
		||||
 | 
			
		||||
  // // Uncomment this if you want the default keyboard behavior
 | 
			
		||||
  // return super.onKeyUp(keyCode, event);
 | 
			
		||||
 | 
			
		||||
  // // The default keyboard behaviour wll be overridden
 | 
			
		||||
  // // This is similar to what e.preventDefault() does in a browser
 | 
			
		||||
  // // comment this if uncommenting the above
 | 
			
		||||
  super.onKeyUp(keyCode, event);
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Override
 | 
			
		||||
public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
 | 
			
		||||
    KeyEventModule.getInstance().onKeyMultipleEvent(keyCode, repeatCount, event);
 | 
			
		||||
    return super.onKeyMultiple(keyCode, repeatCount, event);
 | 
			
		||||
}
 | 
			
		||||
// @generated end react-native-keyevent-body
 | 
			
		||||
  @Override
 | 
			
		||||
  protected void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
    // Set the theme to AppTheme BEFORE onCreate to support 
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ package com.anonymous.Assemblr;
 | 
			
		||||
 | 
			
		||||
import android.app.Application;
 | 
			
		||||
import android.content.res.Configuration;
 | 
			
		||||
 | 
			
		||||
import androidx.annotation.NonNull;
 | 
			
		||||
 | 
			
		||||
import com.facebook.react.PackageList;
 | 
			
		||||
@@ -31,6 +32,7 @@ public class MainApplication extends Application implements ReactApplication {
 | 
			
		||||
                protected List<ReactPackage> getPackages() {
 | 
			
		||||
                    @SuppressWarnings("UnnecessaryLocalVariable")
 | 
			
		||||
                    List<ReactPackage> packages = new PackageList(this).getPackages();
 | 
			
		||||
                    packages.add(new MyAppPackage());
 | 
			
		||||
                    // Packages that cannot be autolinked yet can be added manually here, for example:
 | 
			
		||||
                    // packages.add(new MyReactNativePackage());
 | 
			
		||||
                    return packages;
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,30 @@
 | 
			
		||||
package com.anonymous.Assemblr;
 | 
			
		||||
 | 
			
		||||
import androidx.annotation.NonNull;
 | 
			
		||||
 | 
			
		||||
import com.facebook.react.ReactPackage;
 | 
			
		||||
import com.facebook.react.bridge.NativeModule;
 | 
			
		||||
import com.facebook.react.bridge.ReactApplicationContext;
 | 
			
		||||
import com.facebook.react.uimanager.ViewManager;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
public class MyAppPackage implements ReactPackage {
 | 
			
		||||
    @NonNull
 | 
			
		||||
    @Override
 | 
			
		||||
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
 | 
			
		||||
        return Collections.emptyList();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public List<NativeModule> createNativeModules(
 | 
			
		||||
            ReactApplicationContext reactContext) {
 | 
			
		||||
        List<NativeModule> modules = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
        modules.add(new AwesomeModule(reactContext));
 | 
			
		||||
 | 
			
		||||
        return modules;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -24,10 +24,13 @@
 | 
			
		||||
    "@shopify/flash-list": "1.4.3",
 | 
			
		||||
    "axios": "^1.5.0",
 | 
			
		||||
    "babel-plugin-module-resolver": "^5.0.0",
 | 
			
		||||
    "eas-cli": "^12.5.1",
 | 
			
		||||
    "expo": "~49.0.8",
 | 
			
		||||
    "expo-application": "~5.3.0",
 | 
			
		||||
    "expo-av": "~13.4.1",
 | 
			
		||||
    "expo-build-properties": "~0.8.3",
 | 
			
		||||
    "expo-constants": "~14.4.2",
 | 
			
		||||
    "expo-crypto": "~12.4.1",
 | 
			
		||||
    "expo-dev-client": "~2.4.12",
 | 
			
		||||
    "expo-file-system": "~15.4.4",
 | 
			
		||||
    "expo-intent-launcher": "~10.7.0",
 | 
			
		||||
@@ -59,13 +62,13 @@
 | 
			
		||||
    "react-native-webview": "13.2.2",
 | 
			
		||||
    "react-redux": "^8.1.2",
 | 
			
		||||
    "redux": "^4.2.1",
 | 
			
		||||
    "rn-openapp": "^2.1.2",
 | 
			
		||||
    "expo-av": "~13.4.1"
 | 
			
		||||
    "rn-openapp": "^2.1.2"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@babel/core": "^7.20.0",
 | 
			
		||||
    "@types/react": "~18.2.14",
 | 
			
		||||
    "typescript": "^5.1.3"
 | 
			
		||||
  },
 | 
			
		||||
  "private": true
 | 
			
		||||
  "private": true,
 | 
			
		||||
  "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
import axios from 'axios';
 | 
			
		||||
 | 
			
		||||
export const baseUrl = 'https://assemblr.denco.store';
 | 
			
		||||
// export const baseUrl = 'http://192.168.1.101:5000';
 | 
			
		||||
// export const baseUrl = 'https://assemblr.denco.store';
 | 
			
		||||
export const baseUrl = 'http://192.168.1.101:5000';
 | 
			
		||||
const apiClient = axios.create({
 | 
			
		||||
    baseURL: baseUrl
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -3,15 +3,15 @@ import {Assembly} from "../types/assembly";
 | 
			
		||||
import {closeCancelAssemblyModal} from "../features/cancelAssemblyModal/cancelAssemblyModalSlice";
 | 
			
		||||
 | 
			
		||||
const router = '/assembly';
 | 
			
		||||
 | 
			
		||||
const assemblyApi = {
 | 
			
		||||
    create: async (orderId: number): Promise<{
 | 
			
		||||
export type CreateAssemblyResponse = {
 | 
			
		||||
    ok: boolean,
 | 
			
		||||
    message: string,
 | 
			
		||||
    assemblyId: number,
 | 
			
		||||
    statusCode: AssemblyCreationStatusCode,
 | 
			
		||||
    userName?: string
 | 
			
		||||
    }> => {
 | 
			
		||||
}
 | 
			
		||||
const assemblyApi = {
 | 
			
		||||
    create: async (orderId: number): Promise<CreateAssemblyResponse> => {
 | 
			
		||||
        let response = await apiClient.post(`${router}/create`, {orderId});
 | 
			
		||||
        return response.data;
 | 
			
		||||
    },
 | 
			
		||||
@@ -42,6 +42,14 @@ const assemblyApi = {
 | 
			
		||||
    cancelById: async (assemblyId: number): Promise<{ ok: boolean, message: string }> => {
 | 
			
		||||
        let response = await apiClient.post(`${router}/cancelById`, {assemblyId});
 | 
			
		||||
        return response.data;
 | 
			
		||||
    },
 | 
			
		||||
    attachCrpt: async (orderProductId: number, crpt: string): Promise<{ ok: boolean, message: string }> => {
 | 
			
		||||
        let response = await apiClient.post(`${router}/attachCrpt`, {orderProductId, crpt});
 | 
			
		||||
        return response.data;
 | 
			
		||||
    },
 | 
			
		||||
    needCrpt: async (orderProductId: number): Promise<{ needCrpt: boolean }> => {
 | 
			
		||||
        let response = await apiClient.get(`${router}/needCrpt`, {params: {orderProductId}});
 | 
			
		||||
        return response.data;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ import {RFPercentage} from "react-native-responsive-fontsize";
 | 
			
		||||
import DText from "../DText/DText";
 | 
			
		||||
import DTitle from "../DTitle/DTitle";
 | 
			
		||||
 | 
			
		||||
type Props = {
 | 
			
		||||
export type BasicButtonProps = {
 | 
			
		||||
    label: string;
 | 
			
		||||
    style?: StyleProp<ViewStyle>;
 | 
			
		||||
    containerStyle?: StyleProp<ViewStyle>;
 | 
			
		||||
@@ -14,7 +14,7 @@ type Props = {
 | 
			
		||||
    disabled?: boolean
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const BasicButton: FC<Props> = ({label, onPress, containerStyle, style, isUnset = false, disabled = false}) => {
 | 
			
		||||
const BasicButton: FC<BasicButtonProps> = ({label, onPress, containerStyle, style, isUnset = false, disabled = false}) => {
 | 
			
		||||
    return (
 | 
			
		||||
        <TouchableOpacity style={containerStyle} disabled={disabled} onPress={onPress}>
 | 
			
		||||
            <View style={[styles.container, style, disabled ? {backgroundColor: "#A0A0A0"} : {}, containerStyle]}>
 | 
			
		||||
 
 | 
			
		||||
@@ -7,13 +7,13 @@ import {responsiveHeight, responsiveWidth} from "react-native-responsive-dimensi
 | 
			
		||||
import DTitle from "../../DTitle/DTitle";
 | 
			
		||||
import BasicButton from "../../BasicButton/BasicButton";
 | 
			
		||||
 | 
			
		||||
type Props = {
 | 
			
		||||
export type AcceptModalProps = {
 | 
			
		||||
    visible: boolean;
 | 
			
		||||
    text: string;
 | 
			
		||||
    onAccepted: () => void;
 | 
			
		||||
    onRefused: () => void;
 | 
			
		||||
}
 | 
			
		||||
const AcceptModal: FC<Props> = ({visible, text, onAccepted, onRefused}) => {
 | 
			
		||||
const AcceptModal: FC<AcceptModalProps> = ({visible, text, onAccepted, onRefused}) => {
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <Modal isVisible={visible}>
 | 
			
		||||
 
 | 
			
		||||
@@ -11,15 +11,16 @@ import {closeScanModal, setScannedData} from "../../features/scanModal/scanModal
 | 
			
		||||
 | 
			
		||||
const ScanModal: FC = () => {
 | 
			
		||||
    const inputRef = useRef<TextInput | null>(null);
 | 
			
		||||
    const visible = useSelector((state: RootState) => state.scanModal.isVisible);
 | 
			
		||||
    const state = useSelector((state: RootState) => state.scanModal);
 | 
			
		||||
    const getDefaultLabel = () => "Наведите сканер на штрихкод товара или заказа";
 | 
			
		||||
    const dispatch = useDispatch();
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        if (visible) inputRef.current?.focus();
 | 
			
		||||
    }, [visible]);
 | 
			
		||||
        if (state.isVisible) inputRef.current?.focus();
 | 
			
		||||
    }, [state.isVisible]);
 | 
			
		||||
    return (
 | 
			
		||||
        <Modal isVisible={visible}>
 | 
			
		||||
        <Modal isVisible={state.isVisible}>
 | 
			
		||||
            <View style={styles.container}>
 | 
			
		||||
                <DText style={styles.text}>Наведите сканер на штрихкод товара или заказа</DText>
 | 
			
		||||
                <DText style={styles.text}>{state.customLabel || getDefaultLabel()}</DText>
 | 
			
		||||
                <BasicButton onPress={() => {
 | 
			
		||||
                    dispatch(closeScanModal());
 | 
			
		||||
                }} style={styles.cancelButton} label={"Отмена"}/>
 | 
			
		||||
@@ -31,7 +32,7 @@ const ScanModal: FC = () => {
 | 
			
		||||
                    style={styles.pseudoInput}
 | 
			
		||||
                    ref={inputRef}
 | 
			
		||||
                    autoFocus={true}
 | 
			
		||||
                    showSoftInputOnFocus={false}
 | 
			
		||||
                    // showSoftInputOnFocus={false}
 | 
			
		||||
                />
 | 
			
		||||
            </View>
 | 
			
		||||
        </Modal>
 | 
			
		||||
@@ -61,9 +62,9 @@ const styles = StyleSheet.create({
 | 
			
		||||
    },
 | 
			
		||||
    pseudoInput: {
 | 
			
		||||
        backgroundColor: "red",
 | 
			
		||||
        opacity: 0,
 | 
			
		||||
        // opacity: 0,
 | 
			
		||||
        position: "absolute",
 | 
			
		||||
        zIndex: -1
 | 
			
		||||
        // zIndex: -1
 | 
			
		||||
    }
 | 
			
		||||
})
 | 
			
		||||
export default ScanModal;
 | 
			
		||||
@@ -51,7 +51,7 @@ const SearchBar: FC<Props> = ({onSearch, onProductSelected}) => {
 | 
			
		||||
            }} style={styles.scanButton} label={"Поиск"}/>
 | 
			
		||||
            <View style={styles.scanImageWrapper}>
 | 
			
		||||
                <TouchableOpacity onPress={() => {
 | 
			
		||||
                    dispatch(openScanModal());
 | 
			
		||||
                    dispatch(openScanModal({customLabel: undefined, data: undefined}));
 | 
			
		||||
                }}>
 | 
			
		||||
                    <Image style={styles.scanImage} source={require('assets/icons/scan.png')}/>
 | 
			
		||||
                </TouchableOpacity>
 | 
			
		||||
 
 | 
			
		||||
@@ -10,11 +10,13 @@ export interface AssemblyState {
 | 
			
		||||
    assembly?: Assembly;
 | 
			
		||||
    selectedProductId?: number;
 | 
			
		||||
    localState?: ASSEMBLY_STATE;
 | 
			
		||||
    selectedProduct?: OrderProduct
 | 
			
		||||
    selectedProduct?: OrderProduct;
 | 
			
		||||
    acceptModalVisible: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const initialState: AssemblyState = {
 | 
			
		||||
    localState: ASSEMBLY_STATE.NOT_STARTED
 | 
			
		||||
    localState: ASSEMBLY_STATE.NOT_STARTED,
 | 
			
		||||
    acceptModalVisible: false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const assembly = createSlice({
 | 
			
		||||
@@ -31,12 +33,18 @@ export const assembly = createSlice({
 | 
			
		||||
            state.assembly = action.payload;
 | 
			
		||||
            state.localState = action.payload.state;
 | 
			
		||||
        },
 | 
			
		||||
        skipCrpt: (state) => {
 | 
			
		||||
            if (!state.assembly) return;
 | 
			
		||||
            state.assembly.state = ASSEMBLY_STATE.ASSEMBLING_PRODUCTS;
 | 
			
		||||
            state.localState = ASSEMBLY_STATE.ASSEMBLING_PRODUCTS
 | 
			
		||||
 | 
			
		||||
        },
 | 
			
		||||
        startAssembly: (state) => {
 | 
			
		||||
            if (!state.assembly) return;
 | 
			
		||||
            state.assembly.createdAt = (new Date()).toDateString();
 | 
			
		||||
 | 
			
		||||
            state.assembly.state = ASSEMBLY_STATE.ASSEMBLING_PRODUCTS;
 | 
			
		||||
            state.localState = ASSEMBLY_STATE.ASSEMBLING_PRODUCTS;
 | 
			
		||||
            state.assembly.state = ASSEMBLY_STATE.SCANNING_CRPT;
 | 
			
		||||
            state.localState = ASSEMBLY_STATE.SCANNING_CRPT;
 | 
			
		||||
        },
 | 
			
		||||
        endAssembly: (state) => {
 | 
			
		||||
            if (!state.assembly) return;
 | 
			
		||||
@@ -94,6 +102,12 @@ export const assembly = createSlice({
 | 
			
		||||
            state.localState = ASSEMBLY_STATE.NOT_STARTED
 | 
			
		||||
            state.selectedProductId = undefined;
 | 
			
		||||
            state.selectedProduct = undefined;
 | 
			
		||||
        },
 | 
			
		||||
        openAcceptModal: (state) => {
 | 
			
		||||
            state.acceptModalVisible = true;
 | 
			
		||||
        },
 | 
			
		||||
        closeAcceptModal: (state) => {
 | 
			
		||||
            state.acceptModalVisible = false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
})
 | 
			
		||||
@@ -107,6 +121,10 @@ export const {
 | 
			
		||||
    endAssembly,
 | 
			
		||||
    confirmAssembly,
 | 
			
		||||
    setLocalState,
 | 
			
		||||
    reset
 | 
			
		||||
    reset,
 | 
			
		||||
    openAcceptModal,
 | 
			
		||||
    closeAcceptModal,
 | 
			
		||||
    skipCrpt
 | 
			
		||||
 | 
			
		||||
} = assembly.actions;
 | 
			
		||||
export default assembly.reducer;
 | 
			
		||||
@@ -1,30 +1,44 @@
 | 
			
		||||
import {createSlice} from "@reduxjs/toolkit";
 | 
			
		||||
import {createSlice, PayloadAction} from "@reduxjs/toolkit";
 | 
			
		||||
 | 
			
		||||
export interface ScanModalState {
 | 
			
		||||
    isVisible: boolean;
 | 
			
		||||
    scannedData?: string
 | 
			
		||||
    scannedData?: string;
 | 
			
		||||
    customLabel?: string;
 | 
			
		||||
    data?: any;
 | 
			
		||||
    uuid?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const initialState: ScanModalState = {
 | 
			
		||||
    isVisible: false,
 | 
			
		||||
    scannedData: undefined
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const scanModalSlice = createSlice({
 | 
			
		||||
    name: 'scanModal',
 | 
			
		||||
    initialState,
 | 
			
		||||
    reducers: {
 | 
			
		||||
        openScanModal: (state) => {
 | 
			
		||||
        openScanModalFormContext(state, action: PayloadAction<{ uuid: string }>) {
 | 
			
		||||
            state.isVisible = true;
 | 
			
		||||
            state.uuid = action.payload.uuid;
 | 
			
		||||
        },
 | 
			
		||||
        openScanModal: (state, action: PayloadAction<{ customLabel?: string, data?: any }>) => {
 | 
			
		||||
            state.isVisible = true
 | 
			
		||||
            if (!action) return;
 | 
			
		||||
            state.customLabel = action.payload.customLabel;
 | 
			
		||||
            state.data = action.payload.data;
 | 
			
		||||
        },
 | 
			
		||||
        closeScanModal: (state) => {
 | 
			
		||||
            state.isVisible = false
 | 
			
		||||
            state.customLabel = undefined;
 | 
			
		||||
            state.data = undefined;
 | 
			
		||||
        },
 | 
			
		||||
        setScannedData: (state, action) => {
 | 
			
		||||
            state.scannedData = action.payload;
 | 
			
		||||
        },
 | 
			
		||||
        resolveUuid: (state) => {
 | 
			
		||||
            state.uuid = undefined;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
export const {openScanModal, closeScanModal, setScannedData} = scanModalSlice.actions;
 | 
			
		||||
export const {openScanModal, closeScanModal, setScannedData, openScanModalFormContext,resolveUuid} = scanModalSlice.actions;
 | 
			
		||||
export default scanModalSlice.reducer;
 | 
			
		||||
							
								
								
									
										24
									
								
								src/hooks/useScan.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/hooks/useScan.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
import {useDispatch, useSelector} from "react-redux";
 | 
			
		||||
import {RootState} from "../redux/store";
 | 
			
		||||
import {useEffect, useState} from "react";
 | 
			
		||||
import {openScanModal} from "../features/scanModal/scanModalSlice";
 | 
			
		||||
 | 
			
		||||
type ScanProps<T> = {
 | 
			
		||||
    label: string;
 | 
			
		||||
    onScanned: (scanned: string, data: T) => void;
 | 
			
		||||
}
 | 
			
		||||
type ReturnValue<T> = {
 | 
			
		||||
    scan: (props: ScanProps<T>) => void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function useScan<T>(): ReturnValue<T> {
 | 
			
		||||
    const state = useSelector((state: RootState) => state.scanModal);
 | 
			
		||||
    const dispatch = useDispatch();
 | 
			
		||||
    const scan = (props: ScanProps<T>) => {
 | 
			
		||||
        dispatch(openScanModal({customLabel: props.label, data: props}));
 | 
			
		||||
    }
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
 | 
			
		||||
    }, [state.scannedData]);
 | 
			
		||||
    return {scan}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										69
									
								
								src/providers/ScanProvider.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								src/providers/ScanProvider.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
			
		||||
import {createContext, useContext, useEffect, useState} from "react";
 | 
			
		||||
import {randomUUID} from "expo-crypto";
 | 
			
		||||
import {RootState, useAppDispatch} from "../redux/store";
 | 
			
		||||
import {openScanModalFormContext, resolveUuid} from "../features/scanModal/scanModalSlice";
 | 
			
		||||
import {useSelector} from "react-redux";
 | 
			
		||||
 | 
			
		||||
type ScanCallback = (data: string) => void;
 | 
			
		||||
type ScanProps = {
 | 
			
		||||
    callback: ScanCallback;
 | 
			
		||||
    data?: any;
 | 
			
		||||
}
 | 
			
		||||
type ScanningContextState = {
 | 
			
		||||
    scan: (props: ScanProps) => void;
 | 
			
		||||
}
 | 
			
		||||
const ScanningContext = createContext<ScanningContextState | undefined>(undefined);
 | 
			
		||||
 | 
			
		||||
export const useScanningContext = () => {
 | 
			
		||||
    const context = useContext(ScanningContext);
 | 
			
		||||
    if (!context) {
 | 
			
		||||
        throw new Error(
 | 
			
		||||
            "useScanningContext must be used within a ScanningContextProvider"
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
    return context;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
type ScanningContextProviderProps = {
 | 
			
		||||
    children: React.ReactNode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type CallbacksState = {
 | 
			
		||||
    uuid: string;
 | 
			
		||||
    callback: (data: string) => void;
 | 
			
		||||
}
 | 
			
		||||
type DataState = {
 | 
			
		||||
    uuid: string;
 | 
			
		||||
    data: any;
 | 
			
		||||
}
 | 
			
		||||
export const ScanningContextProvider = (props: ScanningContextProviderProps) => {
 | 
			
		||||
    const [callbacks, setCallbacks] = useState<CallbacksState[]>([]);
 | 
			
		||||
    const [data, setData] = useState<DataState[]>([]);
 | 
			
		||||
    const dispatch = useAppDispatch();
 | 
			
		||||
    const state = useSelector((state: RootState) => state.scanModal);
 | 
			
		||||
    const scan = (props: ScanProps) => {
 | 
			
		||||
        const {callback, data} = props
 | 
			
		||||
        const uuid = randomUUID();
 | 
			
		||||
        setCallbacks([...callbacks, {uuid, callback}]);
 | 
			
		||||
        dispatch(openScanModalFormContext({uuid}));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const handleChange = () => {
 | 
			
		||||
        if (!state.scannedData) return;
 | 
			
		||||
        const uuid = state.uuid;
 | 
			
		||||
        if (!uuid) return;
 | 
			
		||||
        const callback = callbacks.find(item => item.uuid === uuid);
 | 
			
		||||
        if (!callback) return;
 | 
			
		||||
        callback.callback(state.scannedData);
 | 
			
		||||
        dispatch(resolveUuid());
 | 
			
		||||
        setCallbacks(callbacks.filter(item => item.uuid !== uuid));
 | 
			
		||||
    }
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        handleChange();
 | 
			
		||||
    }, [state.scannedData]);
 | 
			
		||||
    return (
 | 
			
		||||
        <ScanningContext.Provider value={{scan}}>
 | 
			
		||||
            {props.children}
 | 
			
		||||
        </ScanningContext.Provider>
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										61
									
								
								src/screens/AssemblyScreen/AcceptAssemblyModal.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/screens/AssemblyScreen/AcceptAssemblyModal.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
			
		||||
import AcceptModal, {AcceptModalProps} from "../../components/Modals/AcceptModal/AcceptModal";
 | 
			
		||||
import assemblyApi, {AssemblyCreationStatusCode, CreateAssemblyResponse} from "../../api/assemblyApi";
 | 
			
		||||
import Toast from "react-native-toast-message";
 | 
			
		||||
import {openCancelAssemblyModal} from "../../features/cancelAssemblyModal/cancelAssemblyModalSlice";
 | 
			
		||||
import React from "react";
 | 
			
		||||
import {RootState, useAppDispatch} from "../../redux/store";
 | 
			
		||||
import {closeAcceptModal} from "../../features/assembly/assemblySlice";
 | 
			
		||||
import {useSelector} from "react-redux";
 | 
			
		||||
 | 
			
		||||
type RestProps = {
 | 
			
		||||
    onCreated: (response: CreateAssemblyResponse) => void;
 | 
			
		||||
}
 | 
			
		||||
type Props = RestProps;
 | 
			
		||||
 | 
			
		||||
const CreateAssemblyModal = (props: Props) => {
 | 
			
		||||
    const {onCreated} = props
 | 
			
		||||
    const {order, acceptModalVisible} = useSelector((state: RootState) => state.assembly);
 | 
			
		||||
    const dispatch = useAppDispatch();
 | 
			
		||||
 | 
			
		||||
    const showCreateStatusToast = (ok: boolean, message: string) => {
 | 
			
		||||
        Toast.show({
 | 
			
		||||
            type: ok ? 'success' : 'error',
 | 
			
		||||
            text1: 'Создание сборки',
 | 
			
		||||
            text2: message
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const onError = (response: CreateAssemblyResponse) => {
 | 
			
		||||
        const {statusCode, assemblyId, userName} = response;
 | 
			
		||||
        if (statusCode !== AssemblyCreationStatusCode.ASSEMBLY_ALREADY_EXISTS) return;
 | 
			
		||||
        const message =
 | 
			
		||||
            `Заказ собирает ${userName}. Отменить и начать сборку на ваш аккаунт?\n\n` +
 | 
			
		||||
            'Удостоверьтесь, что текущая сборка ошибочна и никто другой её не выполняет.';
 | 
			
		||||
 | 
			
		||||
        dispatch(openCancelAssemblyModal({assemblyId: assemblyId, message: message}));
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const onAccepted = async () => {
 | 
			
		||||
        if (!order) return
 | 
			
		||||
        const response = await assemblyApi.create(order.databaseId);
 | 
			
		||||
        showCreateStatusToast(response.ok, response.message);
 | 
			
		||||
        const handler = response.ok ? onCreated : onError;
 | 
			
		||||
        handler(response);
 | 
			
		||||
        dispatch(closeAcceptModal());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const onRefused = async () => {
 | 
			
		||||
        dispatch(closeAcceptModal())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <AcceptModal
 | 
			
		||||
            visible={acceptModalVisible}
 | 
			
		||||
            text={`Вы уверены что хотите начать сборку заказа ${order?.orderNumber}`}
 | 
			
		||||
            onAccepted={onAccepted}
 | 
			
		||||
            onRefused={onRefused}/>
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default CreateAssemblyModal;
 | 
			
		||||
							
								
								
									
										12
									
								
								src/screens/AssemblyScreen/AssemblyController.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/screens/AssemblyScreen/AssemblyController.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
import {useEffect, useState} from "react";
 | 
			
		||||
import {useSelector} from "react-redux";
 | 
			
		||||
import {RootState} from "../../redux/store";
 | 
			
		||||
 | 
			
		||||
const useAssemblyController = () => {
 | 
			
		||||
    const state = useSelector((state: RootState) => state.assembly);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
 | 
			
		||||
    }, [state.localState]);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										287
									
								
								src/screens/AssemblyScreen/AssemblyControls.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										287
									
								
								src/screens/AssemblyScreen/AssemblyControls.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,287 @@
 | 
			
		||||
import {useDispatch, useSelector} from "react-redux";
 | 
			
		||||
import {RootState, useAppDispatch} from "../../redux/store";
 | 
			
		||||
import {ASSEMBLY_STATE} from "../../types/assembly";
 | 
			
		||||
import {StyleSheet, Text, View} from "react-native";
 | 
			
		||||
import BasicButton, {BasicButtonProps} from "../../components/BasicButton/BasicButton";
 | 
			
		||||
import {BaseButton} from "react-native-gesture-handler";
 | 
			
		||||
import {
 | 
			
		||||
    confirmAssembly, endAssembly,
 | 
			
		||||
    openAcceptModal,
 | 
			
		||||
    selectProduct,
 | 
			
		||||
    setAssembled,
 | 
			
		||||
    skipCrpt
 | 
			
		||||
} from "../../features/assembly/assemblySlice";
 | 
			
		||||
import {useScanningContext} from "../../providers/ScanProvider";
 | 
			
		||||
import assemblyApi from "../../api/assemblyApi";
 | 
			
		||||
import {OkMessageResponse} from "../../types/api";
 | 
			
		||||
import Toast from "react-native-toast-message";
 | 
			
		||||
import {useEffect, useState} from "react";
 | 
			
		||||
import {RFPercentage} from "react-native-responsive-fontsize";
 | 
			
		||||
import {responsiveHeight} from "react-native-responsive-dimensions";
 | 
			
		||||
import {closeLoadingModal, openLoadingModal, setLoadingText} from "../../features/loadingModal/loadingModalSlice";
 | 
			
		||||
import printingService from "../../utils/PrintingService";
 | 
			
		||||
import printingApi from "../../api/printingApi";
 | 
			
		||||
import {setPrinterName} from "../../features/printing/printingSlice";
 | 
			
		||||
import {openReprintModal} from "../../features/reprintModal/reprintModalSlice";
 | 
			
		||||
import {showReward} from "../../features/animations/animationsSlice";
 | 
			
		||||
import {fetchBalance, refreshTransactions} from "../../features/balance/balanceSlice";
 | 
			
		||||
import {NavigationProp, useNavigation} from "@react-navigation/native";
 | 
			
		||||
import {TabNavigatorParamList} from "../MainScreen/MainScreen";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const AssemblyButton = (props: BasicButtonProps) => {
 | 
			
		||||
    return (
 | 
			
		||||
        <BasicButton
 | 
			
		||||
            {...props}
 | 
			
		||||
            containerStyle={{flex: 1}}
 | 
			
		||||
        />
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
const NotStartedButtons = () => {
 | 
			
		||||
    const dispatch = useDispatch();
 | 
			
		||||
    const onStartAssembly = () => {
 | 
			
		||||
        dispatch(openAcceptModal());
 | 
			
		||||
    }
 | 
			
		||||
    return (
 | 
			
		||||
        <AssemblyButton
 | 
			
		||||
            label={"Начать сборку"}
 | 
			
		||||
            onPress={onStartAssembly}
 | 
			
		||||
        />
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ScanningCrptButtons = () => {
 | 
			
		||||
    const dispatch = useAppDispatch();
 | 
			
		||||
 | 
			
		||||
    const state = useSelector((state: RootState) => state.assembly);
 | 
			
		||||
    const [isInitialized, setIsInitialized] = useState(false);
 | 
			
		||||
    const getNeedState = () => {
 | 
			
		||||
        if (!state.order) return {};
 | 
			
		||||
        return state.order.products.reduce((acc, product) => {
 | 
			
		||||
            acc[product.databaseId] = false;
 | 
			
		||||
            return acc;
 | 
			
		||||
        }, {} as { [key: number]: boolean })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const [crptNeedState, setCrptNeedState] = useState<{ [key: number]: boolean }>(getNeedState());
 | 
			
		||||
    const {scan} = useScanningContext();
 | 
			
		||||
    const onAttached = (response: OkMessageResponse) => {
 | 
			
		||||
 | 
			
		||||
        Toast.show({
 | 
			
		||||
            type: response.ok ? "success" : "error",
 | 
			
		||||
            text1: "Сканирование честного знака",
 | 
			
		||||
            text2: response.message,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (!response.ok) return
 | 
			
		||||
 | 
			
		||||
        setCrptNeedState(prevState => {
 | 
			
		||||
            if (!state.selectedProduct) return prevState;
 | 
			
		||||
            return {...prevState, [state.selectedProduct.databaseId]: false}
 | 
			
		||||
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
    const onScanned = (data: string) => {
 | 
			
		||||
        if (!state.selectedProduct) return;
 | 
			
		||||
        assemblyApi.attachCrpt(state.selectedProduct.databaseId, data).then(onAttached);
 | 
			
		||||
    }
 | 
			
		||||
    const onScanClick = () => {
 | 
			
		||||
        scan({callback: onScanned});
 | 
			
		||||
    }
 | 
			
		||||
    const initialize = async () => {
 | 
			
		||||
        dispatch(setLoadingText('Проверка необходимости сканирования честного знака...'))
 | 
			
		||||
        dispatch(openLoadingModal());
 | 
			
		||||
        if (!state.order) return;
 | 
			
		||||
        const responses = await Promise.all(state.order.products.map(async (product) => {
 | 
			
		||||
            const response = await assemblyApi.needCrpt(product.databaseId);
 | 
			
		||||
            return {productId: product.databaseId, need: response.needCrpt};
 | 
			
		||||
        }));
 | 
			
		||||
        // update state
 | 
			
		||||
        setCrptNeedState(responses.reduce((acc, {productId, need}) => {
 | 
			
		||||
            acc[productId] = need;
 | 
			
		||||
            return acc;
 | 
			
		||||
        }, {} as { [key: number]: boolean }));
 | 
			
		||||
 | 
			
		||||
        setIsInitialized(true);
 | 
			
		||||
        dispatch(closeLoadingModal());
 | 
			
		||||
        ////
 | 
			
		||||
        ////
 | 
			
		||||
        dispatch(selectProduct(0))
 | 
			
		||||
    }
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        if (!isInitialized) return;
 | 
			
		||||
        if (Object.values(crptNeedState).every(value => !value))
 | 
			
		||||
            dispatch(skipCrpt())
 | 
			
		||||
    }, [crptNeedState]);
 | 
			
		||||
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        initialize();
 | 
			
		||||
    }, []);
 | 
			
		||||
    return (
 | 
			
		||||
        <>
 | 
			
		||||
            <AssemblyButton
 | 
			
		||||
                disabled={!state.selectedProduct || !crptNeedState[state.selectedProduct.databaseId]}
 | 
			
		||||
                onPress={onScanClick}
 | 
			
		||||
                label={"Сканировать честный знак"}
 | 
			
		||||
            />
 | 
			
		||||
            <AssemblyButton
 | 
			
		||||
                onPress={() => {
 | 
			
		||||
                    dispatch(skipCrpt())
 | 
			
		||||
                }}
 | 
			
		||||
                label={"Пропустить"}
 | 
			
		||||
            />
 | 
			
		||||
        </>
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const AssemblingButtons = () => {
 | 
			
		||||
    const dispatch = useAppDispatch();
 | 
			
		||||
    const state = useSelector((state: RootState) => state.assembly);
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        dispatch(selectProduct(0));
 | 
			
		||||
    }, []);
 | 
			
		||||
    return (
 | 
			
		||||
        <AssemblyButton
 | 
			
		||||
            disabled={state.selectedProduct?.assembled}
 | 
			
		||||
            onPress={() => {
 | 
			
		||||
                if (!state.selectedProduct) return;
 | 
			
		||||
                dispatch(setAssembled({orderProductId: state.selectedProduct.databaseId}));
 | 
			
		||||
            }}
 | 
			
		||||
            label={"Отметить как собранный"}
 | 
			
		||||
        />
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const AllProductsAssembledButtons = () => {
 | 
			
		||||
    const dispatch = useAppDispatch();
 | 
			
		||||
    const state = useSelector((state: RootState) => state.assembly);
 | 
			
		||||
 | 
			
		||||
    const [skipButtonVisible, setSkipButtonVisible] = useState(false);
 | 
			
		||||
 | 
			
		||||
    const onConfirmClick = () => {
 | 
			
		||||
        if (!state.assembly) return;
 | 
			
		||||
        dispatch(setLoadingText('Подтверждение сборки...'))
 | 
			
		||||
        dispatch(openLoadingModal());
 | 
			
		||||
        assemblyApi.confirm(state.assembly.databaseId).then(({ok, message}) => {
 | 
			
		||||
            dispatch(closeLoadingModal());
 | 
			
		||||
            Toast.show({
 | 
			
		||||
                type: ok ? 'success' : 'error',
 | 
			
		||||
                text1: 'Подтверждение сборки',
 | 
			
		||||
                text2: message
 | 
			
		||||
            })
 | 
			
		||||
            if (ok) {
 | 
			
		||||
                dispatch(confirmAssembly());
 | 
			
		||||
            } else {
 | 
			
		||||
                setSkipButtonVisible(true);
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
    const onSkipClick = () => {
 | 
			
		||||
        dispatch(confirmAssembly());
 | 
			
		||||
    }
 | 
			
		||||
    return (
 | 
			
		||||
        <>
 | 
			
		||||
            <AssemblyButton label={"Подтвердить сборку"} onPress={onConfirmClick}/>
 | 
			
		||||
            {skipButtonVisible && <AssemblyButton label={"Пропустить"} onPress={onSkipClick}/>}
 | 
			
		||||
        </>
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ConfirmedButtons = () => {
 | 
			
		||||
    const navigator = useNavigation<NavigationProp<TabNavigatorParamList, 'Barcode'>>();
 | 
			
		||||
 | 
			
		||||
    const dispatch = useAppDispatch();
 | 
			
		||||
    const order = useSelector((state: RootState) => state.assembly.order);
 | 
			
		||||
    const assembly = useSelector((state: RootState) => state.assembly.assembly);
 | 
			
		||||
    const onPrintLabel = () => {
 | 
			
		||||
 | 
			
		||||
        return;
 | 
			
		||||
        if (!order) return;
 | 
			
		||||
        let printer = printingService.getInstance().getPrinter(order.baseMarketplace);
 | 
			
		||||
        dispatch(setLoadingText('Идет печать этикетки...'))
 | 
			
		||||
        dispatch(openLoadingModal())
 | 
			
		||||
        printingApi.getLabel(order.databaseId).then(pdfData => {
 | 
			
		||||
            printingService.getInstance().printPdf(printer, pdfData).then((response) => {
 | 
			
		||||
                dispatch(closeLoadingModal());
 | 
			
		||||
                if (response) return;
 | 
			
		||||
                dispatch(setPrinterName({printerName: printer}));
 | 
			
		||||
                dispatch(openReprintModal());
 | 
			
		||||
            });
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
    const onEndAssembly = () => {
 | 
			
		||||
        if (!assembly) return;
 | 
			
		||||
        assemblyApi.close(assembly.databaseId).then(({ok, message, reward}) => {
 | 
			
		||||
            Toast.show({
 | 
			
		||||
                type: ok ? 'success' : 'error',
 | 
			
		||||
                text1: 'Завершение сборки',
 | 
			
		||||
                text2: message
 | 
			
		||||
            });
 | 
			
		||||
            if (ok) {
 | 
			
		||||
                dispatch(showReward({reward}));
 | 
			
		||||
                dispatch(fetchBalance())
 | 
			
		||||
                dispatch(refreshTransactions())
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            dispatch(endAssembly());
 | 
			
		||||
            navigator.navigate('Barcode');
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        onPrintLabel();
 | 
			
		||||
    }, []);
 | 
			
		||||
    return (<>
 | 
			
		||||
        <AssemblyButton
 | 
			
		||||
            onPress={onPrintLabel}
 | 
			
		||||
            label={"Печать этикетки"}/>
 | 
			
		||||
        <AssemblyButton
 | 
			
		||||
            onPress={onEndAssembly}
 | 
			
		||||
            label={"Завершить сборку"}/>
 | 
			
		||||
    </>)
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const EndedButtons = () => {
 | 
			
		||||
    return (<></>)
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const AssemblyControls = () => {
 | 
			
		||||
    const state = useSelector((state: RootState) => state.assembly);
 | 
			
		||||
    const getButtons = () => {
 | 
			
		||||
        if (state.localState === undefined) return (<BasicButton
 | 
			
		||||
            label={"Ошибка"}
 | 
			
		||||
            disabled={true}
 | 
			
		||||
        />)
 | 
			
		||||
 | 
			
		||||
        switch (state.localState) {
 | 
			
		||||
            case ASSEMBLY_STATE.NOT_STARTED:
 | 
			
		||||
                return <NotStartedButtons/>
 | 
			
		||||
            case ASSEMBLY_STATE.SCANNING_CRPT:
 | 
			
		||||
                return <ScanningCrptButtons/>
 | 
			
		||||
            case ASSEMBLY_STATE.ASSEMBLING_PRODUCTS:
 | 
			
		||||
                return <AssemblingButtons/>
 | 
			
		||||
            case ASSEMBLY_STATE.ALL_PRODUCTS_ASSEMBLED:
 | 
			
		||||
                return <AllProductsAssembledButtons/>
 | 
			
		||||
            case ASSEMBLY_STATE.CONFIRMED:
 | 
			
		||||
                return <ConfirmedButtons/>
 | 
			
		||||
            case ASSEMBLY_STATE.ENDED:
 | 
			
		||||
                return <EndedButtons/>
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return (<>{getButtons()}</>)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const styles = StyleSheet.create({
 | 
			
		||||
    buttonsContainer: {
 | 
			
		||||
        backgroundColor: "white",
 | 
			
		||||
        padding: RFPercentage(2),
 | 
			
		||||
        flex: 0.5,
 | 
			
		||||
        borderRadius: RFPercentage(3),
 | 
			
		||||
        rowGap: responsiveHeight(2)
 | 
			
		||||
    },
 | 
			
		||||
})
 | 
			
		||||
export default AssemblyControls;
 | 
			
		||||
							
								
								
									
										39
									
								
								src/screens/AssemblyScreen/AssemblyProductSelect.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/screens/AssemblyScreen/AssemblyProductSelect.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
import {Order} from "../../types/order";
 | 
			
		||||
import React from "react";
 | 
			
		||||
import {RFPercentage} from "react-native-responsive-fontsize";
 | 
			
		||||
import OrderProductsList from "../../components/OrderCard/OrderProductsList";
 | 
			
		||||
import {StyleSheet, View} from "react-native";
 | 
			
		||||
import {useAppDispatch} from "../../redux/store";
 | 
			
		||||
import {selectProduct} from "../../features/assembly/assemblySlice";
 | 
			
		||||
 | 
			
		||||
type Props = {
 | 
			
		||||
    order: Order;
 | 
			
		||||
}
 | 
			
		||||
const AssemblyProductSelect = (props: Props) => {
 | 
			
		||||
    const dispatch = useAppDispatch();
 | 
			
		||||
    const onSelected = (productId: number) => {
 | 
			
		||||
        dispatch(selectProduct(productId));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <View style={styles.wrapper}>
 | 
			
		||||
 | 
			
		||||
            <OrderProductsList
 | 
			
		||||
                products={props.order.products}
 | 
			
		||||
                onSelected={onSelected}
 | 
			
		||||
            />
 | 
			
		||||
        </View>
 | 
			
		||||
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const styles = StyleSheet.create({
 | 
			
		||||
    wrapper: {
 | 
			
		||||
        flex: 0.5,
 | 
			
		||||
        backgroundColor: "white",
 | 
			
		||||
        borderRadius: RFPercentage(3),
 | 
			
		||||
        padding: RFPercentage(2),
 | 
			
		||||
    },
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
export default AssemblyProductSelect;
 | 
			
		||||
							
								
								
									
										14
									
								
								src/screens/AssemblyScreen/AssemblyScreen.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/screens/AssemblyScreen/AssemblyScreen.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
import {useSelector} from "react-redux";
 | 
			
		||||
import {RootState} from "../../redux/store";
 | 
			
		||||
import {NoOrderScreen} from "../NoOrderScreen/NoOrderScreen";
 | 
			
		||||
import AssemblyView from "./AssemblyView";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const AssemblyScreen = () => {
 | 
			
		||||
    const order = useSelector((state: RootState) => state.assembly.order);
 | 
			
		||||
    return (
 | 
			
		||||
        order ? <AssemblyView order={order}/> : <NoOrderScreen/>
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default AssemblyScreen;
 | 
			
		||||
							
								
								
									
										92
									
								
								src/screens/AssemblyScreen/AssemblyView.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								src/screens/AssemblyScreen/AssemblyView.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,92 @@
 | 
			
		||||
import {Order} from "../../types/order";
 | 
			
		||||
import {RootState, useAppDispatch} from "../../redux/store";
 | 
			
		||||
import {useSelector} from "react-redux";
 | 
			
		||||
import {StyleSheet, View} from "react-native";
 | 
			
		||||
import {responsiveHeight, responsiveWidth} from "react-native-responsive-dimensions";
 | 
			
		||||
import {RFPercentage} from "react-native-responsive-fontsize";
 | 
			
		||||
import AssemblyProductSelect from "./AssemblyProductSelect";
 | 
			
		||||
import ProductImageView from "./ProductImageView";
 | 
			
		||||
import OrderInfoView from "./OrderInfoView";
 | 
			
		||||
import AssemblyControls from "./AssemblyControls";
 | 
			
		||||
import AcceptAssemblyModal from "./AcceptAssemblyModal";
 | 
			
		||||
import assemblyApi, {CreateAssemblyResponse} from "../../api/assemblyApi";
 | 
			
		||||
import {setAssembly, startAssembly} from "../../features/assembly/assemblySlice";
 | 
			
		||||
import {ScanCrptContextProvider} from "./contexts/ScanCrptContext";
 | 
			
		||||
 | 
			
		||||
type Props = {
 | 
			
		||||
    order: Order;
 | 
			
		||||
}
 | 
			
		||||
const AssemblyView = (props: Props) => {
 | 
			
		||||
    const {order} = props;
 | 
			
		||||
    const dispatch = useAppDispatch();
 | 
			
		||||
    const state = useSelector((state: RootState) => state.assembly);
 | 
			
		||||
    const onCreated = (_: CreateAssemblyResponse) => {
 | 
			
		||||
        assemblyApi.getActive().then(assembly => {
 | 
			
		||||
            dispatch(setAssembly(assembly));
 | 
			
		||||
            dispatch(startAssembly());
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <View style={styles.viewContainer}>
 | 
			
		||||
            <View style={styles.topSection}>
 | 
			
		||||
                <AssemblyProductSelect order={order}/>
 | 
			
		||||
                <ProductImageView imageUrl={state.selectedProduct?.imageUrl}/>
 | 
			
		||||
            </View>
 | 
			
		||||
            <View style={styles.bottomSection}>
 | 
			
		||||
                <OrderInfoView
 | 
			
		||||
                    order={order}
 | 
			
		||||
                    selectedProduct={state.selectedProduct}
 | 
			
		||||
                />
 | 
			
		||||
                <View style={styles.buttonsWrapper}>
 | 
			
		||||
                    <AssemblyControls/>
 | 
			
		||||
                </View>
 | 
			
		||||
            </View>
 | 
			
		||||
            <AcceptAssemblyModal
 | 
			
		||||
                onCreated={response => onCreated(response)}
 | 
			
		||||
            />
 | 
			
		||||
        </View>
 | 
			
		||||
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const styles = StyleSheet.create({
 | 
			
		||||
    viewContainer: {
 | 
			
		||||
        width: "100%",
 | 
			
		||||
        height: "100%",
 | 
			
		||||
        display: "flex",
 | 
			
		||||
        paddingHorizontal: responsiveWidth(5),
 | 
			
		||||
        paddingBottom: responsiveHeight(10),
 | 
			
		||||
        rowGap: responsiveHeight(2),
 | 
			
		||||
    },
 | 
			
		||||
    topSection: {
 | 
			
		||||
        display: "flex",
 | 
			
		||||
        flexDirection: "row",
 | 
			
		||||
        columnGap: responsiveWidth(3),
 | 
			
		||||
        height: responsiveHeight(30),
 | 
			
		||||
    },
 | 
			
		||||
    productSelectWrapper: {
 | 
			
		||||
        flex: 0.5,
 | 
			
		||||
        backgroundColor: "white",
 | 
			
		||||
        borderRadius: RFPercentage(3),
 | 
			
		||||
        padding: RFPercentage(2),
 | 
			
		||||
    },
 | 
			
		||||
    bottomSection: {
 | 
			
		||||
        backgroundColor: "red",
 | 
			
		||||
 | 
			
		||||
        flex: 1,
 | 
			
		||||
        borderRadius: RFPercentage(3),
 | 
			
		||||
        flexDirection: "row",
 | 
			
		||||
        columnGap: responsiveWidth(3),
 | 
			
		||||
    },
 | 
			
		||||
    buttonsWrapper: {
 | 
			
		||||
        backgroundColor: "white",
 | 
			
		||||
        padding: RFPercentage(2),
 | 
			
		||||
        flex: 0.5,
 | 
			
		||||
        borderRadius: RFPercentage(3),
 | 
			
		||||
        rowGap: responsiveHeight(2)
 | 
			
		||||
    }
 | 
			
		||||
})
 | 
			
		||||
export default AssemblyView;
 | 
			
		||||
							
								
								
									
										55
									
								
								src/screens/AssemblyScreen/OrderInfoView.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/screens/AssemblyScreen/OrderInfoView.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
import {Order, OrderProduct} from "../../types/order";
 | 
			
		||||
import {ScrollView, StyleSheet, View} from "react-native";
 | 
			
		||||
import DTitle from "../../components/DTitle/DTitle";
 | 
			
		||||
import DText from "../../components/DText/DText";
 | 
			
		||||
import {OrderStatus, OrderStatusDictionary} from "../../features/ordersFilter/ordersFilterSlice";
 | 
			
		||||
import React from "react";
 | 
			
		||||
import {RFPercentage} from "react-native-responsive-fontsize";
 | 
			
		||||
import {responsiveWidth} from "react-native-responsive-dimensions";
 | 
			
		||||
 | 
			
		||||
type Props = {
 | 
			
		||||
    order: Order;
 | 
			
		||||
    selectedProduct?: OrderProduct
 | 
			
		||||
}
 | 
			
		||||
const OrderInfoView = (props: Props) => {
 | 
			
		||||
    const {order, selectedProduct} = props;
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
 | 
			
		||||
        <View style={styles.dataContainer}>
 | 
			
		||||
            <ScrollView>
 | 
			
		||||
                <DTitle style={styles.contentTitle}>Заказ</DTitle>
 | 
			
		||||
                <DText>Номер заказа: {order.orderNumber}</DText>
 | 
			
		||||
                <DText>Маркетплейс: {order.marketplaceName}</DText>
 | 
			
		||||
                <DText>Селлер: {order.sellerName}</DText>
 | 
			
		||||
                <DText>Создан: {order.createdOn}</DText>
 | 
			
		||||
                <DText>Отгрузка: {order.shipmentDate}</DText>
 | 
			
		||||
                <DText>Статус: {OrderStatusDictionary[order.status as OrderStatus]}</DText>
 | 
			
		||||
                <DText>Склад отгрузки: {order.shippingWarehouse}</DText>
 | 
			
		||||
                <DText>Город: {order.city}</DText>
 | 
			
		||||
                <DText>{""}</DText>
 | 
			
		||||
                <DTitle style={styles.contentTitle}>Товар</DTitle>
 | 
			
		||||
                <DText>Арт. DENCO: {selectedProduct?.dencoArticle}</DText>
 | 
			
		||||
                <DText>Арт. поставщика: {selectedProduct?.supplierArticle}</DText>
 | 
			
		||||
                <DText>Фасовка: {selectedProduct?.inBlock} шт.</DText>
 | 
			
		||||
                <DText>Поставщик: {selectedProduct?.supplierName}</DText>
 | 
			
		||||
            </ScrollView>
 | 
			
		||||
 | 
			
		||||
        </View>
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const styles = StyleSheet.create({
 | 
			
		||||
    dataContainer: {
 | 
			
		||||
        backgroundColor: "white",
 | 
			
		||||
        padding: RFPercentage(2),
 | 
			
		||||
        flex: 0.5,
 | 
			
		||||
        borderRadius: RFPercentage(3),
 | 
			
		||||
    },
 | 
			
		||||
    contentTitle: {
 | 
			
		||||
        alignSelf: "center"
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
export default OrderInfoView;
 | 
			
		||||
							
								
								
									
										38
									
								
								src/screens/AssemblyScreen/ProductImageView.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/screens/AssemblyScreen/ProductImageView.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
import {Image, StyleSheet, TouchableOpacity, View} from "react-native";
 | 
			
		||||
import {openImageZoomModal, setImages} from "../../features/imageZoomModal/loadingModalSlice";
 | 
			
		||||
import React from "react";
 | 
			
		||||
import {RFPercentage} from "react-native-responsive-fontsize";
 | 
			
		||||
import {useAppDispatch} from "../../redux/store";
 | 
			
		||||
 | 
			
		||||
type Props = {
 | 
			
		||||
    imageUrl?: string;
 | 
			
		||||
}
 | 
			
		||||
const ProductImageView = (props: Props) => {
 | 
			
		||||
    const {imageUrl} = props;
 | 
			
		||||
    const dispatch = useAppDispatch()
 | 
			
		||||
    return (
 | 
			
		||||
        <TouchableOpacity style={styles.imageWrapper} onPress={() => {
 | 
			
		||||
            if (!imageUrl) return;
 | 
			
		||||
            dispatch(setImages([imageUrl]));
 | 
			
		||||
            dispatch(openImageZoomModal());
 | 
			
		||||
        }}>
 | 
			
		||||
            <View style={{flex: 1}}>
 | 
			
		||||
                <Image style={styles.image} source={{uri: imageUrl}}/>
 | 
			
		||||
            </View>
 | 
			
		||||
        </TouchableOpacity>
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const styles = StyleSheet.create({
 | 
			
		||||
    imageWrapper: {
 | 
			
		||||
        flex: 0.5,
 | 
			
		||||
        backgroundColor: "white",
 | 
			
		||||
        padding: RFPercentage(2),
 | 
			
		||||
        borderRadius: RFPercentage(3)
 | 
			
		||||
    },
 | 
			
		||||
    image: {
 | 
			
		||||
        flex: 1,
 | 
			
		||||
        resizeMode: "contain"
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
export default ProductImageView
 | 
			
		||||
							
								
								
									
										3
									
								
								src/screens/AssemblyScreen/ScanCrptModal.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/screens/AssemblyScreen/ScanCrptModal.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
const ScanCrptModal = () => {
 | 
			
		||||
    return (<ScanBa)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										42
									
								
								src/screens/AssemblyScreen/contexts/AssemblyContext.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/screens/AssemblyScreen/contexts/AssemblyContext.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
// based on ScanCrptContext.tsx (src/screens/AssemblyScreen/contexts/ScanCrptContext.tsx) create new context
 | 
			
		||||
 | 
			
		||||
import {createContext, FC} from "react";
 | 
			
		||||
import {OrderProduct} from "../../../types/order";
 | 
			
		||||
import {useDispatch} from "react-redux";
 | 
			
		||||
 | 
			
		||||
type ScanCrptProps = {
 | 
			
		||||
    orderProduct: OrderProduct;
 | 
			
		||||
    onScanned: (data: string) => void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AssemblyContextState = {
 | 
			
		||||
    scanCrpt: (props: ScanCrptProps) => void;
 | 
			
		||||
}
 | 
			
		||||
const AssemblyContext = createContext<AssemblyContextState | undefined>(
 | 
			
		||||
    undefined
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const useAssemblyContextState = () => {
 | 
			
		||||
    const dispatch = useDispatch();
 | 
			
		||||
 | 
			
		||||
    const scanCrpt = (props: ScanCrptProps) => {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {scanCrpt}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
type AssemblyContextProviderProps = {
 | 
			
		||||
    children: React.ReactNode;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const AssemblyContextProvider: FC<AssemblyContextProviderProps> = ({
 | 
			
		||||
                                                                              children,
 | 
			
		||||
                                                                          }) => {
 | 
			
		||||
    const state = useAssemblyContextState();
 | 
			
		||||
    return (
 | 
			
		||||
        <AssemblyContext.Provider value={state}>
 | 
			
		||||
            {children}
 | 
			
		||||
        </AssemblyContext.Provider>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										39
									
								
								src/screens/AssemblyScreen/contexts/ScanCrptContext.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/screens/AssemblyScreen/contexts/ScanCrptContext.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
import {OrderProduct} from "../../../types/order";
 | 
			
		||||
import {createContext, useContext, useState} from "react";
 | 
			
		||||
 | 
			
		||||
type ScanCrptContextState = {
 | 
			
		||||
    currentOrderProduct?: OrderProduct;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ScanCrptContext = createContext<ScanCrptContextState>({});
 | 
			
		||||
 | 
			
		||||
const useScanCrptContextState = () => {
 | 
			
		||||
    const [currentOrderProduct, setCurrentOrderProduct] = useState<OrderProduct | undefined>(undefined);
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        currentOrderProduct,
 | 
			
		||||
        setCurrentOrderProduct
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
type ScanCrptContextProviderProps = {
 | 
			
		||||
    children: React.ReactNode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const ScanCrptContextProvider = ({children}: ScanCrptContextProviderProps) => {
 | 
			
		||||
    const state = useScanCrptContextState();
 | 
			
		||||
    return (
 | 
			
		||||
        <ScanCrptContext.Provider value={state}>
 | 
			
		||||
            {children}
 | 
			
		||||
        </ScanCrptContext.Provider>
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const useScanCrptContext = () => {
 | 
			
		||||
    const context = useContext(ScanCrptContext);
 | 
			
		||||
    if (!context) {
 | 
			
		||||
        throw new Error(
 | 
			
		||||
            "useScanCrptContext must be used within a ScanCrptContextProvider"
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
    return context;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								src/screens/AssemblyScreen/useCrptState.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/screens/AssemblyScreen/useCrptState.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
// Честный знак
 | 
			
		||||
import {useEffect, useState} from "react";
 | 
			
		||||
import {useSelector} from "react-redux";
 | 
			
		||||
import {RootState} from "../../redux/store";
 | 
			
		||||
import {ASSEMBLY_STATE} from "../../types/assembly";
 | 
			
		||||
import assemblyApi from "../../api/assemblyApi";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const useCrptState = () => {
 | 
			
		||||
    const assemblyState = useSelector((state: RootState) => state.assembly);
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -34,7 +34,7 @@ function BarcodeScreen() {
 | 
			
		||||
        if (!navigationState.history) return;
 | 
			
		||||
        // @ts-ignore
 | 
			
		||||
        let currentTabKey: string = navigationState.history[navigationState.history.length - 1].key;
 | 
			
		||||
        if (currentTabKey.startsWith("Barcode")) if (!isScanModalVisible) dispatch(openScanModal());
 | 
			
		||||
        if (currentTabKey.startsWith("Barcode")) if (!isScanModalVisible) dispatch(openScanModal({}));
 | 
			
		||||
    }, [navigationState]);
 | 
			
		||||
    return (
 | 
			
		||||
        <View style={styles.container}>
 | 
			
		||||
 
 | 
			
		||||
@@ -32,6 +32,7 @@ import CancelAssemblyModal from "../../components/Modals/CancelAssemblyModal/Can
 | 
			
		||||
import {AxiosHeaders} from "axios";
 | 
			
		||||
import apiClient from "../../api/apiClient";
 | 
			
		||||
import AnimationsView from "../../components/Animations/AnimationsView";
 | 
			
		||||
import {ScanningContextProvider} from "../../providers/ScanProvider";
 | 
			
		||||
 | 
			
		||||
function CommonPage() {
 | 
			
		||||
 | 
			
		||||
@@ -65,6 +66,7 @@ function CommonPage() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const checkUpdates = async () => {
 | 
			
		||||
        return;
 | 
			
		||||
        const currentVersion = Constants.manifest2?.extra?.expoClient?.version || Constants.manifest?.version;
 | 
			
		||||
 | 
			
		||||
        applicationApi.getVersion('assemblr').then(({latest_version}) => {
 | 
			
		||||
@@ -119,7 +121,7 @@ function CommonPage() {
 | 
			
		||||
    }, []);
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
 | 
			
		||||
        <ScanningContextProvider>
 | 
			
		||||
            <View style={styles.main}>
 | 
			
		||||
                {isAuthorized ? <MainScreen/> : <LoginScreen/>}
 | 
			
		||||
                <AnimationsView/>
 | 
			
		||||
@@ -132,7 +134,7 @@ function CommonPage() {
 | 
			
		||||
                <SortingModal/>
 | 
			
		||||
                <Toast config={toastConfig}/>
 | 
			
		||||
            </View>
 | 
			
		||||
 | 
			
		||||
        </ScanningContextProvider>
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ import {RFPercentage} from "react-native-responsive-fontsize";
 | 
			
		||||
import {OrderScreenController} from "../OrderScreen/OrderScreen";
 | 
			
		||||
import OrdersScreen from "../OrdersScreen/OrdersScreen";
 | 
			
		||||
import {background} from "../../css/colors";
 | 
			
		||||
import AssemblyScreen from "../AssemblyScreen/AssemblyScreen";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export  type TabNavigatorParamList = {
 | 
			
		||||
@@ -50,7 +51,7 @@ function MainScreen() {
 | 
			
		||||
    const tabScreens: CustomTabProps[] = [
 | 
			
		||||
        {
 | 
			
		||||
            name: "Home",
 | 
			
		||||
            component: OrderScreenController,
 | 
			
		||||
            component: AssemblyScreen,
 | 
			
		||||
            icon: require('assets/icons/home.png'),
 | 
			
		||||
            hidden: false
 | 
			
		||||
        },
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,24 @@
 | 
			
		||||
import {Button, Text, View} from "react-native";
 | 
			
		||||
import {useAppDispatch} from "../../redux/store";
 | 
			
		||||
import {logoutUser, useGetPokemonByNameQuery} from "../../features/auth/authSlice.ts.back";
 | 
			
		||||
import * as process from "process";
 | 
			
		||||
import {NativeModules, Text, View} from "react-native";
 | 
			
		||||
import BasicButton from "../../components/BasicButton/BasicButton";
 | 
			
		||||
import {randomUUID} from "expo-crypto";
 | 
			
		||||
import {useScanningContext} from "../../providers/ScanProvider";
 | 
			
		||||
import {dampingFor} from "react-native-toast-message/lib/src/components/AnimatedContainer";
 | 
			
		||||
 | 
			
		||||
const {AwesomeModule} = NativeModules;
 | 
			
		||||
 | 
			
		||||
function MoneyScreen() {
 | 
			
		||||
    const {scan} = useScanningContext();
 | 
			
		||||
    const a = "123";
 | 
			
		||||
    return (
 | 
			
		||||
        <View>
 | 
			
		||||
            <Text style={{fontSize: 36}}>Money</Text>
 | 
			
		||||
            <BasicButton
 | 
			
		||||
                onPress={() => {
 | 
			
		||||
                    console.log("Button pressed");
 | 
			
		||||
                }}
 | 
			
		||||
                label={"Press me"}
 | 
			
		||||
 | 
			
		||||
            />
 | 
			
		||||
        </View>
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										28
									
								
								src/screens/NoOrderScreen/NoOrderScreen.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/screens/NoOrderScreen/NoOrderScreen.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
import React, {FC} from "react";
 | 
			
		||||
import {StyleSheet, View} from "react-native";
 | 
			
		||||
import DText from "../../components/DText/DText";
 | 
			
		||||
import {responsiveHeight, responsiveWidth} from "react-native-responsive-dimensions";
 | 
			
		||||
 | 
			
		||||
export const NoOrderScreen: FC = () => {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <View style={noOrderStyles.container}>
 | 
			
		||||
            <DText style={noOrderStyles.title}>Заказ не выбран!</DText>
 | 
			
		||||
        </View>
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
const noOrderStyles = StyleSheet.create({
 | 
			
		||||
    container: {
 | 
			
		||||
        justifyContent: "center",
 | 
			
		||||
        flex: 1,
 | 
			
		||||
        rowGap: responsiveHeight(5)
 | 
			
		||||
    },
 | 
			
		||||
    title: {
 | 
			
		||||
        textAlign: "center",
 | 
			
		||||
        fontSize: responsiveWidth(5)
 | 
			
		||||
    },
 | 
			
		||||
    buttonWrapper: {
 | 
			
		||||
        paddingHorizontal: responsiveWidth(20),
 | 
			
		||||
    }
 | 
			
		||||
})
 | 
			
		||||
@@ -33,43 +33,19 @@ import {OrderStatus, OrderStatusDictionary} from "../../features/ordersFilter/or
 | 
			
		||||
import {openCancelAssemblyModal} from "../../features/cancelAssemblyModal/cancelAssemblyModalSlice";
 | 
			
		||||
import {fetchBalance, refreshTransactions} from "../../features/balance/balanceSlice";
 | 
			
		||||
import {showReward} from "../../features/animations/animationsSlice";
 | 
			
		||||
import {NoOrderScreen} from "../NoOrderScreen/NoOrderScreen";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type AssemblyPeriod = {
 | 
			
		||||
    started: Date;
 | 
			
		||||
    ended: Date;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const NoOrderScreen: FC = () => {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <View style={noOrderStyles.container}>
 | 
			
		||||
            <DText style={noOrderStyles.title}>Заказ не выбран!</DText>
 | 
			
		||||
        </View>
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
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<NavigationProp<TabNavigatorParamList, 'Barcode'>>();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    const [orderProductCrpt, setOrderProductCrpt] = useState<Map<number, boolean>>({} as Map<number, boolean>);
 | 
			
		||||
    const scanState = useSelector((state: RootState) => state.scanModal);
 | 
			
		||||
 | 
			
		||||
    const dispatch = useAppDispatch();
 | 
			
		||||
    const assembly = useSelector((state: RootState) => state.assembly.assembly);
 | 
			
		||||
    const assemblyState = useSelector((state: RootState) => state.assembly.localState);
 | 
			
		||||
@@ -102,6 +78,25 @@ const OrderScreen: FC<OrderScreenProps> = ({order}) => {
 | 
			
		||||
                return (<BasicButton containerStyle={styles.buttonContainer}
 | 
			
		||||
                                     onPress={() => setAcceptModalVisible(true)}
 | 
			
		||||
                                     label={"Начать сборку"}/>)
 | 
			
		||||
            case ASSEMBLY_STATE.SCANNING_CRPT:
 | 
			
		||||
                return (
 | 
			
		||||
                    <>
 | 
			
		||||
                        <BasicButton
 | 
			
		||||
                            label={"Сканировать честный знак"}
 | 
			
		||||
                            disabled={selectedProduct ? orderProductCrpt.get(selectedProduct.databaseId) : true}
 | 
			
		||||
                            onPress={() => {
 | 
			
		||||
 | 
			
		||||
                            }}
 | 
			
		||||
                        />
 | 
			
		||||
                        <BasicButton
 | 
			
		||||
                            label={"Пропустить сканирование честного знака"}
 | 
			
		||||
                            onPress={() => {
 | 
			
		||||
                                // dispatch(startAssembly())
 | 
			
		||||
                            }}
 | 
			
		||||
                            style={{backgroundColor: 'orange'}}
 | 
			
		||||
                        />
 | 
			
		||||
                    </>
 | 
			
		||||
                )
 | 
			
		||||
            case ASSEMBLY_STATE.ASSEMBLING_PRODUCTS:
 | 
			
		||||
                return (<BasicButton
 | 
			
		||||
                    containerStyle={styles.buttonContainer}
 | 
			
		||||
@@ -176,6 +171,35 @@ const OrderScreen: FC<OrderScreenProps> = ({order}) => {
 | 
			
		||||
                )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const updateAssemblyState = () => {
 | 
			
		||||
        if (!assembly) return;
 | 
			
		||||
        assemblyApi.updateState(assembly.databaseId, Number(assemblyState)).then(ok => {
 | 
			
		||||
            if (ok) return;
 | 
			
		||||
            Toast.show({
 | 
			
		||||
                type: 'error',
 | 
			
		||||
                text1: 'Обновление состояния',
 | 
			
		||||
                text2: 'Неудалось обновить состояние текущей сборки на сервере'
 | 
			
		||||
            })
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const onConfirmed = () => {
 | 
			
		||||
        printLabel();
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    const onAssemblingProducts = () => {
 | 
			
		||||
        if (!selectedProduct || order.products.length > 1) return;
 | 
			
		||||
        dispatch(setAssembled({orderProductId: selectedProduct.databaseId}));
 | 
			
		||||
    }
 | 
			
		||||
    const onScanCrpt = async () => {
 | 
			
		||||
        const result = await Promise.all(order.products.map(async product => {
 | 
			
		||||
            const {need} = await assemblyApi.needCrpt(product.databaseId);
 | 
			
		||||
            return {databaseId: product.databaseId, need};
 | 
			
		||||
        }))
 | 
			
		||||
        setOrderProductCrpt(new Map(result.map(({databaseId, need}) => [databaseId, need])));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const printLabel = () => {
 | 
			
		||||
        if (!order) return;
 | 
			
		||||
        let printer = printingService.getInstance().getPrinter(order.baseMarketplace);
 | 
			
		||||
@@ -190,28 +214,45 @@ const OrderScreen: FC<OrderScreenProps> = ({order}) => {
 | 
			
		||||
            });
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
    const handleOrderProductCrptChange = () => {
 | 
			
		||||
        const someOfProductsNeedCrpt = orderProductCrpt.entries().map(([orderProductId, need]) => {
 | 
			
		||||
            return need;
 | 
			
		||||
        }).some(need => need);
 | 
			
		||||
        if (!someOfProductsNeedCrpt) {
 | 
			
		||||
            // dispatch(startAssembly());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    const onScanned = () => {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        if (!scanState.scannedData) return;
 | 
			
		||||
        onScanned();
 | 
			
		||||
    }, [scanState.scannedData]);
 | 
			
		||||
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        handleOrderProductCrptChange();
 | 
			
		||||
    }, [orderProductCrpt]);
 | 
			
		||||
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        if (!assembly) return;
 | 
			
		||||
        assemblyApi.updateState(assembly.databaseId, Number(assemblyState)).then(ok => {
 | 
			
		||||
            if (ok) return;
 | 
			
		||||
            Toast.show({
 | 
			
		||||
                type: 'error',
 | 
			
		||||
                text1: 'Обновление состояния',
 | 
			
		||||
                text2: 'Неудалось обновить состояние текущей сборки на сервере'
 | 
			
		||||
            })
 | 
			
		||||
        });
 | 
			
		||||
        switch (assemblyState) {
 | 
			
		||||
            case ASSEMBLY_STATE.CONFIRMED:
 | 
			
		||||
                printLabel();
 | 
			
		||||
                break;
 | 
			
		||||
            case ASSEMBLY_STATE.ASSEMBLING_PRODUCTS:
 | 
			
		||||
                if (!selectedProduct) return;
 | 
			
		||||
                if (order.products.length == 1) {
 | 
			
		||||
                    dispatch(setAssembled({orderProductId: selectedProduct.databaseId}));
 | 
			
		||||
        if (!assemblyState) return;
 | 
			
		||||
        updateAssemblyState();
 | 
			
		||||
        const handlers = {
 | 
			
		||||
            [ASSEMBLY_STATE.CONFIRMED]: onConfirmed,
 | 
			
		||||
            [ASSEMBLY_STATE.ASSEMBLING_PRODUCTS]: onAssemblingProducts,
 | 
			
		||||
            [ASSEMBLY_STATE.SCANNING_CRPT]: onScanCrpt,
 | 
			
		||||
            [ASSEMBLY_STATE.ALL_PRODUCTS_ASSEMBLED]: () => {
 | 
			
		||||
            },
 | 
			
		||||
            [ASSEMBLY_STATE.NOT_STARTED]: () => {
 | 
			
		||||
            },
 | 
			
		||||
            [ASSEMBLY_STATE.ENDED]: () => {
 | 
			
		||||
            }
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        const handler = handlers[assemblyState];
 | 
			
		||||
        handler();
 | 
			
		||||
 | 
			
		||||
    }, [assemblyState]);
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        if (!order) return;
 | 
			
		||||
@@ -250,7 +291,7 @@ const OrderScreen: FC<OrderScreenProps> = ({order}) => {
 | 
			
		||||
                        <DText>Статус: {OrderStatusDictionary[order.status as OrderStatus]}</DText>
 | 
			
		||||
                        <DText>Склад отгрузки: {order.shippingWarehouse}</DText>
 | 
			
		||||
                        <DText>Город: {order.city}</DText>
 | 
			
		||||
                        <DText>{}</DText>
 | 
			
		||||
                        <DText>{""}</DText>
 | 
			
		||||
                        <DTitle style={styles.contentTitle}>Товар</DTitle>
 | 
			
		||||
                        <DText>Арт. DENCO: {selectedProduct?.dencoArticle}</DText>
 | 
			
		||||
                        <DText>Арт. поставщика: {selectedProduct?.supplierArticle}</DText>
 | 
			
		||||
@@ -380,4 +421,3 @@ const styles = StyleSheet.create({
 | 
			
		||||
        textDecorationLine: 'underline'
 | 
			
		||||
    }
 | 
			
		||||
})
 | 
			
		||||
export default OrderScreen;
 | 
			
		||||
							
								
								
									
										4
									
								
								src/types/api.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/types/api.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
export type OkMessageResponse = {
 | 
			
		||||
    ok: boolean,
 | 
			
		||||
    message: string
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
export enum ASSEMBLY_STATE {
 | 
			
		||||
    NOT_STARTED,
 | 
			
		||||
    SCANNING_CRPT,
 | 
			
		||||
    ASSEMBLING_PRODUCTS,
 | 
			
		||||
    ALL_PRODUCTS_ASSEMBLED,
 | 
			
		||||
    CONFIRMED,
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user