diff --git a/.env b/.env new file mode 100644 index 0000000..a1f8977 --- /dev/null +++ b/.env @@ -0,0 +1 @@ +EXPO_PUBLIC_DEV_MODE=true \ No newline at end of file diff --git a/src/api/balanceApi.ts b/src/api/balanceApi.ts new file mode 100644 index 0000000..2cbc4b5 --- /dev/null +++ b/src/api/balanceApi.ts @@ -0,0 +1,12 @@ +import {BalanceTransaction} from "../types/balance"; +import apiClient from "./apiClient"; + +const router = '/balance'; +const balanceApi = { + getTransactions: async (page: number): Promise<{ balanceTransactions: BalanceTransaction[] }> => { + const response = await apiClient.get(`${router}/transactions`, {params: {page}}); + return response.data; + } +} + +export default balanceApi; \ No newline at end of file diff --git a/src/contexts/apiContext.tsx b/src/contexts/apiContext.tsx new file mode 100644 index 0000000..17b85a8 --- /dev/null +++ b/src/contexts/apiContext.tsx @@ -0,0 +1,25 @@ +import {createContext, FC, ReactNode, useContext} from "react"; + +type ApiState = { + onLogout: () => void; +}; +const apiContext = createContext(undefined); + +const useApiContext = () => { + const context = useContext(apiContext); + if (!context) throw new Error('useApiContext must be used within ApiContextProvider'); + return context; +} + +type ApiContextProviderProps = { + children: ReactNode; + state: ApiState; +} +export const ApiContextProvider: FC = ({children, state}) => { + + return ( + + {children} + + ) +} diff --git a/src/features/balance/balanceSlice.ts b/src/features/balance/balanceSlice.ts new file mode 100644 index 0000000..bd10df2 --- /dev/null +++ b/src/features/balance/balanceSlice.ts @@ -0,0 +1,36 @@ +import {BalanceTransaction} from "../../types/balance"; +import {createAsyncThunk, createSlice, PayloadAction} from "@reduxjs/toolkit"; +import {RootState} from "../../redux/store"; + +const name = 'balance'; + +interface BalanceState { + transactions: BalanceTransaction[]; + balance: number; + page: number; + loading: boolean; +} + +const initialState: BalanceState = { + transactions: [], + balance: 0, + page: 1, + loading: false +} + +export const balanceSlice = createSlice({ + name: name, + initialState, + reducers: { + appendTransactions: (state, payload: PayloadAction) => { + state.transactions.push(...payload.payload); + state.page = state.page + 1; + state.loading = false + }, + setIsLoading: (state, action: PayloadAction) => { + state.loading = action.payload; + } + }, +}) +export const {appendTransactions, setIsLoading} = balanceSlice.actions; +export default balanceSlice.reducer; \ No newline at end of file diff --git a/src/features/balance/service.ts b/src/features/balance/service.ts new file mode 100644 index 0000000..260c5a8 --- /dev/null +++ b/src/features/balance/service.ts @@ -0,0 +1,15 @@ +import {createApi} from '@reduxjs/toolkit/query/react' + + +import {axiosBaseQuery} from "../../redux/general"; +import {EndpointBuilder} from "@reduxjs/toolkit/dist/query/endpointDefinitions"; +import {BalanceTransaction} from "../../types/balance"; + +export const balanceApi = createApi({ + reducerPath: 'balanceApi', + baseQuery: axiosBaseQuery(), + endpoints: (builder) => ({ + getTransactions: builder.query({query: () => ({url: "/transactions", method: 'get'})}) + }) +}) +export const {useGetTransactionsQuery} = balanceApi; \ No newline at end of file diff --git a/src/redux/general.ts b/src/redux/general.ts new file mode 100644 index 0000000..727a7ec --- /dev/null +++ b/src/redux/general.ts @@ -0,0 +1,33 @@ +import {AxiosError, AxiosRequestConfig} from "axios"; +import {BaseQueryFn} from "@reduxjs/toolkit/query"; +import apiClient from "../api/apiClient"; + +export const axiosBaseQuery = + (): BaseQueryFn< + { + url: string + method?: AxiosRequestConfig['method'] + data?: AxiosRequestConfig['data'] + params?: AxiosRequestConfig['params'] + headers?: AxiosRequestConfig['headers'] + } + > => + async ({method, data, params, headers}) => { + try { + const result = await apiClient({ + method, + data, + params, + headers, + }) + return {data: result.data} + } catch (axiosError) { + const err = axiosError as AxiosError + return { + error: { + status: err.response?.status, + data: err.response?.data || err.message, + }, + } + } + } \ No newline at end of file diff --git a/src/screens/ProfileScreen/SettingsView.tsx b/src/screens/ProfileScreen/SettingsView.tsx new file mode 100644 index 0000000..919f6b6 --- /dev/null +++ b/src/screens/ProfileScreen/SettingsView.tsx @@ -0,0 +1,85 @@ +import {GestureResponderEvent, Image, ScrollView, StyleSheet, TouchableOpacity, View} from "react-native"; +import DText from "../../components/DText/DText"; +import {RFPercentage} from "react-native-responsive-fontsize"; +import {FC} from "react"; +import assemblyApi from "../../api/assemblyApi"; +import Toast from "react-native-toast-message"; +import {reset} from "../../features/assembly/assemblySlice"; +import {useDispatch} from "react-redux"; +import {responsiveWidth} from "react-native-responsive-dimensions"; + +type SettingsElementProps = { + icon: any; + title: string; + onPress?: (event: GestureResponderEvent) => void + +} +const SettingsElement: FC = ({icon, title, onPress}) => { + return ( + + + + + + + {title} + + + + ) +} + +const SettingsView: FC = () => { + const dispatch = useDispatch(); + return ( + + + + + { + assemblyApi.cancel().then(response => { + + Toast.show({ + type: response.ok ? "success" : "error", + text1: "Отмена сборки", + text2: response.message, + }); + dispatch(reset()); + }) + }} icon={require('assets/icons/settings/close.png')} title={'Отменить сборку'}/> + + ) +} + +const styles = StyleSheet.create({ + actionsCarouselElementContainer: { + display: "flex", + alignItems: "center", + }, + actionsCarouselImageWrapper: { + width: RFPercentage(10), + height: RFPercentage(10), + backgroundColor: "white", + borderRadius: 100, + borderWidth: RFPercentage(0.3), + padding: RFPercentage(1.5), + justifyContent: "center", + alignItems: "center" + }, + actionsCarouselImage: { + resizeMode: "contain", + flex: 1, + width: "100%", + height: "100%", + }, + actionsCarousel: { + flexDirection: "row", + columnGap: responsiveWidth(3), + }, +}); + +export default SettingsView; \ No newline at end of file diff --git a/src/screens/ProfileScreen/TransactionView.tsx b/src/screens/ProfileScreen/TransactionView.tsx new file mode 100644 index 0000000..1480600 --- /dev/null +++ b/src/screens/ProfileScreen/TransactionView.tsx @@ -0,0 +1,71 @@ +import {ScrollView, StyleSheet, View} from "react-native"; +import DText from "../../components/DText/DText"; +import {responsiveFontSize, responsiveHeight} from "react-native-responsive-dimensions"; +import {gray} from "../../css/colors"; +import {BalanceTransaction} from "../../types/balance"; +import {FC} from "react"; +import {RFPercentage} from "react-native-responsive-fontsize"; +import {FlashList} from "@shopify/flash-list"; +import flashListSeparator from "../../components/FlashListSeparator/FlashListSeparator"; + +type TransactionElementProps = { + transaction: BalanceTransaction; +} +const TransactionElement: FC = ({transaction}) => { + const formatNumber = (n: number): string => n >= 0 ? `+${n}` : `${n}`; + return ( + + {formatNumber(transaction.amount)} руб + {transaction.description} + + ) +} + +type TransactionsViewProps = { + transactions: BalanceTransaction[]; + onEndReached: () => void; +} +const TransactionsView: FC = ({transactions, onEndReached}) => { + console.log('----------------------------------') + console.log(JSON.stringify(transactions, null, 2)) + return ( + item.id.toString()} + keyboardShouldPersistTaps={"never"} + onEndReachedThreshold={0.5} + estimatedItemSize={100} + renderItem={item => + } + onEndReached={onEndReached} + > + + ) +} + +const styles = StyleSheet.create({ + historyElementContainer: { + width: "100%", + backgroundColor: "white", + borderRadius: RFPercentage(2), + padding: RFPercentage(2), + elevation: 1 + }, + historyElementsContainer: { + rowGap: responsiveHeight(2), + }, +}) + +export default TransactionsView; \ No newline at end of file diff --git a/src/screens/ProfileScreen/useBalance.tsx b/src/screens/ProfileScreen/useBalance.tsx new file mode 100644 index 0000000..d293cd1 --- /dev/null +++ b/src/screens/ProfileScreen/useBalance.tsx @@ -0,0 +1,3 @@ +const useBalance = () => { + +} \ No newline at end of file diff --git a/src/types/balance.ts b/src/types/balance.ts new file mode 100644 index 0000000..b49a11c --- /dev/null +++ b/src/types/balance.ts @@ -0,0 +1,14 @@ +export enum BalanceTransactionType { + TOP_UP, + WITHDRAW +} + +export type BalanceTransaction = { + id: number; + type: BalanceTransactionType; + userId: number; + amount: number; + description: string; + jsonData?: object; + createdAt: string; +} \ No newline at end of file