From 55ba06295e31a4de200083be5e1bce8597381197 Mon Sep 17 00:00:00 2001 From: admin Date: Sat, 12 Oct 2024 03:55:19 +0300 Subject: [PATCH] crpt --- android/app/build.gradle | 3 +- android/app/src/main/AndroidManifest.xml | 3 + .../com/anonymous/Assemblr/AwesomeModule.java | 41 +++ .../com/anonymous/Assemblr/MainActivity.java | 47 +++ .../anonymous/Assemblr/MainApplication.java | 102 ++++--- .../com/anonymous/Assemblr/MyAppPackage.java | 30 ++ package.json | 9 +- src/api/apiClient.ts | 4 +- src/api/assemblyApi.ts | 24 +- src/components/BasicButton/BasicButton.tsx | 4 +- .../Modals/AcceptModal/AcceptModal.tsx | 4 +- src/components/SearchBar/ScanModal.tsx | 17 +- src/components/SearchBar/SearchBar.tsx | 2 +- src/features/assembly/assemblySlice.ts | 28 +- src/features/scanModal/scanModalSlice.ts | 24 +- src/hooks/useScan.tsx | 24 ++ src/providers/ScanProvider.tsx | 69 +++++ .../AssemblyScreen/AcceptAssemblyModal.tsx | 61 ++++ .../AssemblyScreen/AssemblyController.tsx | 12 + .../AssemblyScreen/AssemblyControls.tsx | 287 ++++++++++++++++++ .../AssemblyScreen/AssemblyProductSelect.tsx | 39 +++ src/screens/AssemblyScreen/AssemblyScreen.tsx | 14 + src/screens/AssemblyScreen/AssemblyView.tsx | 92 ++++++ src/screens/AssemblyScreen/OrderInfoView.tsx | 55 ++++ .../AssemblyScreen/ProductImageView.tsx | 38 +++ src/screens/AssemblyScreen/ScanCrptModal.tsx | 3 + .../contexts/AssemblyContext.tsx | 42 +++ .../contexts/ScanCrptContext.tsx | 39 +++ src/screens/AssemblyScreen/useCrptState.tsx | 14 + src/screens/BarcodeScreen/BarcodeScreen.tsx | 2 +- src/screens/CommonPage/CommonPage.tsx | 30 +- src/screens/MainScreen/MainScreen.tsx | 3 +- src/screens/MoneyScreen/MoneyScreen.tsx | 19 +- src/screens/NoOrderScreen/NoOrderScreen.tsx | 28 ++ src/screens/OrderScreen/OrderScreen.tsx | 138 ++++++--- src/types/api.ts | 4 + src/types/assembly.ts | 1 + 37 files changed, 1200 insertions(+), 156 deletions(-) create mode 100644 android/app/src/main/java/com/anonymous/Assemblr/AwesomeModule.java create mode 100644 android/app/src/main/java/com/anonymous/Assemblr/MyAppPackage.java create mode 100644 src/hooks/useScan.tsx create mode 100644 src/providers/ScanProvider.tsx create mode 100644 src/screens/AssemblyScreen/AcceptAssemblyModal.tsx create mode 100644 src/screens/AssemblyScreen/AssemblyController.tsx create mode 100644 src/screens/AssemblyScreen/AssemblyControls.tsx create mode 100644 src/screens/AssemblyScreen/AssemblyProductSelect.tsx create mode 100644 src/screens/AssemblyScreen/AssemblyScreen.tsx create mode 100644 src/screens/AssemblyScreen/AssemblyView.tsx create mode 100644 src/screens/AssemblyScreen/OrderInfoView.tsx create mode 100644 src/screens/AssemblyScreen/ProductImageView.tsx create mode 100644 src/screens/AssemblyScreen/ScanCrptModal.tsx create mode 100644 src/screens/AssemblyScreen/contexts/AssemblyContext.tsx create mode 100644 src/screens/AssemblyScreen/contexts/ScanCrptContext.tsx create mode 100644 src/screens/AssemblyScreen/useCrptState.tsx create mode 100644 src/screens/NoOrderScreen/NoOrderScreen.tsx create mode 100644 src/types/api.ts diff --git a/android/app/build.gradle b/android/app/build.gradle index 6d959bd..c17a6fb 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -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") diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 0184304..a4eac01 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,8 @@ + + @@ -27,6 +29,7 @@ + diff --git a/android/app/src/main/java/com/anonymous/Assemblr/AwesomeModule.java b/android/app/src/main/java/com/anonymous/Assemblr/AwesomeModule.java new file mode 100644 index 0000000..3d9ab86 --- /dev/null +++ b/android/app/src/main/java/com/anonymous/Assemblr/AwesomeModule.java @@ -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 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"); + } +} diff --git a/android/app/src/main/java/com/anonymous/Assemblr/MainActivity.java b/android/app/src/main/java/com/anonymous/Assemblr/MainActivity.java index c1d3a42..6e5cee7 100644 --- a/android/app/src/main/java/com/anonymous/Assemblr/MainActivity.java +++ b/android/app/src/main/java/com/anonymous/Assemblr/MainActivity.java @@ -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 diff --git a/android/app/src/main/java/com/anonymous/Assemblr/MainApplication.java b/android/app/src/main/java/com/anonymous/Assemblr/MainApplication.java index 35c6de4..45c010c 100644 --- a/android/app/src/main/java/com/anonymous/Assemblr/MainApplication.java +++ b/android/app/src/main/java/com/anonymous/Assemblr/MainApplication.java @@ -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 getPackages() { - @SuppressWarnings("UnnecessaryLocalVariable") - List 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 getPackages() { + @SuppressWarnings("UnnecessaryLocalVariable") + List 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); + } } diff --git a/android/app/src/main/java/com/anonymous/Assemblr/MyAppPackage.java b/android/app/src/main/java/com/anonymous/Assemblr/MyAppPackage.java new file mode 100644 index 0000000..6627015 --- /dev/null +++ b/android/app/src/main/java/com/anonymous/Assemblr/MyAppPackage.java @@ -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 createViewManagers(ReactApplicationContext reactContext) { + return Collections.emptyList(); + } + + @Override + public List createNativeModules( + ReactApplicationContext reactContext) { + List modules = new ArrayList<>(); + + modules.add(new AwesomeModule(reactContext)); + + return modules; + } +} diff --git a/package.json b/package.json index f694795..02b8a1b 100644 --- a/package.json +++ b/package.json @@ -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" } diff --git a/src/api/apiClient.ts b/src/api/apiClient.ts index cba44b6..a9f8f03 100644 --- a/src/api/apiClient.ts +++ b/src/api/apiClient.ts @@ -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 }); diff --git a/src/api/assemblyApi.ts b/src/api/assemblyApi.ts index d3cf8db..9999ba7 100644 --- a/src/api/assemblyApi.ts +++ b/src/api/assemblyApi.ts @@ -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 => { 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; } } diff --git a/src/components/BasicButton/BasicButton.tsx b/src/components/BasicButton/BasicButton.tsx index da5c335..b07f3b3 100644 --- a/src/components/BasicButton/BasicButton.tsx +++ b/src/components/BasicButton/BasicButton.tsx @@ -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; containerStyle?: StyleProp; @@ -14,7 +14,7 @@ type Props = { disabled?: boolean }; -const BasicButton: FC = ({label, onPress, containerStyle, style, isUnset = false, disabled = false}) => { +const BasicButton: FC = ({label, onPress, containerStyle, style, isUnset = false, disabled = false}) => { return ( diff --git a/src/components/Modals/AcceptModal/AcceptModal.tsx b/src/components/Modals/AcceptModal/AcceptModal.tsx index 69c769a..fdee411 100644 --- a/src/components/Modals/AcceptModal/AcceptModal.tsx +++ b/src/components/Modals/AcceptModal/AcceptModal.tsx @@ -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 = ({visible, text, onAccepted, onRefused}) => { +const AcceptModal: FC = ({visible, text, onAccepted, onRefused}) => { return ( diff --git a/src/components/SearchBar/ScanModal.tsx b/src/components/SearchBar/ScanModal.tsx index db61d15..1f354e8 100644 --- a/src/components/SearchBar/ScanModal.tsx +++ b/src/components/SearchBar/ScanModal.tsx @@ -11,15 +11,16 @@ import {closeScanModal, setScannedData} from "../../features/scanModal/scanModal const ScanModal: FC = () => { const inputRef = useRef(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 ( - + - Наведите сканер на штрихкод товара или заказа + {state.customLabel || getDefaultLabel()} { dispatch(closeScanModal()); }} style={styles.cancelButton} label={"Отмена"}/> @@ -31,7 +32,7 @@ const ScanModal: FC = () => { style={styles.pseudoInput} ref={inputRef} autoFocus={true} - showSoftInputOnFocus={false} + // showSoftInputOnFocus={false} /> @@ -61,9 +62,9 @@ const styles = StyleSheet.create({ }, pseudoInput: { backgroundColor: "red", - opacity: 0, + // opacity: 0, position: "absolute", - zIndex: -1 + // zIndex: -1 } }) export default ScanModal; \ No newline at end of file diff --git a/src/components/SearchBar/SearchBar.tsx b/src/components/SearchBar/SearchBar.tsx index 9251e08..a9c9f63 100644 --- a/src/components/SearchBar/SearchBar.tsx +++ b/src/components/SearchBar/SearchBar.tsx @@ -51,7 +51,7 @@ const SearchBar: FC = ({onSearch, onProductSelected}) => { }} style={styles.scanButton} label={"Поиск"}/> { - dispatch(openScanModal()); + dispatch(openScanModal({customLabel: undefined, data: undefined})); }}> diff --git a/src/features/assembly/assemblySlice.ts b/src/features/assembly/assemblySlice.ts index 71afcfc..e4ed4d3 100644 --- a/src/features/assembly/assemblySlice.ts +++ b/src/features/assembly/assemblySlice.ts @@ -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; \ No newline at end of file diff --git a/src/features/scanModal/scanModalSlice.ts b/src/features/scanModal/scanModalSlice.ts index 202369d..0caba74 100644 --- a/src/features/scanModal/scanModalSlice.ts +++ b/src/features/scanModal/scanModalSlice.ts @@ -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; \ No newline at end of file diff --git a/src/hooks/useScan.tsx b/src/hooks/useScan.tsx new file mode 100644 index 0000000..7353591 --- /dev/null +++ b/src/hooks/useScan.tsx @@ -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 = { + label: string; + onScanned: (scanned: string, data: T) => void; +} +type ReturnValue = { + scan: (props: ScanProps) => void; +} + +function useScan(): ReturnValue { + const state = useSelector((state: RootState) => state.scanModal); + const dispatch = useDispatch(); + const scan = (props: ScanProps) => { + dispatch(openScanModal({customLabel: props.label, data: props})); + } + useEffect(() => { + + }, [state.scannedData]); + return {scan} +} \ No newline at end of file diff --git a/src/providers/ScanProvider.tsx b/src/providers/ScanProvider.tsx new file mode 100644 index 0000000..83c58ba --- /dev/null +++ b/src/providers/ScanProvider.tsx @@ -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(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([]); + const [data, setData] = useState([]); + 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 ( + + {props.children} + + ) +} \ No newline at end of file diff --git a/src/screens/AssemblyScreen/AcceptAssemblyModal.tsx b/src/screens/AssemblyScreen/AcceptAssemblyModal.tsx new file mode 100644 index 0000000..035a66f --- /dev/null +++ b/src/screens/AssemblyScreen/AcceptAssemblyModal.tsx @@ -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 ( + + ) +} + +export default CreateAssemblyModal; \ No newline at end of file diff --git a/src/screens/AssemblyScreen/AssemblyController.tsx b/src/screens/AssemblyScreen/AssemblyController.tsx new file mode 100644 index 0000000..7dd4215 --- /dev/null +++ b/src/screens/AssemblyScreen/AssemblyController.tsx @@ -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]); +} diff --git a/src/screens/AssemblyScreen/AssemblyControls.tsx b/src/screens/AssemblyScreen/AssemblyControls.tsx new file mode 100644 index 0000000..ddba471 --- /dev/null +++ b/src/screens/AssemblyScreen/AssemblyControls.tsx @@ -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 ( + + ) +} +const NotStartedButtons = () => { + const dispatch = useDispatch(); + const onStartAssembly = () => { + dispatch(openAcceptModal()); + } + return ( + + ) + +} + +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 ( + <> + + { + dispatch(skipCrpt()) + }} + label={"Пропустить"} + /> + + ) +} + +const AssemblingButtons = () => { + const dispatch = useAppDispatch(); + const state = useSelector((state: RootState) => state.assembly); + useEffect(() => { + dispatch(selectProduct(0)); + }, []); + return ( + { + 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 ( + <> + + {skipButtonVisible && } + + ) + +} + +const ConfirmedButtons = () => { + const navigator = useNavigation>(); + + 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 (<> + + + ) + +} + +const EndedButtons = () => { + return (<>) + +} + +const AssemblyControls = () => { + const state = useSelector((state: RootState) => state.assembly); + const getButtons = () => { + if (state.localState === undefined) return () + + switch (state.localState) { + case ASSEMBLY_STATE.NOT_STARTED: + return + case ASSEMBLY_STATE.SCANNING_CRPT: + return + case ASSEMBLY_STATE.ASSEMBLING_PRODUCTS: + return + case ASSEMBLY_STATE.ALL_PRODUCTS_ASSEMBLED: + return + case ASSEMBLY_STATE.CONFIRMED: + return + case ASSEMBLY_STATE.ENDED: + return + } + } + return (<>{getButtons()}) +} + +const styles = StyleSheet.create({ + buttonsContainer: { + backgroundColor: "white", + padding: RFPercentage(2), + flex: 0.5, + borderRadius: RFPercentage(3), + rowGap: responsiveHeight(2) + }, +}) +export default AssemblyControls; \ No newline at end of file diff --git a/src/screens/AssemblyScreen/AssemblyProductSelect.tsx b/src/screens/AssemblyScreen/AssemblyProductSelect.tsx new file mode 100644 index 0000000..6df485a --- /dev/null +++ b/src/screens/AssemblyScreen/AssemblyProductSelect.tsx @@ -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 ( + + + + + + ) +} + +const styles = StyleSheet.create({ + wrapper: { + flex: 0.5, + backgroundColor: "white", + borderRadius: RFPercentage(3), + padding: RFPercentage(2), + }, +}) + +export default AssemblyProductSelect; \ No newline at end of file diff --git a/src/screens/AssemblyScreen/AssemblyScreen.tsx b/src/screens/AssemblyScreen/AssemblyScreen.tsx new file mode 100644 index 0000000..8c08727 --- /dev/null +++ b/src/screens/AssemblyScreen/AssemblyScreen.tsx @@ -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 ? : + ) +} + +export default AssemblyScreen; \ No newline at end of file diff --git a/src/screens/AssemblyScreen/AssemblyView.tsx b/src/screens/AssemblyScreen/AssemblyView.tsx new file mode 100644 index 0000000..5a7841d --- /dev/null +++ b/src/screens/AssemblyScreen/AssemblyView.tsx @@ -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 ( + + + + + + + + + + + + onCreated(response)} + /> + + + ) +} + + +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; \ No newline at end of file diff --git a/src/screens/AssemblyScreen/OrderInfoView.tsx b/src/screens/AssemblyScreen/OrderInfoView.tsx new file mode 100644 index 0000000..c340c14 --- /dev/null +++ b/src/screens/AssemblyScreen/OrderInfoView.tsx @@ -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 ( + + + + Заказ + Номер заказа: {order.orderNumber} + Маркетплейс: {order.marketplaceName} + Селлер: {order.sellerName} + Создан: {order.createdOn} + Отгрузка: {order.shipmentDate} + Статус: {OrderStatusDictionary[order.status as OrderStatus]} + Склад отгрузки: {order.shippingWarehouse} + Город: {order.city} + {""} + Товар + Арт. DENCO: {selectedProduct?.dencoArticle} + Арт. поставщика: {selectedProduct?.supplierArticle} + Фасовка: {selectedProduct?.inBlock} шт. + Поставщик: {selectedProduct?.supplierName} + + + + ) +} + +const styles = StyleSheet.create({ + dataContainer: { + backgroundColor: "white", + padding: RFPercentage(2), + flex: 0.5, + borderRadius: RFPercentage(3), + }, + contentTitle: { + alignSelf: "center" + }, + +}) + +export default OrderInfoView; \ No newline at end of file diff --git a/src/screens/AssemblyScreen/ProductImageView.tsx b/src/screens/AssemblyScreen/ProductImageView.tsx new file mode 100644 index 0000000..0906129 --- /dev/null +++ b/src/screens/AssemblyScreen/ProductImageView.tsx @@ -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 ( + { + if (!imageUrl) return; + dispatch(setImages([imageUrl])); + dispatch(openImageZoomModal()); + }}> + + + + + ) +} + +const styles = StyleSheet.create({ + imageWrapper: { + flex: 0.5, + backgroundColor: "white", + padding: RFPercentage(2), + borderRadius: RFPercentage(3) + }, + image: { + flex: 1, + resizeMode: "contain" + }, +}); +export default ProductImageView \ No newline at end of file diff --git a/src/screens/AssemblyScreen/ScanCrptModal.tsx b/src/screens/AssemblyScreen/ScanCrptModal.tsx new file mode 100644 index 0000000..af87870 --- /dev/null +++ b/src/screens/AssemblyScreen/ScanCrptModal.tsx @@ -0,0 +1,3 @@ +const ScanCrptModal = () => { + return ( void; +} + +type AssemblyContextState = { + scanCrpt: (props: ScanCrptProps) => void; +} +const AssemblyContext = createContext( + undefined +); + +const useAssemblyContextState = () => { + const dispatch = useDispatch(); + + const scanCrpt = (props: ScanCrptProps) => { + + } + + return {scanCrpt} +}; + +type AssemblyContextProviderProps = { + children: React.ReactNode; +}; + +export const AssemblyContextProvider: FC = ({ + children, + }) => { + const state = useAssemblyContextState(); + return ( + + {children} + + ); +}; diff --git a/src/screens/AssemblyScreen/contexts/ScanCrptContext.tsx b/src/screens/AssemblyScreen/contexts/ScanCrptContext.tsx new file mode 100644 index 0000000..3670642 --- /dev/null +++ b/src/screens/AssemblyScreen/contexts/ScanCrptContext.tsx @@ -0,0 +1,39 @@ +import {OrderProduct} from "../../../types/order"; +import {createContext, useContext, useState} from "react"; + +type ScanCrptContextState = { + currentOrderProduct?: OrderProduct; +} + +const ScanCrptContext = createContext({}); + +const useScanCrptContextState = () => { + const [currentOrderProduct, setCurrentOrderProduct] = useState(undefined); + + return { + currentOrderProduct, + setCurrentOrderProduct + } +} +type ScanCrptContextProviderProps = { + children: React.ReactNode; +} + +export const ScanCrptContextProvider = ({children}: ScanCrptContextProviderProps) => { + const state = useScanCrptContextState(); + return ( + + {children} + + ) +} + +export const useScanCrptContext = () => { + const context = useContext(ScanCrptContext); + if (!context) { + throw new Error( + "useScanCrptContext must be used within a ScanCrptContextProvider" + ); + } + return context; +} \ No newline at end of file diff --git a/src/screens/AssemblyScreen/useCrptState.tsx b/src/screens/AssemblyScreen/useCrptState.tsx new file mode 100644 index 0000000..67cdf58 --- /dev/null +++ b/src/screens/AssemblyScreen/useCrptState.tsx @@ -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 { + } +} \ No newline at end of file diff --git a/src/screens/BarcodeScreen/BarcodeScreen.tsx b/src/screens/BarcodeScreen/BarcodeScreen.tsx index 397e55f..6825200 100644 --- a/src/screens/BarcodeScreen/BarcodeScreen.tsx +++ b/src/screens/BarcodeScreen/BarcodeScreen.tsx @@ -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 ( diff --git a/src/screens/CommonPage/CommonPage.tsx b/src/screens/CommonPage/CommonPage.tsx index ac6665b..87af439 100644 --- a/src/screens/CommonPage/CommonPage.tsx +++ b/src/screens/CommonPage/CommonPage.tsx @@ -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 ( - - - {isAuthorized ? : } - - - - - - - - - - - + + + {isAuthorized ? : } + + + + + + + + + + + ) } diff --git a/src/screens/MainScreen/MainScreen.tsx b/src/screens/MainScreen/MainScreen.tsx index 3182c0c..aaf3e96 100644 --- a/src/screens/MainScreen/MainScreen.tsx +++ b/src/screens/MainScreen/MainScreen.tsx @@ -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 }, diff --git a/src/screens/MoneyScreen/MoneyScreen.tsx b/src/screens/MoneyScreen/MoneyScreen.tsx index 1b51bed..9f58409 100644 --- a/src/screens/MoneyScreen/MoneyScreen.tsx +++ b/src/screens/MoneyScreen/MoneyScreen.tsx @@ -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 ( Money + { + console.log("Button pressed"); + }} + label={"Press me"} + /> ) } diff --git a/src/screens/NoOrderScreen/NoOrderScreen.tsx b/src/screens/NoOrderScreen/NoOrderScreen.tsx new file mode 100644 index 0000000..f583799 --- /dev/null +++ b/src/screens/NoOrderScreen/NoOrderScreen.tsx @@ -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 ( + + Заказ не выбран! + + ) +} +const noOrderStyles = StyleSheet.create({ + container: { + justifyContent: "center", + flex: 1, + rowGap: responsiveHeight(5) + }, + title: { + textAlign: "center", + fontSize: responsiveWidth(5) + }, + buttonWrapper: { + paddingHorizontal: responsiveWidth(20), + } +}) \ No newline at end of file diff --git a/src/screens/OrderScreen/OrderScreen.tsx b/src/screens/OrderScreen/OrderScreen.tsx index 5126679..96de4db 100644 --- a/src/screens/OrderScreen/OrderScreen.tsx +++ b/src/screens/OrderScreen/OrderScreen.tsx @@ -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 ( - - Заказ не выбран! - - ) -} -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 = ({order}) => { const navigator = useNavigation>(); + + const [orderProductCrpt, setOrderProductCrpt] = useState>({} as Map); + 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 = ({order}) => { return ( setAcceptModalVisible(true)} label={"Начать сборку"}/>) + case ASSEMBLY_STATE.SCANNING_CRPT: + return ( + <> + { + + }} + /> + { + // dispatch(startAssembly()) + }} + style={{backgroundColor: 'orange'}} + /> + + ) case ASSEMBLY_STATE.ASSEMBLING_PRODUCTS: return ( = ({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 = ({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 = ({order}) => { Статус: {OrderStatusDictionary[order.status as OrderStatus]} Склад отгрузки: {order.shippingWarehouse} Город: {order.city} - {} + {""} Товар Арт. DENCO: {selectedProduct?.dencoArticle} Арт. поставщика: {selectedProduct?.supplierArticle} @@ -380,4 +421,3 @@ const styles = StyleSheet.create({ textDecorationLine: 'underline' } }) -export default OrderScreen; \ No newline at end of file diff --git a/src/types/api.ts b/src/types/api.ts new file mode 100644 index 0000000..abf9b9b --- /dev/null +++ b/src/types/api.ts @@ -0,0 +1,4 @@ +export type OkMessageResponse = { + ok: boolean, + message: string +} \ No newline at end of file diff --git a/src/types/assembly.ts b/src/types/assembly.ts index a2a0ed6..eb9f1f4 100644 --- a/src/types/assembly.ts +++ b/src/types/assembly.ts @@ -1,5 +1,6 @@ export enum ASSEMBLY_STATE { NOT_STARTED, + SCANNING_CRPT, ASSEMBLING_PRODUCTS, ALL_PRODUCTS_ASSEMBLED, CONFIRMED,