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;
|
||||
@@ -20,61 +21,62 @@ import java.util.List;
|
||||
|
||||
public class MainApplication extends Application implements ReactApplication {
|
||||
|
||||
private final ReactNativeHost mReactNativeHost =
|
||||
new ReactNativeHostWrapper(this, new DefaultReactNativeHost(this) {
|
||||
@Override
|
||||
public boolean getUseDeveloperSupport() {
|
||||
return BuildConfig.DEBUG;
|
||||
}
|
||||
private final ReactNativeHost mReactNativeHost =
|
||||
new ReactNativeHostWrapper(this, new DefaultReactNativeHost(this) {
|
||||
@Override
|
||||
public boolean getUseDeveloperSupport() {
|
||||
return BuildConfig.DEBUG;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ReactPackage> getPackages() {
|
||||
@SuppressWarnings("UnnecessaryLocalVariable")
|
||||
List<ReactPackage> packages = new PackageList(this).getPackages();
|
||||
// Packages that cannot be autolinked yet can be added manually here, for example:
|
||||
// packages.add(new MyReactNativePackage());
|
||||
return packages;
|
||||
}
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getJSMainModuleName() {
|
||||
return ".expo/.virtual-metro-entry";
|
||||
}
|
||||
@Override
|
||||
protected String getJSMainModuleName() {
|
||||
return ".expo/.virtual-metro-entry";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isNewArchEnabled() {
|
||||
return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
|
||||
}
|
||||
@Override
|
||||
protected boolean isNewArchEnabled() {
|
||||
return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean isHermesEnabled() {
|
||||
return BuildConfig.IS_HERMES_ENABLED;
|
||||
}
|
||||
});
|
||||
@Override
|
||||
protected Boolean isHermesEnabled() {
|
||||
return BuildConfig.IS_HERMES_ENABLED;
|
||||
}
|
||||
});
|
||||
|
||||
@Override
|
||||
public ReactNativeHost getReactNativeHost() {
|
||||
return mReactNativeHost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
SoLoader.init(this, /* native exopackage */ false);
|
||||
if (!BuildConfig.REACT_NATIVE_UNSTABLE_USE_RUNTIME_SCHEDULER_ALWAYS) {
|
||||
ReactFeatureFlags.unstable_useRuntimeSchedulerAlways = false;
|
||||
@Override
|
||||
public ReactNativeHost getReactNativeHost() {
|
||||
return mReactNativeHost;
|
||||
}
|
||||
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
||||
// If you opted-in for the New Architecture, we load the native entry point for this app.
|
||||
DefaultNewArchitectureEntryPoint.load();
|
||||
}
|
||||
ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
|
||||
ApplicationLifecycleDispatcher.onApplicationCreate(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(@NonNull Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig);
|
||||
}
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
SoLoader.init(this, /* native exopackage */ false);
|
||||
if (!BuildConfig.REACT_NATIVE_UNSTABLE_USE_RUNTIME_SCHEDULER_ALWAYS) {
|
||||
ReactFeatureFlags.unstable_useRuntimeSchedulerAlways = false;
|
||||
}
|
||||
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
||||
// If you opted-in for the New Architecture, we load the native entry point for this app.
|
||||
DefaultNewArchitectureEntryPoint.load();
|
||||
}
|
||||
ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
|
||||
ApplicationLifecycleDispatcher.onApplicationCreate(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(@NonNull Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
export type CreateAssemblyResponse = {
|
||||
ok: boolean,
|
||||
message: string,
|
||||
assemblyId: number,
|
||||
statusCode: AssemblyCreationStatusCode,
|
||||
userName?: string
|
||||
}
|
||||
const assemblyApi = {
|
||||
create: async (orderId: number): Promise<{
|
||||
ok: boolean,
|
||||
message: string,
|
||||
assemblyId: number,
|
||||
statusCode: AssemblyCreationStatusCode,
|
||||
userName?: string
|
||||
}> => {
|
||||
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,20 +121,20 @@ function CommonPage() {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
||||
<View style={styles.main}>
|
||||
{isAuthorized ? <MainScreen/> : <LoginScreen/>}
|
||||
<AnimationsView/>
|
||||
<View style={[styles.overlay, {display: dim ? 'flex' : 'none'}]}/>
|
||||
<LoadingModal/>
|
||||
<ScanModal/>
|
||||
<ReprintModal/>
|
||||
<CancelAssemblyModal/>
|
||||
<ImageZoomModal/>
|
||||
<SortingModal/>
|
||||
<Toast config={toastConfig}/>
|
||||
</View>
|
||||
|
||||
<ScanningContextProvider>
|
||||
<View style={styles.main}>
|
||||
{isAuthorized ? <MainScreen/> : <LoginScreen/>}
|
||||
<AnimationsView/>
|
||||
<View style={[styles.overlay, {display: dim ? 'flex' : 'none'}]}/>
|
||||
<LoadingModal/>
|
||||
<ScanModal/>
|
||||
<ReprintModal/>
|
||||
<CancelAssemblyModal/>
|
||||
<ImageZoomModal/>
|
||||
<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}));
|
||||
}
|
||||
break;
|
||||
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]: () => {
|
||||
}
|
||||
|
||||
}
|
||||
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