first balance
This commit is contained in:
12
src/api/balanceApi.ts
Normal file
12
src/api/balanceApi.ts
Normal file
@@ -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;
|
||||
25
src/contexts/apiContext.tsx
Normal file
25
src/contexts/apiContext.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import {createContext, FC, ReactNode, useContext} from "react";
|
||||
|
||||
type ApiState = {
|
||||
onLogout: () => void;
|
||||
};
|
||||
const apiContext = createContext<ApiState | undefined>(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<ApiContextProviderProps> = ({children, state}) => {
|
||||
|
||||
return (
|
||||
<ApiContextProvider state={state}>
|
||||
{children}
|
||||
</ApiContextProvider>
|
||||
)
|
||||
}
|
||||
36
src/features/balance/balanceSlice.ts
Normal file
36
src/features/balance/balanceSlice.ts
Normal file
@@ -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<BalanceTransaction[]>) => {
|
||||
state.transactions.push(...payload.payload);
|
||||
state.page = state.page + 1;
|
||||
state.loading = false
|
||||
},
|
||||
setIsLoading: (state, action: PayloadAction<boolean>) => {
|
||||
state.loading = action.payload;
|
||||
}
|
||||
},
|
||||
})
|
||||
export const {appendTransactions, setIsLoading} = balanceSlice.actions;
|
||||
export default balanceSlice.reducer;
|
||||
15
src/features/balance/service.ts
Normal file
15
src/features/balance/service.ts
Normal file
@@ -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;
|
||||
33
src/redux/general.ts
Normal file
33
src/redux/general.ts
Normal file
@@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
85
src/screens/ProfileScreen/SettingsView.tsx
Normal file
85
src/screens/ProfileScreen/SettingsView.tsx
Normal file
@@ -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<SettingsElementProps> = ({icon, title, onPress}) => {
|
||||
return (
|
||||
<TouchableOpacity onPress={onPress}>
|
||||
|
||||
<View style={styles.actionsCarouselElementContainer}>
|
||||
<View style={styles.actionsCarouselImageWrapper}>
|
||||
<Image style={styles.actionsCarouselImage} source={icon}/>
|
||||
</View>
|
||||
<DText>{title}</DText>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
const SettingsView: FC = () => {
|
||||
const dispatch = useDispatch();
|
||||
return (
|
||||
<ScrollView horizontal={true}
|
||||
showsVerticalScrollIndicator={false}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
contentContainerStyle={styles.actionsCarousel}
|
||||
>
|
||||
<SettingsElement icon={require('assets/icons/settings/withdraw.png')} title={'Вывод'}/>
|
||||
<SettingsElement icon={require('assets/icons/settings/statistics.png')} title={'Статистика'}/>
|
||||
<SettingsElement icon={require('assets/icons/settings/printer.png')} title={'Принтеры'}/>
|
||||
<SettingsElement onPress={() => {
|
||||
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={'Отменить сборку'}/>
|
||||
</ScrollView>
|
||||
)
|
||||
}
|
||||
|
||||
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;
|
||||
71
src/screens/ProfileScreen/TransactionView.tsx
Normal file
71
src/screens/ProfileScreen/TransactionView.tsx
Normal file
@@ -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<TransactionElementProps> = ({transaction}) => {
|
||||
const formatNumber = (n: number): string => n >= 0 ? `+${n}` : `${n}`;
|
||||
return (
|
||||
<View style={styles.historyElementContainer}>
|
||||
<DText style={{
|
||||
fontSize: responsiveFontSize(2),
|
||||
fontWeight: "500"
|
||||
}}>{formatNumber(transaction.amount)} руб</DText>
|
||||
<DText style={{
|
||||
fontSize: responsiveFontSize(1.5),
|
||||
color: gray,
|
||||
fontWeight: "400"
|
||||
}}>{transaction.description}</DText>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
type TransactionsViewProps = {
|
||||
transactions: BalanceTransaction[];
|
||||
onEndReached: () => void;
|
||||
}
|
||||
const TransactionsView: FC<TransactionsViewProps> = ({transactions, onEndReached}) => {
|
||||
console.log('----------------------------------')
|
||||
console.log(JSON.stringify(transactions, null, 2))
|
||||
return (
|
||||
<FlashList
|
||||
showsHorizontalScrollIndicator={false}
|
||||
showsVerticalScrollIndicator={false}
|
||||
ItemSeparatorComponent={flashListSeparator}
|
||||
data={transactions}
|
||||
keyExtractor={item => item.id.toString()}
|
||||
keyboardShouldPersistTaps={"never"}
|
||||
onEndReachedThreshold={0.5}
|
||||
estimatedItemSize={100}
|
||||
renderItem={item =>
|
||||
<TransactionElement
|
||||
transaction={item.item}
|
||||
/>}
|
||||
onEndReached={onEndReached}
|
||||
>
|
||||
</FlashList>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
historyElementContainer: {
|
||||
width: "100%",
|
||||
backgroundColor: "white",
|
||||
borderRadius: RFPercentage(2),
|
||||
padding: RFPercentage(2),
|
||||
elevation: 1
|
||||
},
|
||||
historyElementsContainer: {
|
||||
rowGap: responsiveHeight(2),
|
||||
},
|
||||
})
|
||||
|
||||
export default TransactionsView;
|
||||
3
src/screens/ProfileScreen/useBalance.tsx
Normal file
3
src/screens/ProfileScreen/useBalance.tsx
Normal file
@@ -0,0 +1,3 @@
|
||||
const useBalance = () => {
|
||||
|
||||
}
|
||||
14
src/types/balance.ts
Normal file
14
src/types/balance.ts
Normal file
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user