crpt
This commit is contained in:
@@ -87,7 +87,7 @@ android {
|
|||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 1
|
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())
|
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'
|
exclude group:'com.squareup.okhttp3', module:'okhttp'
|
||||||
}
|
}
|
||||||
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}")
|
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}")
|
||||||
|
implementation(files("libs/printer-lib-2.2.4.aar"))
|
||||||
|
|
||||||
if (hermesEnabled.toBoolean()) {
|
if (hermesEnabled.toBoolean()) {
|
||||||
implementation("com.facebook.react:hermes-android")
|
implementation("com.facebook.react:hermes-android")
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<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.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.REQUEST_INSTALL_PACKAGES"/>
|
||||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||||
@@ -27,6 +29,7 @@
|
|||||||
<category android:name="android.intent.category.DEFAULT"/>
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
<category android:name="android.intent.category.BROWSABLE"/>
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
<data android:scheme="com.anonymous.Assemblr"/>
|
<data android:scheme="com.anonymous.Assemblr"/>
|
||||||
|
<data android:scheme="exp+assemblr"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" android:exported="false"/>
|
<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;
|
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.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@@ -11,6 +15,49 @@ import com.facebook.react.defaults.DefaultReactActivityDelegate;
|
|||||||
import expo.modules.ReactActivityDelegateWrapper;
|
import expo.modules.ReactActivityDelegateWrapper;
|
||||||
|
|
||||||
public class MainActivity extends ReactActivity {
|
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
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
// Set the theme to AppTheme BEFORE onCreate to support
|
// Set the theme to AppTheme BEFORE onCreate to support
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.anonymous.Assemblr;
|
|||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.facebook.react.PackageList;
|
import com.facebook.react.PackageList;
|
||||||
@@ -20,61 +21,62 @@ import java.util.List;
|
|||||||
|
|
||||||
public class MainApplication extends Application implements ReactApplication {
|
public class MainApplication extends Application implements ReactApplication {
|
||||||
|
|
||||||
private final ReactNativeHost mReactNativeHost =
|
private final ReactNativeHost mReactNativeHost =
|
||||||
new ReactNativeHostWrapper(this, new DefaultReactNativeHost(this) {
|
new ReactNativeHostWrapper(this, new DefaultReactNativeHost(this) {
|
||||||
@Override
|
@Override
|
||||||
public boolean getUseDeveloperSupport() {
|
public boolean getUseDeveloperSupport() {
|
||||||
return BuildConfig.DEBUG;
|
return BuildConfig.DEBUG;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<ReactPackage> getPackages() {
|
protected List<ReactPackage> getPackages() {
|
||||||
@SuppressWarnings("UnnecessaryLocalVariable")
|
@SuppressWarnings("UnnecessaryLocalVariable")
|
||||||
List<ReactPackage> packages = new PackageList(this).getPackages();
|
List<ReactPackage> packages = new PackageList(this).getPackages();
|
||||||
// Packages that cannot be autolinked yet can be added manually here, for example:
|
packages.add(new MyAppPackage());
|
||||||
// packages.add(new MyReactNativePackage());
|
// Packages that cannot be autolinked yet can be added manually here, for example:
|
||||||
return packages;
|
// packages.add(new MyReactNativePackage());
|
||||||
}
|
return packages;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getJSMainModuleName() {
|
protected String getJSMainModuleName() {
|
||||||
return ".expo/.virtual-metro-entry";
|
return ".expo/.virtual-metro-entry";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isNewArchEnabled() {
|
protected boolean isNewArchEnabled() {
|
||||||
return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
|
return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Boolean isHermesEnabled() {
|
protected Boolean isHermesEnabled() {
|
||||||
return BuildConfig.IS_HERMES_ENABLED;
|
return BuildConfig.IS_HERMES_ENABLED;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ReactNativeHost getReactNativeHost() {
|
public ReactNativeHost getReactNativeHost() {
|
||||||
return mReactNativeHost;
|
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;
|
|
||||||
}
|
}
|
||||||
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
|
@Override
|
||||||
public void onConfigurationChanged(@NonNull Configuration newConfig) {
|
public void onCreate() {
|
||||||
super.onConfigurationChanged(newConfig);
|
super.onCreate();
|
||||||
ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig);
|
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",
|
"@shopify/flash-list": "1.4.3",
|
||||||
"axios": "^1.5.0",
|
"axios": "^1.5.0",
|
||||||
"babel-plugin-module-resolver": "^5.0.0",
|
"babel-plugin-module-resolver": "^5.0.0",
|
||||||
|
"eas-cli": "^12.5.1",
|
||||||
"expo": "~49.0.8",
|
"expo": "~49.0.8",
|
||||||
"expo-application": "~5.3.0",
|
"expo-application": "~5.3.0",
|
||||||
|
"expo-av": "~13.4.1",
|
||||||
"expo-build-properties": "~0.8.3",
|
"expo-build-properties": "~0.8.3",
|
||||||
"expo-constants": "~14.4.2",
|
"expo-constants": "~14.4.2",
|
||||||
|
"expo-crypto": "~12.4.1",
|
||||||
"expo-dev-client": "~2.4.12",
|
"expo-dev-client": "~2.4.12",
|
||||||
"expo-file-system": "~15.4.4",
|
"expo-file-system": "~15.4.4",
|
||||||
"expo-intent-launcher": "~10.7.0",
|
"expo-intent-launcher": "~10.7.0",
|
||||||
@@ -59,13 +62,13 @@
|
|||||||
"react-native-webview": "13.2.2",
|
"react-native-webview": "13.2.2",
|
||||||
"react-redux": "^8.1.2",
|
"react-redux": "^8.1.2",
|
||||||
"redux": "^4.2.1",
|
"redux": "^4.2.1",
|
||||||
"rn-openapp": "^2.1.2",
|
"rn-openapp": "^2.1.2"
|
||||||
"expo-av": "~13.4.1"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.20.0",
|
"@babel/core": "^7.20.0",
|
||||||
"@types/react": "~18.2.14",
|
"@types/react": "~18.2.14",
|
||||||
"typescript": "^5.1.3"
|
"typescript": "^5.1.3"
|
||||||
},
|
},
|
||||||
"private": true
|
"private": true,
|
||||||
|
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
export const baseUrl = 'https://assemblr.denco.store';
|
// export const baseUrl = 'https://assemblr.denco.store';
|
||||||
// export const baseUrl = 'http://192.168.1.101:5000';
|
export const baseUrl = 'http://192.168.1.101:5000';
|
||||||
const apiClient = axios.create({
|
const apiClient = axios.create({
|
||||||
baseURL: baseUrl
|
baseURL: baseUrl
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,15 +3,15 @@ import {Assembly} from "../types/assembly";
|
|||||||
import {closeCancelAssemblyModal} from "../features/cancelAssemblyModal/cancelAssemblyModalSlice";
|
import {closeCancelAssemblyModal} from "../features/cancelAssemblyModal/cancelAssemblyModalSlice";
|
||||||
|
|
||||||
const router = '/assembly';
|
const router = '/assembly';
|
||||||
|
export type CreateAssemblyResponse = {
|
||||||
|
ok: boolean,
|
||||||
|
message: string,
|
||||||
|
assemblyId: number,
|
||||||
|
statusCode: AssemblyCreationStatusCode,
|
||||||
|
userName?: string
|
||||||
|
}
|
||||||
const assemblyApi = {
|
const assemblyApi = {
|
||||||
create: async (orderId: number): Promise<{
|
create: async (orderId: number): Promise<CreateAssemblyResponse> => {
|
||||||
ok: boolean,
|
|
||||||
message: string,
|
|
||||||
assemblyId: number,
|
|
||||||
statusCode: AssemblyCreationStatusCode,
|
|
||||||
userName?: string
|
|
||||||
}> => {
|
|
||||||
let response = await apiClient.post(`${router}/create`, {orderId});
|
let response = await apiClient.post(`${router}/create`, {orderId});
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
@@ -42,6 +42,14 @@ const assemblyApi = {
|
|||||||
cancelById: async (assemblyId: number): Promise<{ ok: boolean, message: string }> => {
|
cancelById: async (assemblyId: number): Promise<{ ok: boolean, message: string }> => {
|
||||||
let response = await apiClient.post(`${router}/cancelById`, {assemblyId});
|
let response = await apiClient.post(`${router}/cancelById`, {assemblyId});
|
||||||
return response.data;
|
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 DText from "../DText/DText";
|
||||||
import DTitle from "../DTitle/DTitle";
|
import DTitle from "../DTitle/DTitle";
|
||||||
|
|
||||||
type Props = {
|
export type BasicButtonProps = {
|
||||||
label: string;
|
label: string;
|
||||||
style?: StyleProp<ViewStyle>;
|
style?: StyleProp<ViewStyle>;
|
||||||
containerStyle?: StyleProp<ViewStyle>;
|
containerStyle?: StyleProp<ViewStyle>;
|
||||||
@@ -14,7 +14,7 @@ type Props = {
|
|||||||
disabled?: boolean
|
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 (
|
return (
|
||||||
<TouchableOpacity style={containerStyle} disabled={disabled} onPress={onPress}>
|
<TouchableOpacity style={containerStyle} disabled={disabled} onPress={onPress}>
|
||||||
<View style={[styles.container, style, disabled ? {backgroundColor: "#A0A0A0"} : {}, containerStyle]}>
|
<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 DTitle from "../../DTitle/DTitle";
|
||||||
import BasicButton from "../../BasicButton/BasicButton";
|
import BasicButton from "../../BasicButton/BasicButton";
|
||||||
|
|
||||||
type Props = {
|
export type AcceptModalProps = {
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
text: string;
|
text: string;
|
||||||
onAccepted: () => void;
|
onAccepted: () => void;
|
||||||
onRefused: () => void;
|
onRefused: () => void;
|
||||||
}
|
}
|
||||||
const AcceptModal: FC<Props> = ({visible, text, onAccepted, onRefused}) => {
|
const AcceptModal: FC<AcceptModalProps> = ({visible, text, onAccepted, onRefused}) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal isVisible={visible}>
|
<Modal isVisible={visible}>
|
||||||
|
|||||||
@@ -11,15 +11,16 @@ import {closeScanModal, setScannedData} from "../../features/scanModal/scanModal
|
|||||||
|
|
||||||
const ScanModal: FC = () => {
|
const ScanModal: FC = () => {
|
||||||
const inputRef = useRef<TextInput | null>(null);
|
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();
|
const dispatch = useDispatch();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (visible) inputRef.current?.focus();
|
if (state.isVisible) inputRef.current?.focus();
|
||||||
}, [visible]);
|
}, [state.isVisible]);
|
||||||
return (
|
return (
|
||||||
<Modal isVisible={visible}>
|
<Modal isVisible={state.isVisible}>
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<DText style={styles.text}>Наведите сканер на штрихкод товара или заказа</DText>
|
<DText style={styles.text}>{state.customLabel || getDefaultLabel()}</DText>
|
||||||
<BasicButton onPress={() => {
|
<BasicButton onPress={() => {
|
||||||
dispatch(closeScanModal());
|
dispatch(closeScanModal());
|
||||||
}} style={styles.cancelButton} label={"Отмена"}/>
|
}} style={styles.cancelButton} label={"Отмена"}/>
|
||||||
@@ -31,7 +32,7 @@ const ScanModal: FC = () => {
|
|||||||
style={styles.pseudoInput}
|
style={styles.pseudoInput}
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
showSoftInputOnFocus={false}
|
// showSoftInputOnFocus={false}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</Modal>
|
</Modal>
|
||||||
@@ -61,9 +62,9 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
pseudoInput: {
|
pseudoInput: {
|
||||||
backgroundColor: "red",
|
backgroundColor: "red",
|
||||||
opacity: 0,
|
// opacity: 0,
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
zIndex: -1
|
// zIndex: -1
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
export default ScanModal;
|
export default ScanModal;
|
||||||
@@ -51,7 +51,7 @@ const SearchBar: FC<Props> = ({onSearch, onProductSelected}) => {
|
|||||||
}} style={styles.scanButton} label={"Поиск"}/>
|
}} style={styles.scanButton} label={"Поиск"}/>
|
||||||
<View style={styles.scanImageWrapper}>
|
<View style={styles.scanImageWrapper}>
|
||||||
<TouchableOpacity onPress={() => {
|
<TouchableOpacity onPress={() => {
|
||||||
dispatch(openScanModal());
|
dispatch(openScanModal({customLabel: undefined, data: undefined}));
|
||||||
}}>
|
}}>
|
||||||
<Image style={styles.scanImage} source={require('assets/icons/scan.png')}/>
|
<Image style={styles.scanImage} source={require('assets/icons/scan.png')}/>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|||||||
@@ -10,11 +10,13 @@ export interface AssemblyState {
|
|||||||
assembly?: Assembly;
|
assembly?: Assembly;
|
||||||
selectedProductId?: number;
|
selectedProductId?: number;
|
||||||
localState?: ASSEMBLY_STATE;
|
localState?: ASSEMBLY_STATE;
|
||||||
selectedProduct?: OrderProduct
|
selectedProduct?: OrderProduct;
|
||||||
|
acceptModalVisible: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: AssemblyState = {
|
const initialState: AssemblyState = {
|
||||||
localState: ASSEMBLY_STATE.NOT_STARTED
|
localState: ASSEMBLY_STATE.NOT_STARTED,
|
||||||
|
acceptModalVisible: false
|
||||||
}
|
}
|
||||||
|
|
||||||
export const assembly = createSlice({
|
export const assembly = createSlice({
|
||||||
@@ -31,12 +33,18 @@ export const assembly = createSlice({
|
|||||||
state.assembly = action.payload;
|
state.assembly = action.payload;
|
||||||
state.localState = action.payload.state;
|
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) => {
|
startAssembly: (state) => {
|
||||||
if (!state.assembly) return;
|
if (!state.assembly) return;
|
||||||
state.assembly.createdAt = (new Date()).toDateString();
|
state.assembly.createdAt = (new Date()).toDateString();
|
||||||
|
|
||||||
state.assembly.state = ASSEMBLY_STATE.ASSEMBLING_PRODUCTS;
|
state.assembly.state = ASSEMBLY_STATE.SCANNING_CRPT;
|
||||||
state.localState = ASSEMBLY_STATE.ASSEMBLING_PRODUCTS;
|
state.localState = ASSEMBLY_STATE.SCANNING_CRPT;
|
||||||
},
|
},
|
||||||
endAssembly: (state) => {
|
endAssembly: (state) => {
|
||||||
if (!state.assembly) return;
|
if (!state.assembly) return;
|
||||||
@@ -94,6 +102,12 @@ export const assembly = createSlice({
|
|||||||
state.localState = ASSEMBLY_STATE.NOT_STARTED
|
state.localState = ASSEMBLY_STATE.NOT_STARTED
|
||||||
state.selectedProductId = undefined;
|
state.selectedProductId = undefined;
|
||||||
state.selectedProduct = undefined;
|
state.selectedProduct = undefined;
|
||||||
|
},
|
||||||
|
openAcceptModal: (state) => {
|
||||||
|
state.acceptModalVisible = true;
|
||||||
|
},
|
||||||
|
closeAcceptModal: (state) => {
|
||||||
|
state.acceptModalVisible = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -107,6 +121,10 @@ export const {
|
|||||||
endAssembly,
|
endAssembly,
|
||||||
confirmAssembly,
|
confirmAssembly,
|
||||||
setLocalState,
|
setLocalState,
|
||||||
reset
|
reset,
|
||||||
|
openAcceptModal,
|
||||||
|
closeAcceptModal,
|
||||||
|
skipCrpt
|
||||||
|
|
||||||
} = assembly.actions;
|
} = assembly.actions;
|
||||||
export default assembly.reducer;
|
export default assembly.reducer;
|
||||||
@@ -1,30 +1,44 @@
|
|||||||
import {createSlice} from "@reduxjs/toolkit";
|
import {createSlice, PayloadAction} from "@reduxjs/toolkit";
|
||||||
|
|
||||||
export interface ScanModalState {
|
export interface ScanModalState {
|
||||||
isVisible: boolean;
|
isVisible: boolean;
|
||||||
scannedData?: string
|
scannedData?: string;
|
||||||
|
customLabel?: string;
|
||||||
|
data?: any;
|
||||||
|
uuid?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: ScanModalState = {
|
const initialState: ScanModalState = {
|
||||||
isVisible: false,
|
isVisible: false,
|
||||||
scannedData: undefined
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const scanModalSlice = createSlice({
|
export const scanModalSlice = createSlice({
|
||||||
name: 'scanModal',
|
name: 'scanModal',
|
||||||
initialState,
|
initialState,
|
||||||
reducers: {
|
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
|
state.isVisible = true
|
||||||
|
if (!action) return;
|
||||||
|
state.customLabel = action.payload.customLabel;
|
||||||
|
state.data = action.payload.data;
|
||||||
},
|
},
|
||||||
closeScanModal: (state) => {
|
closeScanModal: (state) => {
|
||||||
state.isVisible = false
|
state.isVisible = false
|
||||||
|
state.customLabel = undefined;
|
||||||
|
state.data = undefined;
|
||||||
},
|
},
|
||||||
setScannedData: (state, action) => {
|
setScannedData: (state, action) => {
|
||||||
state.scannedData = action.payload;
|
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;
|
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;
|
if (!navigationState.history) return;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
let currentTabKey: string = navigationState.history[navigationState.history.length - 1].key;
|
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]);
|
}, [navigationState]);
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import CancelAssemblyModal from "../../components/Modals/CancelAssemblyModal/Can
|
|||||||
import {AxiosHeaders} from "axios";
|
import {AxiosHeaders} from "axios";
|
||||||
import apiClient from "../../api/apiClient";
|
import apiClient from "../../api/apiClient";
|
||||||
import AnimationsView from "../../components/Animations/AnimationsView";
|
import AnimationsView from "../../components/Animations/AnimationsView";
|
||||||
|
import {ScanningContextProvider} from "../../providers/ScanProvider";
|
||||||
|
|
||||||
function CommonPage() {
|
function CommonPage() {
|
||||||
|
|
||||||
@@ -65,6 +66,7 @@ function CommonPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const checkUpdates = async () => {
|
const checkUpdates = async () => {
|
||||||
|
return;
|
||||||
const currentVersion = Constants.manifest2?.extra?.expoClient?.version || Constants.manifest?.version;
|
const currentVersion = Constants.manifest2?.extra?.expoClient?.version || Constants.manifest?.version;
|
||||||
|
|
||||||
applicationApi.getVersion('assemblr').then(({latest_version}) => {
|
applicationApi.getVersion('assemblr').then(({latest_version}) => {
|
||||||
@@ -119,20 +121,20 @@ function CommonPage() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<ScanningContextProvider>
|
||||||
<View style={styles.main}>
|
<View style={styles.main}>
|
||||||
{isAuthorized ? <MainScreen/> : <LoginScreen/>}
|
{isAuthorized ? <MainScreen/> : <LoginScreen/>}
|
||||||
<AnimationsView/>
|
<AnimationsView/>
|
||||||
<View style={[styles.overlay, {display: dim ? 'flex' : 'none'}]}/>
|
<View style={[styles.overlay, {display: dim ? 'flex' : 'none'}]}/>
|
||||||
<LoadingModal/>
|
<LoadingModal/>
|
||||||
<ScanModal/>
|
<ScanModal/>
|
||||||
<ReprintModal/>
|
<ReprintModal/>
|
||||||
<CancelAssemblyModal/>
|
<CancelAssemblyModal/>
|
||||||
<ImageZoomModal/>
|
<ImageZoomModal/>
|
||||||
<SortingModal/>
|
<SortingModal/>
|
||||||
<Toast config={toastConfig}/>
|
<Toast config={toastConfig}/>
|
||||||
</View>
|
</View>
|
||||||
|
</ScanningContextProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {RFPercentage} from "react-native-responsive-fontsize";
|
|||||||
import {OrderScreenController} from "../OrderScreen/OrderScreen";
|
import {OrderScreenController} from "../OrderScreen/OrderScreen";
|
||||||
import OrdersScreen from "../OrdersScreen/OrdersScreen";
|
import OrdersScreen from "../OrdersScreen/OrdersScreen";
|
||||||
import {background} from "../../css/colors";
|
import {background} from "../../css/colors";
|
||||||
|
import AssemblyScreen from "../AssemblyScreen/AssemblyScreen";
|
||||||
|
|
||||||
|
|
||||||
export type TabNavigatorParamList = {
|
export type TabNavigatorParamList = {
|
||||||
@@ -50,7 +51,7 @@ function MainScreen() {
|
|||||||
const tabScreens: CustomTabProps[] = [
|
const tabScreens: CustomTabProps[] = [
|
||||||
{
|
{
|
||||||
name: "Home",
|
name: "Home",
|
||||||
component: OrderScreenController,
|
component: AssemblyScreen,
|
||||||
icon: require('assets/icons/home.png'),
|
icon: require('assets/icons/home.png'),
|
||||||
hidden: false
|
hidden: false
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,13 +1,24 @@
|
|||||||
import {Button, Text, View} from "react-native";
|
import {NativeModules, Text, View} from "react-native";
|
||||||
import {useAppDispatch} from "../../redux/store";
|
import BasicButton from "../../components/BasicButton/BasicButton";
|
||||||
import {logoutUser, useGetPokemonByNameQuery} from "../../features/auth/authSlice.ts.back";
|
import {randomUUID} from "expo-crypto";
|
||||||
import * as process from "process";
|
import {useScanningContext} from "../../providers/ScanProvider";
|
||||||
|
import {dampingFor} from "react-native-toast-message/lib/src/components/AnimatedContainer";
|
||||||
|
|
||||||
|
const {AwesomeModule} = NativeModules;
|
||||||
|
|
||||||
function MoneyScreen() {
|
function MoneyScreen() {
|
||||||
|
const {scan} = useScanningContext();
|
||||||
|
const a = "123";
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<Text style={{fontSize: 36}}>Money</Text>
|
<Text style={{fontSize: 36}}>Money</Text>
|
||||||
|
<BasicButton
|
||||||
|
onPress={() => {
|
||||||
|
console.log("Button pressed");
|
||||||
|
}}
|
||||||
|
label={"Press me"}
|
||||||
|
|
||||||
|
/>
|
||||||
</View>
|
</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 {openCancelAssemblyModal} from "../../features/cancelAssemblyModal/cancelAssemblyModalSlice";
|
||||||
import {fetchBalance, refreshTransactions} from "../../features/balance/balanceSlice";
|
import {fetchBalance, refreshTransactions} from "../../features/balance/balanceSlice";
|
||||||
import {showReward} from "../../features/animations/animationsSlice";
|
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 = {
|
type OrderScreenProps = {
|
||||||
order: Order;
|
order: Order;
|
||||||
}
|
}
|
||||||
const OrderScreen: FC<OrderScreenProps> = ({order}) => {
|
const OrderScreen: FC<OrderScreenProps> = ({order}) => {
|
||||||
const navigator = useNavigation<NavigationProp<TabNavigatorParamList, 'Barcode'>>();
|
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 dispatch = useAppDispatch();
|
||||||
const assembly = useSelector((state: RootState) => state.assembly.assembly);
|
const assembly = useSelector((state: RootState) => state.assembly.assembly);
|
||||||
const assemblyState = useSelector((state: RootState) => state.assembly.localState);
|
const assemblyState = useSelector((state: RootState) => state.assembly.localState);
|
||||||
@@ -102,6 +78,25 @@ const OrderScreen: FC<OrderScreenProps> = ({order}) => {
|
|||||||
return (<BasicButton containerStyle={styles.buttonContainer}
|
return (<BasicButton containerStyle={styles.buttonContainer}
|
||||||
onPress={() => setAcceptModalVisible(true)}
|
onPress={() => setAcceptModalVisible(true)}
|
||||||
label={"Начать сборку"}/>)
|
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:
|
case ASSEMBLY_STATE.ASSEMBLING_PRODUCTS:
|
||||||
return (<BasicButton
|
return (<BasicButton
|
||||||
containerStyle={styles.buttonContainer}
|
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 = () => {
|
const printLabel = () => {
|
||||||
if (!order) return;
|
if (!order) return;
|
||||||
let printer = printingService.getInstance().getPrinter(order.baseMarketplace);
|
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(() => {
|
useEffect(() => {
|
||||||
if (!assembly) return;
|
if (!assembly) return;
|
||||||
assemblyApi.updateState(assembly.databaseId, Number(assemblyState)).then(ok => {
|
if (!assemblyState) return;
|
||||||
if (ok) return;
|
updateAssemblyState();
|
||||||
Toast.show({
|
const handlers = {
|
||||||
type: 'error',
|
[ASSEMBLY_STATE.CONFIRMED]: onConfirmed,
|
||||||
text1: 'Обновление состояния',
|
[ASSEMBLY_STATE.ASSEMBLING_PRODUCTS]: onAssemblingProducts,
|
||||||
text2: 'Неудалось обновить состояние текущей сборки на сервере'
|
[ASSEMBLY_STATE.SCANNING_CRPT]: onScanCrpt,
|
||||||
})
|
[ASSEMBLY_STATE.ALL_PRODUCTS_ASSEMBLED]: () => {
|
||||||
});
|
},
|
||||||
switch (assemblyState) {
|
[ASSEMBLY_STATE.NOT_STARTED]: () => {
|
||||||
case ASSEMBLY_STATE.CONFIRMED:
|
},
|
||||||
printLabel();
|
[ASSEMBLY_STATE.ENDED]: () => {
|
||||||
break;
|
}
|
||||||
case ASSEMBLY_STATE.ASSEMBLING_PRODUCTS:
|
|
||||||
if (!selectedProduct) return;
|
|
||||||
if (order.products.length == 1) {
|
|
||||||
dispatch(setAssembled({orderProductId: selectedProduct.databaseId}));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
const handler = handlers[assemblyState];
|
||||||
|
handler();
|
||||||
|
|
||||||
}, [assemblyState]);
|
}, [assemblyState]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!order) return;
|
if (!order) return;
|
||||||
@@ -250,7 +291,7 @@ const OrderScreen: FC<OrderScreenProps> = ({order}) => {
|
|||||||
<DText>Статус: {OrderStatusDictionary[order.status as OrderStatus]}</DText>
|
<DText>Статус: {OrderStatusDictionary[order.status as OrderStatus]}</DText>
|
||||||
<DText>Склад отгрузки: {order.shippingWarehouse}</DText>
|
<DText>Склад отгрузки: {order.shippingWarehouse}</DText>
|
||||||
<DText>Город: {order.city}</DText>
|
<DText>Город: {order.city}</DText>
|
||||||
<DText>{}</DText>
|
<DText>{""}</DText>
|
||||||
<DTitle style={styles.contentTitle}>Товар</DTitle>
|
<DTitle style={styles.contentTitle}>Товар</DTitle>
|
||||||
<DText>Арт. DENCO: {selectedProduct?.dencoArticle}</DText>
|
<DText>Арт. DENCO: {selectedProduct?.dencoArticle}</DText>
|
||||||
<DText>Арт. поставщика: {selectedProduct?.supplierArticle}</DText>
|
<DText>Арт. поставщика: {selectedProduct?.supplierArticle}</DText>
|
||||||
@@ -380,4 +421,3 @@ const styles = StyleSheet.create({
|
|||||||
textDecorationLine: 'underline'
|
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 {
|
export enum ASSEMBLY_STATE {
|
||||||
NOT_STARTED,
|
NOT_STARTED,
|
||||||
|
SCANNING_CRPT,
|
||||||
ASSEMBLING_PRODUCTS,
|
ASSEMBLING_PRODUCTS,
|
||||||
ALL_PRODUCTS_ASSEMBLED,
|
ALL_PRODUCTS_ASSEMBLED,
|
||||||
CONFIRMED,
|
CONFIRMED,
|
||||||
|
|||||||
Reference in New Issue
Block a user