first commit
@@ -1,131 +1,42 @@
|
|||||||
/**
|
import React, { useEffect, useState } from 'react';
|
||||||
* Sample React Native App
|
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
||||||
* https://github.com/facebook/react-native
|
import { Alert, Linking, LogBox, StatusBar, View } from 'react-native';
|
||||||
*
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||||
* @format
|
import { AlertNotificationRoot } from 'react-native-alert-notification';
|
||||||
*/
|
import { PersistGate } from 'redux-persist/integration/react';
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
import { persistor, store } from './src/Redux/Store.js';
|
||||||
|
import Routes from './src/Navigation/Routes.jsx';
|
||||||
|
import { NavigationContainer } from '@react-navigation/native';
|
||||||
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
|
import colors from './src/Constant/Color';
|
||||||
|
import { TextEncoder } from 'text-encoding';
|
||||||
|
LogBox.ignoreAllLogs(true);
|
||||||
|
|
||||||
import React from 'react';
|
const App = () => {
|
||||||
import type {PropsWithChildren} from 'react';
|
const queryClient = new QueryClient();
|
||||||
import {
|
global.TextEncoder = TextEncoder;
|
||||||
ScrollView,
|
|
||||||
StatusBar,
|
|
||||||
StyleSheet,
|
|
||||||
Text,
|
|
||||||
useColorScheme,
|
|
||||||
View,
|
|
||||||
} from 'react-native';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Colors,
|
|
||||||
DebugInstructions,
|
|
||||||
Header,
|
|
||||||
LearnMoreLinks,
|
|
||||||
ReloadInstructions,
|
|
||||||
} from 'react-native/Libraries/NewAppScreen';
|
|
||||||
|
|
||||||
type SectionProps = PropsWithChildren<{
|
|
||||||
title: string;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
function Section({children, title}: SectionProps): React.JSX.Element {
|
|
||||||
const isDarkMode = useColorScheme() === 'dark';
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.sectionContainer}>
|
<NavigationContainer >
|
||||||
<Text
|
<QueryClientProvider client={queryClient}>
|
||||||
style={[
|
<GestureHandlerRootView style={{ flex: 1 }}>
|
||||||
styles.sectionTitle,
|
<AlertNotificationRoot>
|
||||||
{
|
<Provider store={store}>
|
||||||
color: isDarkMode ? Colors.white : Colors.black,
|
<PersistGate persistor={persistor}>
|
||||||
},
|
{/* paddingTop: inset.top + 10, */}
|
||||||
]}>
|
<View style={{ flex: 1, backgroundColor: colors.white }}>
|
||||||
{title}
|
<StatusBar
|
||||||
</Text>
|
barStyle="dark-content"
|
||||||
<Text
|
/>
|
||||||
style={[
|
<Routes />
|
||||||
styles.sectionDescription,
|
|
||||||
{
|
|
||||||
color: isDarkMode ? Colors.light : Colors.dark,
|
|
||||||
},
|
|
||||||
]}>
|
|
||||||
{children}
|
|
||||||
</Text>
|
|
||||||
</View>
|
</View>
|
||||||
|
</PersistGate>
|
||||||
|
</Provider>
|
||||||
|
</AlertNotificationRoot>
|
||||||
|
</GestureHandlerRootView>
|
||||||
|
</QueryClientProvider>
|
||||||
|
</NavigationContainer>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
function App(): React.JSX.Element {
|
|
||||||
const isDarkMode = useColorScheme() === 'dark';
|
|
||||||
|
|
||||||
const backgroundStyle = {
|
|
||||||
backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
* To keep the template simple and small we're adding padding to prevent view
|
|
||||||
* from rendering under the System UI.
|
|
||||||
* For bigger apps the recommendation is to use `react-native-safe-area-context`:
|
|
||||||
* https://github.com/AppAndFlow/react-native-safe-area-context
|
|
||||||
*
|
|
||||||
* You can read more about it here:
|
|
||||||
* https://github.com/react-native-community/discussions-and-proposals/discussions/827
|
|
||||||
*/
|
|
||||||
const safePadding = '5%';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View style={backgroundStyle}>
|
|
||||||
<StatusBar
|
|
||||||
barStyle={isDarkMode ? 'light-content' : 'dark-content'}
|
|
||||||
backgroundColor={backgroundStyle.backgroundColor}
|
|
||||||
/>
|
|
||||||
<ScrollView
|
|
||||||
style={backgroundStyle}>
|
|
||||||
<View style={{paddingRight: safePadding}}>
|
|
||||||
<Header/>
|
|
||||||
</View>
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
backgroundColor: isDarkMode ? Colors.black : Colors.white,
|
|
||||||
paddingHorizontal: safePadding,
|
|
||||||
paddingBottom: safePadding,
|
|
||||||
}}>
|
|
||||||
<Section title="Step One">
|
|
||||||
Edit <Text style={styles.highlight}>App.tsx</Text> to change this
|
|
||||||
screen and then come back to see your edits.
|
|
||||||
</Section>
|
|
||||||
<Section title="See Your Changes">
|
|
||||||
<ReloadInstructions />
|
|
||||||
</Section>
|
|
||||||
<Section title="Debug">
|
|
||||||
<DebugInstructions />
|
|
||||||
</Section>
|
|
||||||
<Section title="Learn More">
|
|
||||||
Read the docs to discover what to do next:
|
|
||||||
</Section>
|
|
||||||
<LearnMoreLinks />
|
|
||||||
</View>
|
|
||||||
</ScrollView>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
sectionContainer: {
|
|
||||||
marginTop: 32,
|
|
||||||
paddingHorizontal: 24,
|
|
||||||
},
|
|
||||||
sectionTitle: {
|
|
||||||
fontSize: 24,
|
|
||||||
fontWeight: '600',
|
|
||||||
},
|
|
||||||
sectionDescription: {
|
|
||||||
marginTop: 8,
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: '400',
|
|
||||||
},
|
|
||||||
highlight: {
|
|
||||||
fontWeight: '700',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
/**
|
|
||||||
* @format
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import ReactTestRenderer from 'react-test-renderer';
|
|
||||||
import App from '../App';
|
|
||||||
|
|
||||||
test('renders correctly', async () => {
|
|
||||||
await ReactTestRenderer.act(() => {
|
|
||||||
ReactTestRenderer.create(<App />);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 7.1 KiB |
|
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 18 KiB |
@@ -0,0 +1,77 @@
|
|||||||
|
{
|
||||||
|
"migIndex": 1,
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-Black.ttf",
|
||||||
|
"sha1": "645e04c53c6b5b35bce654a811ebce16af8aa721"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-BlackItalic.ttf",
|
||||||
|
"sha1": "be4ef2aac7775a4b0179c0ecfce6cea8f7783f63"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-Bold.ttf",
|
||||||
|
"sha1": "875cf0cecd647bcf22e79d633d868c1b1ec98dfa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-BoldItalic.ttf",
|
||||||
|
"sha1": "6de8952ca08d27ee77f57347d2cef4b4e26aa78f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-ExtraBold.ttf",
|
||||||
|
"sha1": "4b5c0750f073abd576413a0898d3b95adaf199c8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-ExtraBoldItalic.ttf",
|
||||||
|
"sha1": "94d3ee3acdbd00e37a715a25f2b5237666b5b8a3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-ExtraLight.ttf",
|
||||||
|
"sha1": "85af6582a7e6155917c605f9d3fed68c02b23b06"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-ExtraLightItalic.ttf",
|
||||||
|
"sha1": "6fa0418f91032680d2d8fe4843009f2c80faa61d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-Italic.ttf",
|
||||||
|
"sha1": "e2bfcd56016c3c915221ef1c5fc1a17b0776eb91"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-Light.ttf",
|
||||||
|
"sha1": "e247a92158e112f8bf7b638c8d95381d66b00dbb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-LightItalic.ttf",
|
||||||
|
"sha1": "f76102b361e705751a83b7b22fe954daf1f27dd4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-Medium.ttf",
|
||||||
|
"sha1": "283f21b44efbdbf276ba802be2d949a36bbc4233"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-MediumItalic.ttf",
|
||||||
|
"sha1": "b592c62fdf88c40cfd0f031bba6423b1d4430472"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-Regular.ttf",
|
||||||
|
"sha1": "fdd3002e7d814ee47c1c1b8487c72c6bbb3a2d00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-SemiBold.ttf",
|
||||||
|
"sha1": "8a4ace9392d06bcb7f8ea2f5169b07e4c383a90d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-SemiBoldItalic.ttf",
|
||||||
|
"sha1": "0c8bed0a5942b2388d36b11c115685d9f01700b0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-Thin.ttf",
|
||||||
|
"sha1": "09ba4dcd5509c8085bf88c665dcc51cbdfced27b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-ThinItalic.ttf",
|
||||||
|
"sha1": "fb8fccca21550e71135f92bbaf8a2e8a12c90465"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
presets: ['module:@react-native/babel-preset'],
|
presets: ['module:@react-native/babel-preset'],
|
||||||
|
plugins: [
|
||||||
|
'react-native-reanimated/plugin',
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,5 +5,12 @@
|
|||||||
import { AppRegistry } from 'react-native';
|
import { AppRegistry } from 'react-native';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
import { name as appName } from './app.json';
|
import { name as appName } from './app.json';
|
||||||
|
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
||||||
|
|
||||||
AppRegistry.registerComponent(appName, () => App);
|
const Root = () => (
|
||||||
|
<SafeAreaProvider>
|
||||||
|
<App />
|
||||||
|
</SafeAreaProvider>
|
||||||
|
);
|
||||||
|
|
||||||
|
AppRegistry.registerComponent(appName, () => Root);
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
{
|
||||||
|
"migIndex": 1,
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-Black.ttf",
|
||||||
|
"sha1": "645e04c53c6b5b35bce654a811ebce16af8aa721"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-BlackItalic.ttf",
|
||||||
|
"sha1": "be4ef2aac7775a4b0179c0ecfce6cea8f7783f63"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-Bold.ttf",
|
||||||
|
"sha1": "875cf0cecd647bcf22e79d633d868c1b1ec98dfa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-BoldItalic.ttf",
|
||||||
|
"sha1": "6de8952ca08d27ee77f57347d2cef4b4e26aa78f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-ExtraBold.ttf",
|
||||||
|
"sha1": "4b5c0750f073abd576413a0898d3b95adaf199c8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-ExtraBoldItalic.ttf",
|
||||||
|
"sha1": "94d3ee3acdbd00e37a715a25f2b5237666b5b8a3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-ExtraLight.ttf",
|
||||||
|
"sha1": "85af6582a7e6155917c605f9d3fed68c02b23b06"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-ExtraLightItalic.ttf",
|
||||||
|
"sha1": "6fa0418f91032680d2d8fe4843009f2c80faa61d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-Italic.ttf",
|
||||||
|
"sha1": "e2bfcd56016c3c915221ef1c5fc1a17b0776eb91"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-Light.ttf",
|
||||||
|
"sha1": "e247a92158e112f8bf7b638c8d95381d66b00dbb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-LightItalic.ttf",
|
||||||
|
"sha1": "f76102b361e705751a83b7b22fe954daf1f27dd4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-Medium.ttf",
|
||||||
|
"sha1": "283f21b44efbdbf276ba802be2d949a36bbc4233"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-MediumItalic.ttf",
|
||||||
|
"sha1": "b592c62fdf88c40cfd0f031bba6423b1d4430472"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-Regular.ttf",
|
||||||
|
"sha1": "fdd3002e7d814ee47c1c1b8487c72c6bbb3a2d00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-SemiBold.ttf",
|
||||||
|
"sha1": "8a4ace9392d06bcb7f8ea2f5169b07e4c383a90d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-SemiBoldItalic.ttf",
|
||||||
|
"sha1": "0c8bed0a5942b2388d36b11c115685d9f01700b0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-Thin.ttf",
|
||||||
|
"sha1": "09ba4dcd5509c8085bf88c665dcc51cbdfced27b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "src/assets/fonts/Poppins-ThinItalic.ttf",
|
||||||
|
"sha1": "fb8fccca21550e71135f92bbaf8a2e8a12c90465"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -11,6 +11,24 @@
|
|||||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||||
761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; };
|
761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; };
|
||||||
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
|
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
|
||||||
|
964D9EFFF949426497936119 /* Poppins-Black.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 59DF4DC2A1074281AFD93BD2 /* Poppins-Black.ttf */; };
|
||||||
|
ACECB8A895044D30ACF7416B /* Poppins-BlackItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 01553EE1B40A4956AF0C191D /* Poppins-BlackItalic.ttf */; };
|
||||||
|
B58DDD0C7DD24557A75EF7BB /* Poppins-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 00CB15A27360479683161101 /* Poppins-Bold.ttf */; };
|
||||||
|
D82FFC334B73418290BC099A /* Poppins-BoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D0D680E8F25849D8B9AD362C /* Poppins-BoldItalic.ttf */; };
|
||||||
|
4CFDC44A435143C7962A611F /* Poppins-ExtraBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 37E0779F5E2A40DAABA77561 /* Poppins-ExtraBold.ttf */; };
|
||||||
|
70EB5A61A8644D7AB1E4994C /* Poppins-ExtraBoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 2A8C792275CD46E980094FEA /* Poppins-ExtraBoldItalic.ttf */; };
|
||||||
|
B2974C51D296442C84A51C20 /* Poppins-ExtraLight.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 950E673CE0464B909DD33084 /* Poppins-ExtraLight.ttf */; };
|
||||||
|
A99A934BDC9344F48BBDFD7A /* Poppins-ExtraLightItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 4914B2BF37894F3FAA86CF19 /* Poppins-ExtraLightItalic.ttf */; };
|
||||||
|
49C7DA208E1244C185CB1A46 /* Poppins-Italic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = ADD8486B8FF34AE592C9D1D8 /* Poppins-Italic.ttf */; };
|
||||||
|
3AB4ECE5D1664326B60B04B2 /* Poppins-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 5906046BA159488BB8FCE957 /* Poppins-Light.ttf */; };
|
||||||
|
BDF80A80EB314B4F95E0C91D /* Poppins-LightItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 0A0169F767B1406687B44958 /* Poppins-LightItalic.ttf */; };
|
||||||
|
E09D37FF4FC34B4F8763BB96 /* Poppins-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 5879DC0078CA4C07B9D5E013 /* Poppins-Medium.ttf */; };
|
||||||
|
A4D014A273AA4777A5AE1A64 /* Poppins-MediumItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 0C1CE79110884CEE93B05AB1 /* Poppins-MediumItalic.ttf */; };
|
||||||
|
60967907C5F447D59DB8E9BD /* Poppins-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 22FB847CC0FC46FAB594866D /* Poppins-Regular.ttf */; };
|
||||||
|
71CBF8F3C1724A92B13191BC /* Poppins-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A8E614E6394542FAA0C515C8 /* Poppins-SemiBold.ttf */; };
|
||||||
|
238F4A8F2D37484091F4ABAB /* Poppins-SemiBoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 0DA14ED038B64A6197941A31 /* Poppins-SemiBoldItalic.ttf */; };
|
||||||
|
0BEB68B228444EBCA6011D60 /* Poppins-Thin.ttf in Resources */ = {isa = PBXBuildFile; fileRef = FE7AE6E1A9F9419BA8865118 /* Poppins-Thin.ttf */; };
|
||||||
|
69D218CA23C2476FB5D0020C /* Poppins-ThinItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 8DBE315213A140DF8E345AF7 /* Poppins-ThinItalic.ttf */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@@ -35,6 +53,24 @@
|
|||||||
761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = procure/AppDelegate.swift; sourceTree = "<group>"; };
|
761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = procure/AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = procure/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = procure/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
|
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
|
||||||
|
59DF4DC2A1074281AFD93BD2 /* Poppins-Black.ttf */ = {isa = PBXFileReference; name = "Poppins-Black.ttf"; path = "../src/assets/fonts/Poppins-Black.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
01553EE1B40A4956AF0C191D /* Poppins-BlackItalic.ttf */ = {isa = PBXFileReference; name = "Poppins-BlackItalic.ttf"; path = "../src/assets/fonts/Poppins-BlackItalic.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
00CB15A27360479683161101 /* Poppins-Bold.ttf */ = {isa = PBXFileReference; name = "Poppins-Bold.ttf"; path = "../src/assets/fonts/Poppins-Bold.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
D0D680E8F25849D8B9AD362C /* Poppins-BoldItalic.ttf */ = {isa = PBXFileReference; name = "Poppins-BoldItalic.ttf"; path = "../src/assets/fonts/Poppins-BoldItalic.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
37E0779F5E2A40DAABA77561 /* Poppins-ExtraBold.ttf */ = {isa = PBXFileReference; name = "Poppins-ExtraBold.ttf"; path = "../src/assets/fonts/Poppins-ExtraBold.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
2A8C792275CD46E980094FEA /* Poppins-ExtraBoldItalic.ttf */ = {isa = PBXFileReference; name = "Poppins-ExtraBoldItalic.ttf"; path = "../src/assets/fonts/Poppins-ExtraBoldItalic.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
950E673CE0464B909DD33084 /* Poppins-ExtraLight.ttf */ = {isa = PBXFileReference; name = "Poppins-ExtraLight.ttf"; path = "../src/assets/fonts/Poppins-ExtraLight.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
4914B2BF37894F3FAA86CF19 /* Poppins-ExtraLightItalic.ttf */ = {isa = PBXFileReference; name = "Poppins-ExtraLightItalic.ttf"; path = "../src/assets/fonts/Poppins-ExtraLightItalic.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
ADD8486B8FF34AE592C9D1D8 /* Poppins-Italic.ttf */ = {isa = PBXFileReference; name = "Poppins-Italic.ttf"; path = "../src/assets/fonts/Poppins-Italic.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
5906046BA159488BB8FCE957 /* Poppins-Light.ttf */ = {isa = PBXFileReference; name = "Poppins-Light.ttf"; path = "../src/assets/fonts/Poppins-Light.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
0A0169F767B1406687B44958 /* Poppins-LightItalic.ttf */ = {isa = PBXFileReference; name = "Poppins-LightItalic.ttf"; path = "../src/assets/fonts/Poppins-LightItalic.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
5879DC0078CA4C07B9D5E013 /* Poppins-Medium.ttf */ = {isa = PBXFileReference; name = "Poppins-Medium.ttf"; path = "../src/assets/fonts/Poppins-Medium.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
0C1CE79110884CEE93B05AB1 /* Poppins-MediumItalic.ttf */ = {isa = PBXFileReference; name = "Poppins-MediumItalic.ttf"; path = "../src/assets/fonts/Poppins-MediumItalic.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
22FB847CC0FC46FAB594866D /* Poppins-Regular.ttf */ = {isa = PBXFileReference; name = "Poppins-Regular.ttf"; path = "../src/assets/fonts/Poppins-Regular.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
A8E614E6394542FAA0C515C8 /* Poppins-SemiBold.ttf */ = {isa = PBXFileReference; name = "Poppins-SemiBold.ttf"; path = "../src/assets/fonts/Poppins-SemiBold.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
0DA14ED038B64A6197941A31 /* Poppins-SemiBoldItalic.ttf */ = {isa = PBXFileReference; name = "Poppins-SemiBoldItalic.ttf"; path = "../src/assets/fonts/Poppins-SemiBoldItalic.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
FE7AE6E1A9F9419BA8865118 /* Poppins-Thin.ttf */ = {isa = PBXFileReference; name = "Poppins-Thin.ttf"; path = "../src/assets/fonts/Poppins-Thin.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
8DBE315213A140DF8E345AF7 /* Poppins-ThinItalic.ttf */ = {isa = PBXFileReference; name = "Poppins-ThinItalic.ttf"; path = "../src/assets/fonts/Poppins-ThinItalic.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -93,6 +129,7 @@
|
|||||||
83CBBA001A601CBA00E9B192 /* Products */,
|
83CBBA001A601CBA00E9B192 /* Products */,
|
||||||
2D16E6871FA4F8E400B85C8A /* Frameworks */,
|
2D16E6871FA4F8E400B85C8A /* Frameworks */,
|
||||||
BBD78D7AC51CEA395F1C20DB /* Pods */,
|
BBD78D7AC51CEA395F1C20DB /* Pods */,
|
||||||
|
95F279C442D9485A8F81B199 /* Resources */,
|
||||||
);
|
);
|
||||||
indentWidth = 2;
|
indentWidth = 2;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -116,6 +153,32 @@
|
|||||||
path = Pods;
|
path = Pods;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
95F279C442D9485A8F81B199 /* Resources */ = {
|
||||||
|
isa = "PBXGroup";
|
||||||
|
children = (
|
||||||
|
59DF4DC2A1074281AFD93BD2 /* Poppins-Black.ttf */,
|
||||||
|
01553EE1B40A4956AF0C191D /* Poppins-BlackItalic.ttf */,
|
||||||
|
00CB15A27360479683161101 /* Poppins-Bold.ttf */,
|
||||||
|
D0D680E8F25849D8B9AD362C /* Poppins-BoldItalic.ttf */,
|
||||||
|
37E0779F5E2A40DAABA77561 /* Poppins-ExtraBold.ttf */,
|
||||||
|
2A8C792275CD46E980094FEA /* Poppins-ExtraBoldItalic.ttf */,
|
||||||
|
950E673CE0464B909DD33084 /* Poppins-ExtraLight.ttf */,
|
||||||
|
4914B2BF37894F3FAA86CF19 /* Poppins-ExtraLightItalic.ttf */,
|
||||||
|
ADD8486B8FF34AE592C9D1D8 /* Poppins-Italic.ttf */,
|
||||||
|
5906046BA159488BB8FCE957 /* Poppins-Light.ttf */,
|
||||||
|
0A0169F767B1406687B44958 /* Poppins-LightItalic.ttf */,
|
||||||
|
5879DC0078CA4C07B9D5E013 /* Poppins-Medium.ttf */,
|
||||||
|
0C1CE79110884CEE93B05AB1 /* Poppins-MediumItalic.ttf */,
|
||||||
|
22FB847CC0FC46FAB594866D /* Poppins-Regular.ttf */,
|
||||||
|
A8E614E6394542FAA0C515C8 /* Poppins-SemiBold.ttf */,
|
||||||
|
0DA14ED038B64A6197941A31 /* Poppins-SemiBoldItalic.ttf */,
|
||||||
|
FE7AE6E1A9F9419BA8865118 /* Poppins-Thin.ttf */,
|
||||||
|
8DBE315213A140DF8E345AF7 /* Poppins-ThinItalic.ttf */,
|
||||||
|
);
|
||||||
|
name = Resources;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
path = "";
|
||||||
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
@@ -185,6 +248,24 @@
|
|||||||
files = (
|
files = (
|
||||||
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
|
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
|
||||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
|
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
|
||||||
|
964D9EFFF949426497936119 /* Poppins-Black.ttf in Resources */,
|
||||||
|
ACECB8A895044D30ACF7416B /* Poppins-BlackItalic.ttf in Resources */,
|
||||||
|
B58DDD0C7DD24557A75EF7BB /* Poppins-Bold.ttf in Resources */,
|
||||||
|
D82FFC334B73418290BC099A /* Poppins-BoldItalic.ttf in Resources */,
|
||||||
|
4CFDC44A435143C7962A611F /* Poppins-ExtraBold.ttf in Resources */,
|
||||||
|
70EB5A61A8644D7AB1E4994C /* Poppins-ExtraBoldItalic.ttf in Resources */,
|
||||||
|
B2974C51D296442C84A51C20 /* Poppins-ExtraLight.ttf in Resources */,
|
||||||
|
A99A934BDC9344F48BBDFD7A /* Poppins-ExtraLightItalic.ttf in Resources */,
|
||||||
|
49C7DA208E1244C185CB1A46 /* Poppins-Italic.ttf in Resources */,
|
||||||
|
3AB4ECE5D1664326B60B04B2 /* Poppins-Light.ttf in Resources */,
|
||||||
|
BDF80A80EB314B4F95E0C91D /* Poppins-LightItalic.ttf in Resources */,
|
||||||
|
E09D37FF4FC34B4F8763BB96 /* Poppins-Medium.ttf in Resources */,
|
||||||
|
A4D014A273AA4777A5AE1A64 /* Poppins-MediumItalic.ttf in Resources */,
|
||||||
|
60967907C5F447D59DB8E9BD /* Poppins-Regular.ttf in Resources */,
|
||||||
|
71CBF8F3C1724A92B13191BC /* Poppins-SemiBold.ttf in Resources */,
|
||||||
|
238F4A8F2D37484091F4ABAB /* Poppins-SemiBoldItalic.ttf in Resources */,
|
||||||
|
0BEB68B228444EBCA6011D60 /* Poppins-Thin.ttf in Resources */,
|
||||||
|
69D218CA23C2476FB5D0020C /* Poppins-ThinItalic.ttf in Resources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -26,14 +26,13 @@
|
|||||||
<true/>
|
<true/>
|
||||||
<key>NSAppTransportSecurity</key>
|
<key>NSAppTransportSecurity</key>
|
||||||
<dict>
|
<dict>
|
||||||
<!-- Do not change NSAllowsArbitraryLoads to true, or you will risk app rejection! -->
|
|
||||||
<key>NSAllowsArbitraryLoads</key>
|
<key>NSAllowsArbitraryLoads</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>NSAllowsLocalNetworking</key>
|
<key>NSAllowsLocalNetworking</key>
|
||||||
<true/>
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
<key>NSLocationWhenInUseUsageDescription</key>
|
<key>NSLocationWhenInUseUsageDescription</key>
|
||||||
<string></string>
|
<string/>
|
||||||
<key>UILaunchStoryboardName</key>
|
<key>UILaunchStoryboardName</key>
|
||||||
<string>LaunchScreen</string>
|
<string>LaunchScreen</string>
|
||||||
<key>UIRequiredDeviceCapabilities</key>
|
<key>UIRequiredDeviceCapabilities</key>
|
||||||
@@ -48,5 +47,26 @@
|
|||||||
</array>
|
</array>
|
||||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||||
<false/>
|
<false/>
|
||||||
|
<key>UIAppFonts</key>
|
||||||
|
<array>
|
||||||
|
<string>Poppins-Black.ttf</string>
|
||||||
|
<string>Poppins-BlackItalic.ttf</string>
|
||||||
|
<string>Poppins-Bold.ttf</string>
|
||||||
|
<string>Poppins-BoldItalic.ttf</string>
|
||||||
|
<string>Poppins-ExtraBold.ttf</string>
|
||||||
|
<string>Poppins-ExtraBoldItalic.ttf</string>
|
||||||
|
<string>Poppins-ExtraLight.ttf</string>
|
||||||
|
<string>Poppins-ExtraLightItalic.ttf</string>
|
||||||
|
<string>Poppins-Italic.ttf</string>
|
||||||
|
<string>Poppins-Light.ttf</string>
|
||||||
|
<string>Poppins-LightItalic.ttf</string>
|
||||||
|
<string>Poppins-Medium.ttf</string>
|
||||||
|
<string>Poppins-MediumItalic.ttf</string>
|
||||||
|
<string>Poppins-Regular.ttf</string>
|
||||||
|
<string>Poppins-SemiBold.ttf</string>
|
||||||
|
<string>Poppins-SemiBoldItalic.ttf</string>
|
||||||
|
<string>Poppins-Thin.ttf</string>
|
||||||
|
<string>Poppins-ThinItalic.ttf</string>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -1,11 +1,24 @@
|
|||||||
const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
|
const { getDefaultConfig, mergeConfig } = require("@react-native/metro-config");
|
||||||
|
|
||||||
|
const defaultConfig = getDefaultConfig(__dirname);
|
||||||
|
const { assetExts, sourceExts } = defaultConfig.resolver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Metro configuration
|
* Metro configuration
|
||||||
* https://reactnative.dev/docs/metro
|
* https://reactnative.dev/docs/metro
|
||||||
*
|
*
|
||||||
* @type {import('@react-native/metro-config').MetroConfig}
|
* @type {import('metro-config').MetroConfig}
|
||||||
*/
|
*/
|
||||||
const config = {};
|
const config = {
|
||||||
|
transformer: {
|
||||||
|
babelTransformerPath: require.resolve(
|
||||||
|
"react-native-svg-transformer/react-native"
|
||||||
|
)
|
||||||
|
},
|
||||||
|
resolver: {
|
||||||
|
assetExts: assetExts.filter((ext) => ext !== "svg"),
|
||||||
|
sourceExts: [...sourceExts, "svg"]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = mergeConfig(getDefaultConfig(__dirname), config);
|
module.exports = mergeConfig(defaultConfig, config);
|
||||||
@@ -10,8 +10,48 @@
|
|||||||
"test": "jest"
|
"test": "jest"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@react-native-async-storage/async-storage": "^2.2.0",
|
||||||
|
"@react-native-community/blur": "^4.4.1",
|
||||||
|
"@react-native-documents/picker": "^11.0.3",
|
||||||
|
"@react-native-masked-view/masked-view": "^0.3.2",
|
||||||
|
"@react-navigation/bottom-tabs": "^7.9.0",
|
||||||
|
"@react-navigation/native": "^7.1.26",
|
||||||
|
"@react-navigation/stack": "^7.6.13",
|
||||||
|
"@reduxjs/toolkit": "^2.11.2",
|
||||||
|
"@stomp/stompjs": "^7.2.1",
|
||||||
|
"@tanstack/react-query": "^5.90.12",
|
||||||
|
"axios": "^1.13.2",
|
||||||
|
"eslint-config-prettier": "^10.1.8",
|
||||||
|
"eslint-plugin-react": "^7.37.5",
|
||||||
|
"eslint-plugin-react-native": "^5.0.0",
|
||||||
|
"formik": "^2.4.9",
|
||||||
|
"lottie-react-native": "^7.3.4",
|
||||||
|
"lucide-react-native": "^0.562.0",
|
||||||
|
"moment": "^2.30.1",
|
||||||
"react": "19.0.0",
|
"react": "19.0.0",
|
||||||
"react-native": "0.79.0"
|
"react-native": "0.79.0",
|
||||||
|
"react-native-alert-notification": "^0.4.2",
|
||||||
|
"react-native-app-intro-slider": "^4.0.4",
|
||||||
|
"react-native-bouncy-checkbox": "^4.1.2",
|
||||||
|
"react-native-date-picker": "^5.0.13",
|
||||||
|
"react-native-gesture-handler": "^2.30.0",
|
||||||
|
"react-native-gifted-charts": "^1.4.70",
|
||||||
|
"react-native-haptic-feedback": "^2.3.3",
|
||||||
|
"react-native-linear-gradient": "^2.8.3",
|
||||||
|
"react-native-modal": "^14.0.0-rc.1",
|
||||||
|
"react-native-permissions": "^5.4.4",
|
||||||
|
"react-native-reanimated": "^3.18.0",
|
||||||
|
"react-native-responsive-screen": "^1.4.2",
|
||||||
|
"react-native-safe-area-context": "^5.6.2",
|
||||||
|
"react-native-screens": "^4.12.0",
|
||||||
|
"react-native-skeleton-placeholder": "^5.2.4",
|
||||||
|
"react-native-svg": "^15.12.0",
|
||||||
|
"react-native-svg-transformer": "^1.5.1",
|
||||||
|
"react-redux": "^9.2.0",
|
||||||
|
"redux-persist": "^6.0.0",
|
||||||
|
"sockjs-client": "^1.6.1",
|
||||||
|
"text-encoding": "^0.7.0",
|
||||||
|
"yup": "^1.7.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.25.2",
|
"@babel/core": "^7.25.2",
|
||||||
@@ -27,6 +67,8 @@
|
|||||||
"@types/jest": "^29.5.13",
|
"@types/jest": "^29.5.13",
|
||||||
"@types/react": "^19.0.0",
|
"@types/react": "^19.0.0",
|
||||||
"@types/react-test-renderer": "^19.0.0",
|
"@types/react-test-renderer": "^19.0.0",
|
||||||
|
"@types/sockjs-client": "^1.5.4",
|
||||||
|
"@types/text-encoding": "^0.0.40",
|
||||||
"eslint": "^8.19.0",
|
"eslint": "^8.19.0",
|
||||||
"jest": "^29.6.3",
|
"jest": "^29.6.3",
|
||||||
"prettier": "2.8.8",
|
"prettier": "2.8.8",
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
module.exports={
|
||||||
|
project:{
|
||||||
|
ios:{},
|
||||||
|
android:{},
|
||||||
|
},
|
||||||
|
assets:["./src/assets/fonts"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
import { NavigationString, showErrorAlert, Variable } from '../Constant/AllImports';
|
||||||
|
import { setToken } from '../Redux/Slices/Token';
|
||||||
|
import { store } from '../Redux/Store';
|
||||||
|
|
||||||
|
export const apiCall = async <R, D = {}>(
|
||||||
|
method: 'get' | 'post' | 'patch' | 'delete' | 'put',
|
||||||
|
url: string,
|
||||||
|
data?: D,
|
||||||
|
params?: any,
|
||||||
|
contentType?: string
|
||||||
|
): Promise<R> => {
|
||||||
|
try {
|
||||||
|
const token: any = store.getState().token.token;
|
||||||
|
|
||||||
|
const response = await axios({
|
||||||
|
method,
|
||||||
|
url,
|
||||||
|
data,
|
||||||
|
params,
|
||||||
|
// withCredentials: true,
|
||||||
|
baseURL: Variable.Main_Base,
|
||||||
|
headers: {
|
||||||
|
Authentication: `Bearer ${token?.token}`,
|
||||||
|
'Content-Type': contentType || 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
} catch (error: any) {
|
||||||
|
const errorMessage = error?.response?.data?.error;
|
||||||
|
{ error?.response?.data?.error && showErrorAlert(error?.response?.data?.error || '') }
|
||||||
|
console.log(errorMessage, '---------------url error');
|
||||||
|
|
||||||
|
if (error?.response?.status === 401) {
|
||||||
|
store.dispatch(setToken({}));
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: 'JWT Error - Token expired or malformed',
|
||||||
|
} as R;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: error?.response?.data?.message || 'No Response From Server',
|
||||||
|
} as R;
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
import React, { FC, useRef, useEffect } from 'react';
|
||||||
|
import {
|
||||||
|
StyleSheet,
|
||||||
|
Text,
|
||||||
|
View,
|
||||||
|
TouchableOpacity,
|
||||||
|
Animated,
|
||||||
|
Platform,
|
||||||
|
} from 'react-native';
|
||||||
|
import LinearGradient from 'react-native-linear-gradient';
|
||||||
|
import { BlurView } from '@react-native-community/blur';
|
||||||
|
import colors from '../../Constant/Color';
|
||||||
|
|
||||||
|
interface CardProps {
|
||||||
|
title: string;
|
||||||
|
leftLabel: string;
|
||||||
|
leftValue: React.ReactNode;
|
||||||
|
rightLabel: string;
|
||||||
|
rightValue: React.ReactNode;
|
||||||
|
buttonText: string;
|
||||||
|
onPress: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Card: FC<CardProps> = ({
|
||||||
|
title,
|
||||||
|
leftLabel,
|
||||||
|
leftValue,
|
||||||
|
rightLabel,
|
||||||
|
rightValue,
|
||||||
|
buttonText,
|
||||||
|
onPress,
|
||||||
|
}) => {
|
||||||
|
const scale = useRef(new Animated.Value(0.95)).current;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
Animated.spring(scale, {
|
||||||
|
toValue: 1,
|
||||||
|
friction: 8,
|
||||||
|
tension: 90,
|
||||||
|
useNativeDriver: true,
|
||||||
|
}).start();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Animated.View style={[styles.outer, { transform: [{ scale }] }]}>
|
||||||
|
{/* Glass Base */}
|
||||||
|
{Platform.OS === 'ios' && (
|
||||||
|
<BlurView
|
||||||
|
style={StyleSheet.absoluteFill}
|
||||||
|
blurType="light"
|
||||||
|
blurAmount={10}
|
||||||
|
reducedTransparencyFallbackColor="#ffffff"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Card Body */}
|
||||||
|
<LinearGradient
|
||||||
|
colors={[
|
||||||
|
'rgba(255,255,255,0.85)',
|
||||||
|
'rgba(255,255,255,0.65)',
|
||||||
|
]}
|
||||||
|
style={styles.card}
|
||||||
|
>
|
||||||
|
{/* Title */}
|
||||||
|
<Text style={styles.title}>{title}</Text>
|
||||||
|
|
||||||
|
{/* Stats */}
|
||||||
|
<View style={styles.statsRow}>
|
||||||
|
<View style={styles.statBox}>
|
||||||
|
<Text style={styles.statValue}>{leftValue}</Text>
|
||||||
|
<Text style={styles.statLabel}>{leftLabel}</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.verticalDivider} />
|
||||||
|
|
||||||
|
<View style={styles.statBox}>
|
||||||
|
<Text style={styles.statValue}>{rightValue}</Text>
|
||||||
|
<Text style={styles.statLabel}>{rightLabel}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* CTA */}
|
||||||
|
<TouchableOpacity activeOpacity={0.85} onPress={onPress}>
|
||||||
|
<LinearGradient
|
||||||
|
colors={[colors.primary, colors.primaryLight]}
|
||||||
|
start={{ x: 0, y: 0 }}
|
||||||
|
end={{ x: 1, y: 1 }}
|
||||||
|
style={styles.ctaButton}
|
||||||
|
>
|
||||||
|
<Text style={styles.ctaText}>{buttonText}</Text>
|
||||||
|
</LinearGradient>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</LinearGradient>
|
||||||
|
</Animated.View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Card;
|
||||||
|
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
outer: {
|
||||||
|
marginBottom: 20,
|
||||||
|
borderRadius: 20,
|
||||||
|
overflow: 'hidden',
|
||||||
|
shadowColor: '#000',
|
||||||
|
shadowOffset: { width: 0, height: 10 },
|
||||||
|
shadowOpacity: 0.12,
|
||||||
|
shadowRadius: 18,
|
||||||
|
elevation: 8,
|
||||||
|
},
|
||||||
|
|
||||||
|
card: {
|
||||||
|
padding: 22,
|
||||||
|
borderRadius: 20,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: 'rgba(255,255,255,0.6)',
|
||||||
|
},
|
||||||
|
|
||||||
|
title: {
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: '800',
|
||||||
|
color: '#0f172a',
|
||||||
|
marginBottom: 16,
|
||||||
|
},
|
||||||
|
|
||||||
|
statsRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginBottom: 18,
|
||||||
|
},
|
||||||
|
|
||||||
|
statBox: {
|
||||||
|
flex: 1,
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
|
||||||
|
statValue: {
|
||||||
|
fontSize: 26,
|
||||||
|
fontWeight: '900',
|
||||||
|
color: '#020617',
|
||||||
|
},
|
||||||
|
|
||||||
|
statLabel: {
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: '600',
|
||||||
|
color: colors.secondary,
|
||||||
|
marginTop: 4,
|
||||||
|
},
|
||||||
|
|
||||||
|
verticalDivider: {
|
||||||
|
width: 1,
|
||||||
|
height: 42,
|
||||||
|
backgroundColor: 'rgba(15,23,42,0.15)',
|
||||||
|
},
|
||||||
|
|
||||||
|
ctaButton: {
|
||||||
|
height: 46,
|
||||||
|
borderRadius: 14,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
|
||||||
|
ctaText: {
|
||||||
|
color: '#fff',
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: '800',
|
||||||
|
letterSpacing: 0.6,
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import React, { useEffect, useState, useCallback } from 'react';
|
||||||
|
import { View, Text, StyleSheet } from 'react-native';
|
||||||
|
import colors from '../../Constant/Color';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
|
interface CountdownTimerProps {
|
||||||
|
endDate: string; // Expected format: "DD-MM-YYYY HH:mm:ss"
|
||||||
|
}
|
||||||
|
|
||||||
|
const CountdownTimer: React.FC<CountdownTimerProps> = React.memo(({ endDate }) => {
|
||||||
|
const calculateTimeLeft = useCallback(() => {
|
||||||
|
if (!endDate) return null;
|
||||||
|
|
||||||
|
const end = moment(endDate, 'DD-MM-YYYY HH:mm:ss');
|
||||||
|
if (!end.isValid()) return null;
|
||||||
|
|
||||||
|
const now = moment();
|
||||||
|
const diff = end.diff(now);
|
||||||
|
if (diff <= 0) return null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
days: Math.floor(diff / (1000 * 60 * 60 * 24)),
|
||||||
|
hours: Math.floor((diff / (1000 * 60 * 60)) % 24),
|
||||||
|
minutes: Math.floor((diff / (1000 * 60)) % 60),
|
||||||
|
seconds: Math.floor((diff / 1000) % 60),
|
||||||
|
};
|
||||||
|
}, [endDate]);
|
||||||
|
|
||||||
|
const [timeLeft, setTimeLeft] = useState(calculateTimeLeft);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const timerId = setInterval(() => setTimeLeft(calculateTimeLeft()), 1000);
|
||||||
|
return () => clearInterval(timerId);
|
||||||
|
}, [calculateTimeLeft]);
|
||||||
|
|
||||||
|
if (!timeLeft) {
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Text style={styles.expiredText}>Time's up!</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Text style={styles.timerText}>
|
||||||
|
{timeLeft.days}d {timeLeft.hours}h {timeLeft.minutes}m {timeLeft.seconds}s
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default CountdownTimer;
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
backgroundColor: colors.white,
|
||||||
|
paddingVertical: 5,
|
||||||
|
paddingHorizontal: 10,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: colors.primary,
|
||||||
|
borderRadius: 25,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
timerText: {
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: colors.primary,
|
||||||
|
},
|
||||||
|
expiredText: {
|
||||||
|
fontSize: 12,
|
||||||
|
color: 'red',
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
|
import { View, Text, Animated, StyleSheet } from 'react-native';
|
||||||
|
import colors from '../../Constant/Color';
|
||||||
|
|
||||||
|
const Counter = ({ toValue = 1000, duration = 2000 }) => {
|
||||||
|
const animatedValue = useRef(new Animated.Value(0)).current;
|
||||||
|
const [displayValue, setDisplayValue] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
Animated.timing(animatedValue, {
|
||||||
|
toValue,
|
||||||
|
duration,
|
||||||
|
useNativeDriver: false,
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
const listener = animatedValue.addListener(({ value }) => {
|
||||||
|
setDisplayValue(Math.floor(value));
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
animatedValue.removeListener(listener);
|
||||||
|
};
|
||||||
|
}, [toValue]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Text style={styles.counterText}>{displayValue}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Counter
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
backgroundColor: '#f2f2f2',
|
||||||
|
padding: 5,
|
||||||
|
borderRadius: 5,
|
||||||
|
},
|
||||||
|
counterText: {
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: colors.primary,
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import {
|
||||||
|
View,
|
||||||
|
Text,
|
||||||
|
TouchableOpacity,
|
||||||
|
Image,
|
||||||
|
StyleSheet,
|
||||||
|
Dimensions,
|
||||||
|
} from 'react-native';
|
||||||
|
import LinearGradient from 'react-native-linear-gradient';
|
||||||
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
|
import { Menu } from 'lucide-react-native';
|
||||||
|
import colors from '../../Constant/Color';
|
||||||
|
import ImagePath from '../../Constant/ImagePath';
|
||||||
|
import FontsFamily from '../../Constant/FontsFamily';
|
||||||
|
import SideDrawer from '../SideDrawer/SideDrawer';
|
||||||
|
|
||||||
|
const { width } = Dimensions.get('window');
|
||||||
|
|
||||||
|
interface HeaderProps {
|
||||||
|
title: string;
|
||||||
|
showDrawer?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Header: React.FC<HeaderProps> = ({ title, showDrawer = true }) => {
|
||||||
|
const insets = useSafeAreaInsets();
|
||||||
|
const [drawerVisible, setDrawerVisible] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<LinearGradient
|
||||||
|
colors={[colors.white, 'rgba(255,255,255,0.8)']}
|
||||||
|
start={{ x: 0, y: 0 }}
|
||||||
|
end={{ x: 0, y: 1 }}
|
||||||
|
style={[styles.container, { paddingTop: insets.top + 10 }]}
|
||||||
|
>
|
||||||
|
<View style={styles.row}>
|
||||||
|
<View style={styles.left}>
|
||||||
|
{showDrawer && (
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={() => setDrawerVisible(true)}
|
||||||
|
style={styles.drawerIcon}
|
||||||
|
activeOpacity={0.7}
|
||||||
|
>
|
||||||
|
<Menu size={28} color={colors.black} strokeWidth={2.5} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
<Text style={styles.title}>{title}</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<Image
|
||||||
|
source={ImagePath.Icon.Logo2}
|
||||||
|
style={styles.logo}
|
||||||
|
resizeMode="contain"
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</LinearGradient>
|
||||||
|
|
||||||
|
{/* Drawer Modal */}
|
||||||
|
<SideDrawer
|
||||||
|
visible={drawerVisible}
|
||||||
|
onClose={() => setDrawerVisible(false)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Header;
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
width,
|
||||||
|
paddingBottom: 5,
|
||||||
|
},
|
||||||
|
row: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
},
|
||||||
|
left: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
drawerIcon: {
|
||||||
|
marginRight: 12,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 20,
|
||||||
|
color: colors.black,
|
||||||
|
fontFamily: FontsFamily.poppinsSemiBold,
|
||||||
|
},
|
||||||
|
logo: {
|
||||||
|
width: 55,
|
||||||
|
height: 55,
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
View,
|
||||||
|
Text,
|
||||||
|
TouchableOpacity,
|
||||||
|
Image,
|
||||||
|
StyleSheet,
|
||||||
|
Dimensions,
|
||||||
|
} from 'react-native';
|
||||||
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
|
import { ArrowLeft } from 'lucide-react-native';
|
||||||
|
import colors from '../../Constant/Color';
|
||||||
|
import ImagePath from '../../Constant/ImagePath';
|
||||||
|
import { useNavigation } from '@react-navigation/native';
|
||||||
|
import FontsFamily from '../../Constant/FontsFamily';
|
||||||
|
import NavigationString from '../../Constant/NavigationString';
|
||||||
|
|
||||||
|
const { width } = Dimensions.get('window');
|
||||||
|
|
||||||
|
interface HeaderProps {
|
||||||
|
title: string;
|
||||||
|
showArrow?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const InsideHeader: React.FC<HeaderProps> = ({ title, showArrow = false }) => {
|
||||||
|
const navigation:any = useNavigation();
|
||||||
|
const insets = useSafeAreaInsets();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
styles.container,
|
||||||
|
{
|
||||||
|
paddingTop: insets.top - 10,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<View style={styles.row}>
|
||||||
|
<View style={styles.leftRow}>
|
||||||
|
{showArrow && (
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={() => navigation.goBack()}
|
||||||
|
style={styles.backButton}
|
||||||
|
activeOpacity={0.7}
|
||||||
|
>
|
||||||
|
<ArrowLeft size={26} color={colors.black} strokeWidth={2.5} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Text style={styles.title}>{title}</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<TouchableOpacity onPress={() => { navigation.navigate(NavigationString.Emd) }}>
|
||||||
|
<Image
|
||||||
|
source={ImagePath.Icon.Logo2}
|
||||||
|
style={styles.logo}
|
||||||
|
resizeMode="contain"
|
||||||
|
/>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InsideHeader;
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
width,
|
||||||
|
backgroundColor: colors.white, // IMPORTANT for shadow
|
||||||
|
shadowColor: '#000', // iOS shadow
|
||||||
|
shadowOffset: { width: 0, height: 6 },
|
||||||
|
shadowOpacity: 0.12,
|
||||||
|
shadowRadius: 10,
|
||||||
|
elevation: 6,
|
||||||
|
zIndex: 10,
|
||||||
|
},
|
||||||
|
|
||||||
|
row: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
paddingHorizontal: 10,
|
||||||
|
},
|
||||||
|
|
||||||
|
leftRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
|
||||||
|
backButton: {
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
borderRadius: 20,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
marginRight: 8,
|
||||||
|
},
|
||||||
|
|
||||||
|
title: {
|
||||||
|
fontSize: 20,
|
||||||
|
color: colors.black,
|
||||||
|
fontFamily: FontsFamily.poppinsSemiBold,
|
||||||
|
},
|
||||||
|
|
||||||
|
logo: {
|
||||||
|
height: 60,
|
||||||
|
width: 60,
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import Header from "./Header/Header";
|
||||||
|
import InsideHeader from "./Header/InsideHeader";
|
||||||
|
import Loader from "./Loader/Loader";
|
||||||
|
import Skelton from "./Skelton/Skelton";
|
||||||
|
|
||||||
|
export { Header , Skelton , Loader , InsideHeader}
|
||||||
@@ -0,0 +1,188 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { View, Text, StyleSheet, TouchableOpacity, Dimensions } from "react-native";
|
||||||
|
import LottieView from "lottie-react-native";
|
||||||
|
import LinearGradient from "react-native-linear-gradient";
|
||||||
|
|
||||||
|
const { width } = Dimensions.get("window");
|
||||||
|
|
||||||
|
const InternetConnectivity = () => {
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<View style={styles.card}>
|
||||||
|
<View style={styles.lottieContainer}>
|
||||||
|
<LottieView
|
||||||
|
source={require("../../lottie/NoInternet.json")}
|
||||||
|
autoPlay
|
||||||
|
loop
|
||||||
|
style={styles.lottie}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.content}>
|
||||||
|
<Text style={styles.title}>No Internet Connection</Text>
|
||||||
|
|
||||||
|
<Text style={styles.subtitle}>
|
||||||
|
It looks like you're offline. Check your connection and try again to continue.
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<View style={styles.hintContainer}>
|
||||||
|
<View style={styles.hintRow}>
|
||||||
|
<View style={styles.hintDot} />
|
||||||
|
<Text style={styles.hintText}>Check your Wi-Fi or mobile data</Text>
|
||||||
|
</View>
|
||||||
|
<View style={styles.hintRow}>
|
||||||
|
<View style={styles.hintDot} />
|
||||||
|
<Text style={styles.hintText}>Try turning airplane mode on/off</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.buttonContainer}>
|
||||||
|
<TouchableOpacity
|
||||||
|
activeOpacity={0.7}
|
||||||
|
onPress={() => {}}
|
||||||
|
style={styles.touchable}
|
||||||
|
>
|
||||||
|
<LinearGradient
|
||||||
|
colors={["#63adde", "#3089bd50"]}
|
||||||
|
start={{ x: 0, y: 0 }}
|
||||||
|
end={{ x: 1, y: 0 }}
|
||||||
|
style={styles.retryBtn}
|
||||||
|
>
|
||||||
|
<Text style={styles.retryText}>Try Again</Text>
|
||||||
|
</LinearGradient>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InternetConnectivity;
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: "#F8FAFF",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
padding: 20,
|
||||||
|
},
|
||||||
|
card: {
|
||||||
|
width: "100%",
|
||||||
|
maxWidth: 400,
|
||||||
|
backgroundColor: "#FFF",
|
||||||
|
borderRadius: 24,
|
||||||
|
paddingVertical: 32,
|
||||||
|
paddingHorizontal: 24,
|
||||||
|
alignItems: "center",
|
||||||
|
shadowColor: "#667EEA",
|
||||||
|
shadowOpacity: 0.1,
|
||||||
|
shadowOffset: { width: 0, height: 10 },
|
||||||
|
shadowRadius: 20,
|
||||||
|
elevation: 8,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: "rgba(255, 255, 255, 0.8)",
|
||||||
|
},
|
||||||
|
lottieContainer: {
|
||||||
|
width: 180,
|
||||||
|
height: 180,
|
||||||
|
marginBottom: 8,
|
||||||
|
},
|
||||||
|
lottie: {
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
alignItems: "center",
|
||||||
|
width: "100%",
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 26,
|
||||||
|
fontWeight: "800",
|
||||||
|
color: "#1A1A1A",
|
||||||
|
marginTop: 8,
|
||||||
|
letterSpacing: -0.5,
|
||||||
|
textAlign: "center",
|
||||||
|
},
|
||||||
|
subtitle: {
|
||||||
|
fontSize: 16,
|
||||||
|
color: "#666",
|
||||||
|
textAlign: "center",
|
||||||
|
marginTop: 12,
|
||||||
|
marginBottom: 28,
|
||||||
|
lineHeight: 24,
|
||||||
|
paddingHorizontal: 10,
|
||||||
|
fontWeight: "400",
|
||||||
|
},
|
||||||
|
hintContainer: {
|
||||||
|
width: "100%",
|
||||||
|
backgroundColor: "#F8F9FF",
|
||||||
|
borderRadius: 16,
|
||||||
|
padding: 20,
|
||||||
|
marginBottom: 32,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: "#EDF2FE",
|
||||||
|
},
|
||||||
|
hintRow: {
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
marginBottom: 12,
|
||||||
|
},
|
||||||
|
hintDot: {
|
||||||
|
width: 6,
|
||||||
|
height: 6,
|
||||||
|
borderRadius: 3,
|
||||||
|
backgroundColor: "#667EEA",
|
||||||
|
marginRight: 12,
|
||||||
|
},
|
||||||
|
hintText: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: "#555",
|
||||||
|
fontWeight: "400",
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
buttonContainer: {
|
||||||
|
width: "100%",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
touchable: {
|
||||||
|
width: "100%",
|
||||||
|
shadowColor: "#667EEA",
|
||||||
|
shadowOffset: { width: 0, height: 4 },
|
||||||
|
shadowOpacity: 0.3,
|
||||||
|
shadowRadius: 8,
|
||||||
|
elevation: 5,
|
||||||
|
},
|
||||||
|
retryBtn: {
|
||||||
|
paddingVertical: 18,
|
||||||
|
paddingHorizontal: 30,
|
||||||
|
borderRadius: 16,
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
width: "100%",
|
||||||
|
},
|
||||||
|
retryText: {
|
||||||
|
color: "#FFF",
|
||||||
|
fontSize: 17,
|
||||||
|
fontWeight: "700",
|
||||||
|
letterSpacing: 0.5,
|
||||||
|
},
|
||||||
|
secondaryButton: {
|
||||||
|
marginTop: 16,
|
||||||
|
paddingVertical: 14,
|
||||||
|
paddingHorizontal: 30,
|
||||||
|
borderRadius: 16,
|
||||||
|
borderWidth: 1.5,
|
||||||
|
borderColor: "#E2E8F0",
|
||||||
|
backgroundColor: "#FFF",
|
||||||
|
width: "100%",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
secondaryText: {
|
||||||
|
color: "#64748B",
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: "600",
|
||||||
|
letterSpacing: 0.3,
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,406 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
View,
|
||||||
|
Text,
|
||||||
|
StyleSheet,
|
||||||
|
TouchableOpacity,
|
||||||
|
Dimensions,
|
||||||
|
ScrollView,
|
||||||
|
StatusBar,
|
||||||
|
} from 'react-native';
|
||||||
|
import LottieView from 'lottie-react-native';
|
||||||
|
import LinearGradient from 'react-native-linear-gradient';
|
||||||
|
import {
|
||||||
|
AlertCircle,
|
||||||
|
Clock,
|
||||||
|
Server,
|
||||||
|
Calendar,
|
||||||
|
RefreshCw,
|
||||||
|
MessageCircle,
|
||||||
|
Twitter,
|
||||||
|
Activity,
|
||||||
|
MessageSquare,
|
||||||
|
Bell,
|
||||||
|
WifiOff,
|
||||||
|
} from 'lucide-react-native';
|
||||||
|
import Colors from '../../Styles/Color';
|
||||||
|
import FontsFamily from '../../Constant/FontsFamily';
|
||||||
|
|
||||||
|
const {width} = Dimensions.get('window');
|
||||||
|
|
||||||
|
const ServerDown = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<StatusBar
|
||||||
|
// translucent={true}
|
||||||
|
// backgroundColor="transparent"
|
||||||
|
barStyle="dark-content"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
contentContainerStyle={styles.scrollContainer}
|
||||||
|
showsVerticalScrollIndicator={false}>
|
||||||
|
<View style={styles.container}>
|
||||||
|
<View style={styles.card}>
|
||||||
|
{/* Status Indicator */}
|
||||||
|
<View style={styles.statusIndicator}>
|
||||||
|
<View style={styles.statusPulse} />
|
||||||
|
<View style={styles.statusDot}>
|
||||||
|
<AlertCircle size={24} color="#FFF" />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Animated Illustration */}
|
||||||
|
<View style={styles.animationContainer}>
|
||||||
|
<LottieView
|
||||||
|
source={require('../../lottie/MaintenanceServer.json')}
|
||||||
|
autoPlay
|
||||||
|
loop
|
||||||
|
style={styles.animation}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Main Content */}
|
||||||
|
<View style={styles.content}>
|
||||||
|
<Text style={styles.title}>Server Unavailable</Text>
|
||||||
|
|
||||||
|
<View style={styles.statusMessage}>
|
||||||
|
<Clock size={18} color="#FF6B6B" style={styles.statusIcon} />
|
||||||
|
<Text style={styles.statusText}>
|
||||||
|
Temporarily down for maintenance
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<Text style={styles.description}>
|
||||||
|
Our servers are currently undergoing maintenance. This usually
|
||||||
|
takes 15-30 minutes. We appreciate your patience while we work
|
||||||
|
to improve your experience.
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
{/* Action Buttons */}
|
||||||
|
<View style={styles.buttonGroup}>
|
||||||
|
<TouchableOpacity
|
||||||
|
activeOpacity={0.7}
|
||||||
|
onPress={() => {}}
|
||||||
|
style={styles.touchable}>
|
||||||
|
<LinearGradient
|
||||||
|
colors={[Colors.appColourDark, Colors.appColourDarkDim]}
|
||||||
|
start={{x: 0, y: 0}}
|
||||||
|
end={{x: 1, y: 0}}
|
||||||
|
style={styles.primaryButton}>
|
||||||
|
<RefreshCw
|
||||||
|
size={20}
|
||||||
|
color="#FFF"
|
||||||
|
style={styles.buttonIcon}
|
||||||
|
/>
|
||||||
|
<Text style={styles.primaryButtonText}>Refresh Status</Text>
|
||||||
|
</LinearGradient>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Live Status */}
|
||||||
|
<View style={styles.liveStatus}>
|
||||||
|
<View style={styles.liveIndicator}>
|
||||||
|
<View style={styles.livePulse} />
|
||||||
|
<View style={styles.liveDot} />
|
||||||
|
</View>
|
||||||
|
<Activity size={14} color="#10B981" style={styles.liveIcon} />
|
||||||
|
<Text style={styles.liveText}>Monitoring server status...</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Background Pattern */}
|
||||||
|
<View style={styles.backgroundPattern} />
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ServerDown;
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
scrollContainer: {
|
||||||
|
flexGrow: 1,
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: '#F8FAFF',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
padding: 20,
|
||||||
|
minHeight: Dimensions.get('window').height,
|
||||||
|
},
|
||||||
|
card: {
|
||||||
|
width: '100%',
|
||||||
|
maxWidth: 420,
|
||||||
|
backgroundColor: '#FFF',
|
||||||
|
borderRadius: 28,
|
||||||
|
paddingVertical: 36,
|
||||||
|
paddingHorizontal: 28,
|
||||||
|
alignItems: 'center',
|
||||||
|
shadowColor: Colors.appColourDark,
|
||||||
|
shadowOpacity: 0.08,
|
||||||
|
shadowOffset: {width: 0, height: 12},
|
||||||
|
shadowRadius: 24,
|
||||||
|
elevation: 10,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: 'rgba(255, 255, 255, 0.9)',
|
||||||
|
zIndex: 2,
|
||||||
|
},
|
||||||
|
statusIndicator: {
|
||||||
|
position: 'absolute',
|
||||||
|
top: -20,
|
||||||
|
width: 60,
|
||||||
|
height: 60,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
statusPulse: {
|
||||||
|
position: 'absolute',
|
||||||
|
width: 60,
|
||||||
|
height: 60,
|
||||||
|
borderRadius: 30,
|
||||||
|
backgroundColor: Colors.appColourDark,
|
||||||
|
opacity: 0.2,
|
||||||
|
},
|
||||||
|
statusDot: {
|
||||||
|
width: 44,
|
||||||
|
height: 44,
|
||||||
|
borderRadius: 22,
|
||||||
|
backgroundColor: Colors.appColourDarkDim,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
shadowColor: Colors.appColourDark,
|
||||||
|
shadowOffset: {width: 0, height: 4},
|
||||||
|
shadowOpacity: 0.3,
|
||||||
|
shadowRadius: 8,
|
||||||
|
elevation: 6,
|
||||||
|
},
|
||||||
|
animationContainer: {
|
||||||
|
width: 200,
|
||||||
|
height: 200,
|
||||||
|
marginBottom: 8,
|
||||||
|
},
|
||||||
|
animation: {
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
marginLeft: -20,
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
alignItems: 'center',
|
||||||
|
width: '100%',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 28,
|
||||||
|
fontWeight: '800',
|
||||||
|
color: '#1A1A1A',
|
||||||
|
marginTop: 8,
|
||||||
|
letterSpacing: -0.5,
|
||||||
|
textAlign: 'center',
|
||||||
|
marginBottom: 12,
|
||||||
|
},
|
||||||
|
statusMessage: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: '#FFF5F5',
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
paddingVertical: 10,
|
||||||
|
borderRadius: 12,
|
||||||
|
marginBottom: 24,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: '#FED7D7',
|
||||||
|
},
|
||||||
|
statusIcon: {
|
||||||
|
marginRight: 8,
|
||||||
|
},
|
||||||
|
statusText: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: '#C53030',
|
||||||
|
fontWeight: '600',
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
fontSize: 12,
|
||||||
|
color: '#666',
|
||||||
|
textAlign: 'center',
|
||||||
|
lineHeight: 24,
|
||||||
|
marginBottom: 28,
|
||||||
|
paddingHorizontal: 8,
|
||||||
|
fontFamily: FontsFamily.poppinsMedium,
|
||||||
|
},
|
||||||
|
progressContainer: {
|
||||||
|
width: '100%',
|
||||||
|
marginBottom: 32,
|
||||||
|
},
|
||||||
|
progressBar: {
|
||||||
|
width: '100%',
|
||||||
|
height: 8,
|
||||||
|
backgroundColor: '#E2E8F0',
|
||||||
|
borderRadius: 4,
|
||||||
|
overflow: 'hidden',
|
||||||
|
marginBottom: 12,
|
||||||
|
},
|
||||||
|
progressFill: {
|
||||||
|
width: '60%',
|
||||||
|
height: '100%',
|
||||||
|
backgroundColor: '#8B5CF6',
|
||||||
|
borderRadius: 4,
|
||||||
|
},
|
||||||
|
progressText: {
|
||||||
|
fontSize: 13,
|
||||||
|
color: '#64748B',
|
||||||
|
textAlign: 'center',
|
||||||
|
fontWeight: '500',
|
||||||
|
},
|
||||||
|
infoContainer: {
|
||||||
|
width: '100%',
|
||||||
|
backgroundColor: '#F8FAFF',
|
||||||
|
borderRadius: 16,
|
||||||
|
padding: 20,
|
||||||
|
marginBottom: 32,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: '#EDF2FE',
|
||||||
|
},
|
||||||
|
infoRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginBottom: 16,
|
||||||
|
paddingBottom: 16,
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: '#EDF2F7',
|
||||||
|
},
|
||||||
|
infoLabel: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: '#64748B',
|
||||||
|
fontWeight: '500',
|
||||||
|
marginLeft: 12,
|
||||||
|
marginRight: 8,
|
||||||
|
width: 90,
|
||||||
|
},
|
||||||
|
infoValue: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: '#1A1A1A',
|
||||||
|
fontWeight: '600',
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
statusBadge: {
|
||||||
|
paddingHorizontal: 12,
|
||||||
|
paddingVertical: 6,
|
||||||
|
borderRadius: 20,
|
||||||
|
backgroundColor: '#F3E8FF',
|
||||||
|
},
|
||||||
|
statusMaintenance: {
|
||||||
|
backgroundColor: '#F3E8FF',
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: '#D6BCFA',
|
||||||
|
},
|
||||||
|
statusBadgeText: {
|
||||||
|
fontSize: 12,
|
||||||
|
color: '#8B5CF6',
|
||||||
|
fontWeight: '700',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
letterSpacing: 0.5,
|
||||||
|
},
|
||||||
|
buttonGroup: {
|
||||||
|
width: '100%',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginBottom: 24,
|
||||||
|
},
|
||||||
|
touchable: {
|
||||||
|
width: '100%',
|
||||||
|
shadowColor: '#FF6B6B',
|
||||||
|
shadowOffset: {width: 0, height: 4},
|
||||||
|
shadowOpacity: 0.3,
|
||||||
|
shadowRadius: 8,
|
||||||
|
elevation: 6,
|
||||||
|
marginBottom: 16,
|
||||||
|
},
|
||||||
|
primaryButton: {
|
||||||
|
paddingVertical: 18,
|
||||||
|
paddingHorizontal: 30,
|
||||||
|
borderRadius: 16,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
width: '100%',
|
||||||
|
flexDirection: 'row',
|
||||||
|
},
|
||||||
|
buttonIcon: {
|
||||||
|
marginRight: 10,
|
||||||
|
},
|
||||||
|
primaryButtonText: {
|
||||||
|
color: '#FFF',
|
||||||
|
fontSize: 17,
|
||||||
|
fontWeight: '700',
|
||||||
|
letterSpacing: 0.5,
|
||||||
|
},
|
||||||
|
secondaryButtonGroup: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
width: '100%',
|
||||||
|
gap: 12,
|
||||||
|
},
|
||||||
|
secondaryButton: {
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
paddingVertical: 14,
|
||||||
|
paddingHorizontal: 20,
|
||||||
|
borderRadius: 12,
|
||||||
|
backgroundColor: '#FFF',
|
||||||
|
borderWidth: 1.5,
|
||||||
|
borderColor: '#E2E8F0',
|
||||||
|
gap: 8,
|
||||||
|
},
|
||||||
|
secondaryButtonText: {
|
||||||
|
color: '#64748B',
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: '600',
|
||||||
|
},
|
||||||
|
liveStatus: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
liveIndicator: {
|
||||||
|
position: 'relative',
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
marginRight: 8,
|
||||||
|
},
|
||||||
|
livePulse: {
|
||||||
|
position: 'absolute',
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
borderRadius: 12,
|
||||||
|
backgroundColor: '#10B981',
|
||||||
|
opacity: 0.4,
|
||||||
|
},
|
||||||
|
liveDot: {
|
||||||
|
width: 10,
|
||||||
|
height: 10,
|
||||||
|
borderRadius: 5,
|
||||||
|
backgroundColor: '#10B981',
|
||||||
|
},
|
||||||
|
liveIcon: {
|
||||||
|
marginRight: 6,
|
||||||
|
},
|
||||||
|
liveText: {
|
||||||
|
fontSize: 13,
|
||||||
|
color: '#64748B',
|
||||||
|
fontWeight: '500',
|
||||||
|
},
|
||||||
|
backgroundPattern: {
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
backgroundColor: '#F8FAFF',
|
||||||
|
opacity: 0.3,
|
||||||
|
zIndex: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, ActivityIndicator } from 'react-native';
|
||||||
|
interface LoaderType {
|
||||||
|
size?: 'small' | 'large';
|
||||||
|
color?: string;
|
||||||
|
style?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Loader = ({ size = "large", color = '#000', style }: LoaderType) => {
|
||||||
|
return (
|
||||||
|
<View style={[{ justifyContent: 'center', alignItems: 'center' , flex:1}, style]}>
|
||||||
|
<ActivityIndicator size={size} color={color} />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Loader;
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
import { Filter, Search } from "lucide-react-native";
|
||||||
|
import React from "react";
|
||||||
|
import { View, Text, TextInput, StyleSheet, TouchableOpacity, Platform } from "react-native";
|
||||||
|
import Animated, {
|
||||||
|
useAnimatedStyle,
|
||||||
|
useSharedValue,
|
||||||
|
withTiming,
|
||||||
|
interpolateColor
|
||||||
|
} from "react-native-reanimated";
|
||||||
|
import colors from "../../Constant/Color";
|
||||||
|
|
||||||
|
export interface SearchType {
|
||||||
|
placeholder?: string;
|
||||||
|
onChange: (value: string) => void;
|
||||||
|
label?: string;
|
||||||
|
filter?: boolean;
|
||||||
|
filterPress?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AnimatedView = Animated.createAnimatedComponent(View);
|
||||||
|
|
||||||
|
export const SearchInput: React.FC<SearchType> = ({
|
||||||
|
placeholder,
|
||||||
|
onChange,
|
||||||
|
label,
|
||||||
|
filter = true,
|
||||||
|
filterPress
|
||||||
|
}) => {
|
||||||
|
const isFocused = useSharedValue(0);
|
||||||
|
|
||||||
|
// Animate border color and shadow based on focus
|
||||||
|
const inputContainerStyle = useAnimatedStyle(() => {
|
||||||
|
const borderColor = interpolateColor(
|
||||||
|
isFocused.value,
|
||||||
|
[0, 1],
|
||||||
|
[colors.grayText + '50', colors.grayText] // Assuming colors.primary exists
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
borderColor: borderColor,
|
||||||
|
borderWidth: withTiming(isFocused.value ? 1.5 : 1),
|
||||||
|
transform: [{ scale: withTiming(isFocused.value ? 1.01 : 1) }],
|
||||||
|
shadowOpacity: withTiming(isFocused.value ? 0.1 : 0),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
{label && <Text style={styles.label}>{label}</Text>}
|
||||||
|
|
||||||
|
<View style={styles.row}>
|
||||||
|
<AnimatedView style={[styles.inputWrapper, inputContainerStyle]}>
|
||||||
|
<Search
|
||||||
|
size={18}
|
||||||
|
color={isFocused.value ? colors.grayText : colors.grayText}
|
||||||
|
style={styles.searchIcon}
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
style={styles.input}
|
||||||
|
placeholder={placeholder}
|
||||||
|
onChangeText={onChange}
|
||||||
|
placeholderTextColor={colors.grayText}
|
||||||
|
onFocus={() => (isFocused.value = withTiming(1))}
|
||||||
|
onBlur={() => (isFocused.value = withTiming(0))}
|
||||||
|
selectionColor={colors.grayText}
|
||||||
|
/>
|
||||||
|
</AnimatedView>
|
||||||
|
|
||||||
|
{filter && (
|
||||||
|
<TouchableOpacity
|
||||||
|
activeOpacity={0.7}
|
||||||
|
style={styles.filterButton}
|
||||||
|
onPress={filterPress}
|
||||||
|
>
|
||||||
|
<Filter size={22} color={colors.black} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
marginVertical: 12,
|
||||||
|
marginHorizontal: 16,
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: '600',
|
||||||
|
marginBottom: 8,
|
||||||
|
color: colors.black,
|
||||||
|
letterSpacing: 0.3,
|
||||||
|
},
|
||||||
|
row: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 12,
|
||||||
|
},
|
||||||
|
inputWrapper: {
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
height: 48,
|
||||||
|
backgroundColor: '#F9FAFB',
|
||||||
|
borderRadius: 12,
|
||||||
|
paddingHorizontal: 12,
|
||||||
|
borderWidth: 1,
|
||||||
|
// Shadow for iOS
|
||||||
|
shadowColor: "#000",
|
||||||
|
shadowOffset: { width: 0, height: 2 },
|
||||||
|
shadowRadius: 4,
|
||||||
|
// Elevation for Android
|
||||||
|
elevation: 2,
|
||||||
|
},
|
||||||
|
searchIcon: {
|
||||||
|
marginRight: 8,
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
flex: 1,
|
||||||
|
height: '100%',
|
||||||
|
fontSize: 15,
|
||||||
|
color: colors.black,
|
||||||
|
paddingVertical: Platform.OS === 'ios' ? 0 : 8,
|
||||||
|
},
|
||||||
|
filterButton: {
|
||||||
|
height: 48,
|
||||||
|
width: 48,
|
||||||
|
backgroundColor: '#FFFFFF',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
borderRadius: 12,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: colors.grayText + '30',
|
||||||
|
elevation: 2,
|
||||||
|
shadowColor: "#000",
|
||||||
|
shadowOffset: { width: 0, height: 1 },
|
||||||
|
shadowOpacity: 0.1,
|
||||||
|
shadowRadius: 2,
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -0,0 +1,246 @@
|
|||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { View, Text, StyleSheet, TouchableOpacity, Dimensions, Image } from 'react-native';
|
||||||
|
import Modal from 'react-native-modal';
|
||||||
|
import Animated, { useSharedValue, useAnimatedStyle, withTiming, withDelay } from 'react-native-reanimated';
|
||||||
|
import { X, Home, Gavel, FileText, User, LogOut, ChevronRight } from 'lucide-react-native';
|
||||||
|
import { CommonActions, useNavigation } from '@react-navigation/native';
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
import { removeToken } from '../../Redux/Slices/Token';
|
||||||
|
import NavigationString from '../../Constant/NavigationString';
|
||||||
|
|
||||||
|
const { width } = Dimensions.get('window');
|
||||||
|
const DRAWER_WIDTH = width * 0.78;
|
||||||
|
|
||||||
|
interface Props { visible: boolean; onClose: () => void; }
|
||||||
|
|
||||||
|
const SideDrawer: React.FC<Props> = ({ visible, onClose }) => {
|
||||||
|
const navigation: any = useNavigation();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const user = { name: 'Rohit Kumar', role: 'Procurement Manager', avatar: 'https://i.pravatar.cc/150?img=12', };
|
||||||
|
const translateX = useSharedValue(-DRAWER_WIDTH);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
translateX.value = visible
|
||||||
|
? withTiming(0, { duration: 300 })
|
||||||
|
: withTiming(-DRAWER_WIDTH, { duration: 250 });
|
||||||
|
}, [visible]);
|
||||||
|
|
||||||
|
const drawerStyle = useAnimatedStyle(() => ({
|
||||||
|
transform: [{ translateX: translateX.value }],
|
||||||
|
}));
|
||||||
|
|
||||||
|
const menuItems = [
|
||||||
|
{
|
||||||
|
label: 'Home',
|
||||||
|
icon: Home,
|
||||||
|
onPress: () => navigation.navigate(NavigationString.Home),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Auctions',
|
||||||
|
icon: Gavel,
|
||||||
|
onPress: () => navigation.navigate(NavigationString.Auction),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Tenders',
|
||||||
|
icon: FileText,
|
||||||
|
onPress: () => navigation.navigate(NavigationString.Tender),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Profile',
|
||||||
|
icon: User,
|
||||||
|
onPress: () => { },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isVisible={visible}
|
||||||
|
onBackdropPress={onClose}
|
||||||
|
onBackButtonPress={onClose}
|
||||||
|
backdropOpacity={0.45}
|
||||||
|
animationIn="fadeIn"
|
||||||
|
animationOut="fadeOut"
|
||||||
|
style={styles.modal}
|
||||||
|
useNativeDriver
|
||||||
|
>
|
||||||
|
<Animated.View style={[styles.drawer, drawerStyle]}>
|
||||||
|
{/* ================= Header / Profile ================= */}
|
||||||
|
<View style={styles.profileHeader}>
|
||||||
|
<TouchableOpacity style={styles.closeBtn} onPress={onClose}>
|
||||||
|
<X size={20} color="#111827" />
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
<Image source={{ uri: user.avatar }} style={styles.avatar} />
|
||||||
|
|
||||||
|
<Text style={styles.userName}>{user.name}</Text>
|
||||||
|
<Text style={styles.userRole}>{user.role}</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* ================= Menu ================= */}
|
||||||
|
<View style={styles.menuContainer}>
|
||||||
|
{menuItems.map((item, index) => {
|
||||||
|
const Icon = item.icon;
|
||||||
|
|
||||||
|
const itemStyle = useAnimatedStyle(() => ({
|
||||||
|
opacity: withDelay(
|
||||||
|
index * 80,
|
||||||
|
withTiming(1, { duration: 300 }),
|
||||||
|
),
|
||||||
|
transform: [
|
||||||
|
{
|
||||||
|
translateX: withDelay(
|
||||||
|
index * 80,
|
||||||
|
withTiming(0, { duration: 300 }),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Animated.View key={item.label} style={itemStyle}>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.menuItem}
|
||||||
|
activeOpacity={0.7}
|
||||||
|
onPress={() => {
|
||||||
|
onClose();
|
||||||
|
item.onPress();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View style={styles.menuLeft}>
|
||||||
|
<Icon size={20} color="#1F2937" />
|
||||||
|
<Text style={styles.menuText}>{item.label}</Text>
|
||||||
|
</View>
|
||||||
|
<ChevronRight size={18} color="#9CA3AF" />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</Animated.View>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* ================= Logout ================= */}
|
||||||
|
<View style={styles.logoutContainer}>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.logoutBtn}
|
||||||
|
onPress={() => {
|
||||||
|
dispatch(removeToken());
|
||||||
|
navigation.dispatch(
|
||||||
|
CommonActions.reset({
|
||||||
|
index: 0,
|
||||||
|
routes: [{ name: NavigationString.Login }],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<LogOut size={20} color="#EF4444" />
|
||||||
|
<Text style={styles.logoutText}>Logout</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</Animated.View>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SideDrawer;
|
||||||
|
|
||||||
|
/* ================= Styles ================= */
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
modal: {
|
||||||
|
margin: 0,
|
||||||
|
justifyContent: 'flex-start',
|
||||||
|
},
|
||||||
|
|
||||||
|
drawer: {
|
||||||
|
width: DRAWER_WIDTH,
|
||||||
|
height: '100%',
|
||||||
|
backgroundColor: '#FFFFFF',
|
||||||
|
borderTopRightRadius: 24,
|
||||||
|
borderBottomRightRadius: 24,
|
||||||
|
elevation: 12,
|
||||||
|
},
|
||||||
|
|
||||||
|
/* -------- Profile -------- */
|
||||||
|
profileHeader: {
|
||||||
|
backgroundColor: '#F9FAFB',
|
||||||
|
paddingTop: 50,
|
||||||
|
paddingBottom: 24,
|
||||||
|
alignItems: 'center',
|
||||||
|
borderTopRightRadius: 24,
|
||||||
|
},
|
||||||
|
|
||||||
|
closeBtn: {
|
||||||
|
position: 'absolute',
|
||||||
|
top: 20,
|
||||||
|
right: 16,
|
||||||
|
},
|
||||||
|
|
||||||
|
avatar: {
|
||||||
|
width: 72,
|
||||||
|
height: 72,
|
||||||
|
borderRadius: 36,
|
||||||
|
marginBottom: 10,
|
||||||
|
},
|
||||||
|
|
||||||
|
userName: {
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: '700',
|
||||||
|
color: '#111827',
|
||||||
|
},
|
||||||
|
|
||||||
|
userRole: {
|
||||||
|
fontSize: 13,
|
||||||
|
color: '#6B7280',
|
||||||
|
marginTop: 2,
|
||||||
|
},
|
||||||
|
|
||||||
|
/* -------- Menu -------- */
|
||||||
|
menuContainer: {
|
||||||
|
paddingHorizontal: 14,
|
||||||
|
paddingTop: 20,
|
||||||
|
},
|
||||||
|
|
||||||
|
menuItem: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingVertical: 14,
|
||||||
|
paddingHorizontal: 12,
|
||||||
|
borderRadius: 14,
|
||||||
|
marginBottom: 6,
|
||||||
|
},
|
||||||
|
|
||||||
|
menuLeft: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 12,
|
||||||
|
},
|
||||||
|
|
||||||
|
menuText: {
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: '600',
|
||||||
|
color: '#1F2937',
|
||||||
|
},
|
||||||
|
|
||||||
|
/* -------- Logout -------- */
|
||||||
|
logoutContainer: {
|
||||||
|
marginTop: 'auto',
|
||||||
|
paddingHorizontal: 14,
|
||||||
|
paddingBottom: 24,
|
||||||
|
borderTopWidth: 1,
|
||||||
|
borderTopColor: '#E5E7EB',
|
||||||
|
},
|
||||||
|
|
||||||
|
logoutBtn: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 12,
|
||||||
|
paddingVertical: 16,
|
||||||
|
},
|
||||||
|
|
||||||
|
logoutText: {
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: '600',
|
||||||
|
color: '#EF4444',
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { View, Dimensions } from 'react-native';
|
||||||
|
import React from 'react';
|
||||||
|
import SkeletonPlaceholder from 'react-native-skeleton-placeholder';
|
||||||
|
|
||||||
|
const Skeleton = () => {
|
||||||
|
const { width } = Dimensions.get('screen');
|
||||||
|
const cardWidth = width - 30; // full width with some padding
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={{ padding: 10 }}>
|
||||||
|
{[...Array(6)].map((_, index) => (
|
||||||
|
<SkeletonPlaceholder key={index}>
|
||||||
|
<SkeletonPlaceholder.Item
|
||||||
|
width={cardWidth}
|
||||||
|
marginBottom={20}
|
||||||
|
borderRadius={12}
|
||||||
|
overflow="hidden"
|
||||||
|
>
|
||||||
|
{/* Image */}
|
||||||
|
<SkeletonPlaceholder.Item width={cardWidth} height={150} borderRadius={12} />
|
||||||
|
|
||||||
|
{/* Title */}
|
||||||
|
<SkeletonPlaceholder.Item marginTop={10} width={cardWidth * 0.7} height={18} borderRadius={6} />
|
||||||
|
|
||||||
|
{/* Subtitle / Price */}
|
||||||
|
<SkeletonPlaceholder.Item marginTop={6} width={cardWidth * 0.5} height={14} borderRadius={6} />
|
||||||
|
</SkeletonPlaceholder.Item>
|
||||||
|
</SkeletonPlaceholder>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Skeleton;
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { View, Dimensions } from 'react-native';
|
||||||
|
import React from 'react';
|
||||||
|
import SkeletonPlaceholder from 'react-native-skeleton-placeholder';
|
||||||
|
|
||||||
|
const SkeltonList = () => {
|
||||||
|
const { width, height } = Dimensions.get('screen');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={{ width: width - 30, height }}>
|
||||||
|
{[...Array(10)].map((_, index) => (
|
||||||
|
<SkeletonPlaceholder key={index} borderRadius={5}>
|
||||||
|
<SkeletonPlaceholder.Item width={"100%"} height={50} marginBottom={12} />
|
||||||
|
</SkeletonPlaceholder>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SkeltonList;
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import React, { useEffect, useRef } from 'react';
|
||||||
|
import { Animated, Text, StyleSheet } from 'react-native';
|
||||||
|
import colors from '../../Constant/Color';
|
||||||
|
|
||||||
|
interface AuctionItemProps {
|
||||||
|
item: any;
|
||||||
|
index: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AuctionItem: React.FC<AuctionItemProps> = ({ item, index }) => {
|
||||||
|
const scale = useRef(new Animated.Value(0)).current;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
Animated.spring(scale, {
|
||||||
|
toValue: 1,
|
||||||
|
friction: 8,
|
||||||
|
useNativeDriver: true,
|
||||||
|
delay: index * 50,
|
||||||
|
}).start();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Animated.View style={[styles.itemCard, { transform: [{ scale }] }]}>
|
||||||
|
<Text style={styles.itemTitle}>{item.title || `${item.auctionNumber}`}</Text>
|
||||||
|
<Text style={styles.itemDate}>End Date: {item.purchaseEnd || 'N/A'} </Text>
|
||||||
|
</Animated.View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AuctionItem;
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
itemCard: {
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
padding: 18,
|
||||||
|
borderRadius: 16,
|
||||||
|
marginBottom: 14,
|
||||||
|
shadowColor: '#000',
|
||||||
|
shadowOffset: { width: 0, height: 4 },
|
||||||
|
shadowOpacity: 0.12,
|
||||||
|
shadowRadius: 8,
|
||||||
|
elevation: 4,
|
||||||
|
},
|
||||||
|
itemTitle: {
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: '700',
|
||||||
|
marginBottom: 4,
|
||||||
|
color: '#020617',
|
||||||
|
},
|
||||||
|
itemDate: {
|
||||||
|
fontSize: 13,
|
||||||
|
color: '#64748b',
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import colors from './Color';
|
||||||
|
import AllUrls from './AllUrls';
|
||||||
|
import FontsFamily from './FontsFamily';
|
||||||
|
import ImagePath from './ImagePath';
|
||||||
|
import NavigationString from './NavigationString';
|
||||||
|
import { showErrorAlert, showSuccessAlert, showToast } from './ShowDailog';
|
||||||
|
import Variable from './Variable';
|
||||||
|
|
||||||
|
export { AllUrls, colors, ImagePath, NavigationString, showErrorAlert, showSuccessAlert, showToast, Variable ,FontsFamily};
|
||||||
|
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
import Variable from "./Variable"
|
||||||
|
|
||||||
|
// Customer
|
||||||
|
const Login = `${Variable.Main_Base}/customer/login`
|
||||||
|
const OtpVerify = `${Variable.Main_Base}/customer/verify_otp`
|
||||||
|
const userUpdate = `${Variable.Main_Base}/customer`
|
||||||
|
const userLogout = `${Variable.Main_Base}/customer/logout`
|
||||||
|
const userInfo = `${Variable.Main_Base}/customer/info`
|
||||||
|
|
||||||
|
// Category
|
||||||
|
const categoryApi = `${Variable.Main_Base}/category`
|
||||||
|
|
||||||
|
// Brand
|
||||||
|
const brandsApi = `${Variable.Main_Base}/brand`
|
||||||
|
|
||||||
|
// Brand
|
||||||
|
const bannersApi = `${Variable.Main_Base}/banner/position`
|
||||||
|
|
||||||
|
// Vehicle Segment
|
||||||
|
const segmentApi = `${Variable.Main_Base}/vehicleSegmentType/without-page`
|
||||||
|
const vehicleApi = `${Variable.Main_Base}/vehicle`
|
||||||
|
|
||||||
|
// Products
|
||||||
|
const productApi = `${Variable.Main_Base}/product`
|
||||||
|
const brandProductApi = `${Variable.Main_Base}/product/assigned`
|
||||||
|
const ProductDetailsApi = `${Variable.Main_Base}/product`
|
||||||
|
const ProductWishlistApi = `${Variable.Main_Base}/wish-list`
|
||||||
|
const recentViewedProductApi = `${Variable.Main_Base}/view`
|
||||||
|
const updateAddToCartApi = `${Variable.Main_Base}/add-to-cart`
|
||||||
|
const brandCategoryApi = `${Variable.Main_Base}/category/brand`
|
||||||
|
|
||||||
|
// Rating
|
||||||
|
const ratingApi = `${Variable.Main_Base}/rating`
|
||||||
|
|
||||||
|
// Coupon
|
||||||
|
const couponApi = `${Variable.Main_Base}/coupon`
|
||||||
|
|
||||||
|
// Orders
|
||||||
|
const orderApi = `${Variable.Main_Base}/order`
|
||||||
|
|
||||||
|
// Address
|
||||||
|
const addressApi = `${Variable.Main_Base}/address`
|
||||||
|
const addressStatusApi = `${Variable.Main_Base}/address/status`
|
||||||
|
|
||||||
|
// Recharge
|
||||||
|
const rechrgeWalletApi = `${Variable.Main_Base}/wallets/create-order`
|
||||||
|
const rechrgeWalletVerifyApi = `${Variable.Main_Base}/wallets/verify`
|
||||||
|
const rechrgeWalletHistoryApi = `${Variable.Main_Base}/wallets`
|
||||||
|
|
||||||
|
// Order Payment
|
||||||
|
const orderPaymentApi = `${Variable.Main_Base}/order/init`
|
||||||
|
const orderConfirmApi = `${Variable.Main_Base}/order/callback`
|
||||||
|
|
||||||
|
// Charges
|
||||||
|
const chargesApi = `${Variable.Main_Base}/charges`
|
||||||
|
// FAQs
|
||||||
|
const FAQsApi = `${Variable.Main_Base}/faq`
|
||||||
|
|
||||||
|
// offers
|
||||||
|
const offersApi = `${Variable.Main_Base}/recharge`
|
||||||
|
// PrivacyPolicy
|
||||||
|
const privacyPolicyApi = `${Variable.Main_Base}/documents`
|
||||||
|
|
||||||
|
const AllUrls = {
|
||||||
|
Login, OtpVerify, userUpdate, userLogout, categoryApi, brandsApi, bannersApi, segmentApi, productApi, brandProductApi, ProductDetailsApi, ratingApi,
|
||||||
|
ProductWishlistApi, recentViewedProductApi, updateAddToCartApi, vehicleApi, brandCategoryApi, userInfo, couponApi, orderApi, addressApi, addressStatusApi,
|
||||||
|
rechrgeWalletApi, orderPaymentApi, orderConfirmApi, rechrgeWalletVerifyApi, chargesApi, FAQsApi, rechrgeWalletHistoryApi, offersApi,privacyPolicyApi
|
||||||
|
};
|
||||||
|
export default AllUrls;
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
const colors = {
|
||||||
|
// Primary (Orange-based)
|
||||||
|
primary: '#F97316', // Main orange (buttons, highlights)
|
||||||
|
primaryLight: '#FDBA74', // Soft orange (chips, badges)
|
||||||
|
primaryHighlight: '#f4f1efff', // Very light orange (backgrounds)
|
||||||
|
|
||||||
|
// Secondary (Warm contrast)
|
||||||
|
secondary: '#1B4B66', // Deep blue for balance
|
||||||
|
secondaryLight: '#639599', // Muted blue
|
||||||
|
|
||||||
|
// Status colors
|
||||||
|
success: '#22C55E', // Green
|
||||||
|
error: '#EF4444', // Red
|
||||||
|
warning: '#F59E0B', // Amber
|
||||||
|
info: '#3B82F6', // Blue
|
||||||
|
golden: '#FFD700',
|
||||||
|
|
||||||
|
// Neutral colors
|
||||||
|
white: '#FFFFFF',
|
||||||
|
black: '#000000',
|
||||||
|
darkText: '#2B2B2B',
|
||||||
|
grayText: '#6B7280',
|
||||||
|
lightText: '#9CA3AF',
|
||||||
|
border: '#E5E7EB',
|
||||||
|
lightGray: '#F9FAFB',
|
||||||
|
disabled: '#D1D5DB',
|
||||||
|
|
||||||
|
// Additional
|
||||||
|
placeholder: '#9CA3AF',
|
||||||
|
backdrop: 'rgba(0,0,0,0.5)',
|
||||||
|
shadow: 'rgba(0,0,0,0.12)',
|
||||||
|
|
||||||
|
// Aliases (optional)
|
||||||
|
Orange: '#F97316',
|
||||||
|
OrangeLight: '#FDBA74',
|
||||||
|
OrangeSoft: '#FFF3E8',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default colors;
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
const FontsFamily = {
|
||||||
|
poppinsRegular: 'Poppins-Regular',
|
||||||
|
poppinsThin: 'Poppins-Thin',
|
||||||
|
poppinsThinItalic: 'Poppins-ThinItalic',
|
||||||
|
poppinsExtraLight: 'Poppins-ExtraLight',
|
||||||
|
poppinsExtraLightItalic: 'Poppins-ExtraLightItalic',
|
||||||
|
poppinsLight: 'Poppins-Light',
|
||||||
|
poppinsLightItalic: 'Poppins-LightItalic',
|
||||||
|
poppinsItalic: 'Poppins-Italic',
|
||||||
|
poppinsMedium: 'Poppins-Medium',
|
||||||
|
poppinsMediumItalic: 'Poppins-MediumItalic',
|
||||||
|
poppinsSemiBold: 'Poppins-SemiBold',
|
||||||
|
poppinsSemiBoldItalic: 'Poppins-SemiBoldItalic',
|
||||||
|
poppinsBold: 'Poppins-Bold',
|
||||||
|
poppinsBoldItalic: 'Poppins-BoldItalic',
|
||||||
|
poppinsExtraBold: 'Poppins-ExtraBold',
|
||||||
|
poppinsExtraBoldItalic: 'Poppins-ExtraBoldItalic',
|
||||||
|
poppinsBlack: 'Poppins-Black',
|
||||||
|
poppinsBlackItalic: 'Poppins-BlackItalic',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FontsFamily;
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import OnBoard1 from '../Images/Onboarding/undraw_1.svg'
|
||||||
|
import OnBoard2 from '../Images/Onboarding/undraw_2.svg'
|
||||||
|
import OnBoard3 from '../Images/Onboarding/undraw_3.svg'
|
||||||
|
import Diamond from '../Images/diamond.svg'
|
||||||
|
import Hill from '../Images/Svg/Hill.svg'
|
||||||
|
const Logo = require('../Images/logo.png')
|
||||||
|
const Logo2 = require('../Images/logo2.png')
|
||||||
|
const Icon = {
|
||||||
|
Logo,
|
||||||
|
Logo2
|
||||||
|
}
|
||||||
|
const ImagePath = {
|
||||||
|
Icon,
|
||||||
|
OnBoard1,
|
||||||
|
OnBoard2,
|
||||||
|
OnBoard3,
|
||||||
|
Diamond,
|
||||||
|
Hill
|
||||||
|
|
||||||
|
}
|
||||||
|
export default ImagePath;
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
const NavigationString = {
|
||||||
|
Login: 'Login',
|
||||||
|
Splash: 'Splash',
|
||||||
|
Home: 'Home',
|
||||||
|
Onboarding: 'Onboarding',
|
||||||
|
Auction: 'Auction',
|
||||||
|
Tender: 'Tender',
|
||||||
|
PurchaseList: 'PurchaseList',
|
||||||
|
Emd: 'Emd',
|
||||||
|
StartBidding: 'StartBidding',
|
||||||
|
PurchaseAuctionList:'PurchaseAuctionList'
|
||||||
|
}
|
||||||
|
export default NavigationString
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { ALERT_TYPE, Dialog, Root, Toast } from 'react-native-alert-notification';
|
||||||
|
|
||||||
|
const showSuccessAlert = (text: string) => {
|
||||||
|
Toast.show({
|
||||||
|
type: ALERT_TYPE.SUCCESS,
|
||||||
|
title: 'Success',
|
||||||
|
textBody: text,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const showErrorAlert = (text: string) => {
|
||||||
|
Toast.show({
|
||||||
|
type: ALERT_TYPE.DANGER,
|
||||||
|
title: 'Error',
|
||||||
|
textBody: text,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const showToast = (text: string) => {
|
||||||
|
Toast.show({
|
||||||
|
type: ALERT_TYPE.SUCCESS,
|
||||||
|
title: 'Success',
|
||||||
|
textBody: text,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
export { showErrorAlert, showSuccessAlert, showToast }
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
const webSocketUrl = 'https://procurelinc.in/Auction/liveBid/';
|
||||||
|
const socketUrl = `https://procurelinc.in/Auction/api/`;
|
||||||
|
const Variables = { webSocketUrl, socketUrl }
|
||||||
|
export default Variables
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
const formatHtmlForWebView = (rawHtml: string) => {
|
||||||
|
if (!rawHtml) return '';
|
||||||
|
|
||||||
|
// Remove dangerous scripts
|
||||||
|
let safeHtml = rawHtml
|
||||||
|
.replace(/<script[\s\S]*?<\/script>/gi, '') // Remove all script tags
|
||||||
|
.replace(/on\w+="[^"]*"/g, '') // Remove inline JS events
|
||||||
|
.replace(/window\.[^;]*;/g, '') // Remove window.* calls
|
||||||
|
.replace(/alert\(.*?\);?/g, '') // Remove alert()
|
||||||
|
.replace(/history\.(forward|back)\(\);?/g, '') // Remove back button blocking
|
||||||
|
.replace(/document\.onkeydown[\s\S]*?=/g, '') // Remove key blocking
|
||||||
|
.replace(/contextmenu/gi, ''); // Remove right click disable
|
||||||
|
|
||||||
|
// Wrap inside full safe HTML structure
|
||||||
|
const finalHtml = `
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
}
|
||||||
|
body { margin: 0; padding: 0; font-family: Arial, sans-serif; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
${safeHtml}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`;
|
||||||
|
|
||||||
|
return finalHtml;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default formatHtmlForWebView;
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { Platform } from 'react-native';
|
||||||
|
import * as Network from 'expo-network';
|
||||||
|
|
||||||
|
export const getDeviceDetails = async () => {
|
||||||
|
const ipAddress = await Network.getIpAddressAsync();
|
||||||
|
|
||||||
|
const detail = [
|
||||||
|
{ name: 'IMEI', value: '356938035643809' },
|
||||||
|
{ name: 'IP', value: ipAddress },
|
||||||
|
{ name: 'OS', value: Platform.OS },
|
||||||
|
{ name: 'APP', value: 'Globots' },
|
||||||
|
];
|
||||||
|
|
||||||
|
return {detail};
|
||||||
|
};
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export const getThemeBgColor = (value: string) => {
|
||||||
|
return value === "light" ? "black" : "white";
|
||||||
|
};
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
let counter = 0;
|
||||||
|
export const uniqueIdGenerator = (prefix = 'id') => {
|
||||||
|
const uniquePart = Date.now().toString(36) + Math.random().toString(36).substring(2, 8);
|
||||||
|
counter++;
|
||||||
|
return `${prefix}_${uniquePart}_${counter}`;
|
||||||
|
};
|
||||||
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 735 KiB |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 480 480'><path d='M197.6 42.4 42.4 197.6a60 60 0 0 0 0 84.8l155.2 155.2a60 60 0 0 0 84.8 0l155.2-155.2a60 60 0 0 0 0-84.8L282.4 42.4a60 60 0 0 0-84.8 0Z' fill='#fff'></path></svg>
|
||||||
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 66 KiB |
@@ -0,0 +1,182 @@
|
|||||||
|
import React, { useEffect, useRef } from 'react';
|
||||||
|
import {
|
||||||
|
View,
|
||||||
|
Text,
|
||||||
|
TouchableOpacity,
|
||||||
|
StyleSheet,
|
||||||
|
Dimensions,
|
||||||
|
Animated,
|
||||||
|
} from 'react-native';
|
||||||
|
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
|
||||||
|
import * as LucideIcons from 'lucide-react-native';
|
||||||
|
import Screens from '../Screens';
|
||||||
|
import colors from '../Constant/Color';
|
||||||
|
import NavigationString from '../Constant/NavigationString';
|
||||||
|
|
||||||
|
const { width } = Dimensions.get('window');
|
||||||
|
const TAB_BAR_HEIGHT = 60;
|
||||||
|
const TAB_BUTTON_WIDTH = width / 4;
|
||||||
|
|
||||||
|
const Tab = createBottomTabNavigator();
|
||||||
|
|
||||||
|
const TabButton = ({ route, isFocused, onPress }: any) => {
|
||||||
|
const icons: Record<string, { outline: any; filled: any }> = {
|
||||||
|
Home: { outline: "Home", filled: "Home" },
|
||||||
|
Auction: { outline: "Gavel", filled: "Gavel" },
|
||||||
|
Tender: { outline: "FileText", filled: "FileText" },
|
||||||
|
};
|
||||||
|
const IconName = LucideIcons[isFocused ? icons[route.name].filled : icons[route.name].outline];
|
||||||
|
const scaleAnim = useRef(new Animated.Value(1)).current;
|
||||||
|
const translateYAnim = useRef(new Animated.Value(0)).current;
|
||||||
|
const opacityAnim = useRef(new Animated.Value(1)).current;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isFocused) {
|
||||||
|
Animated.parallel([
|
||||||
|
Animated.sequence([
|
||||||
|
Animated.timing(translateYAnim, {
|
||||||
|
toValue: -8,
|
||||||
|
duration: 120,
|
||||||
|
useNativeDriver: true,
|
||||||
|
}),
|
||||||
|
Animated.spring(translateYAnim, {
|
||||||
|
toValue: 0,
|
||||||
|
friction: 3,
|
||||||
|
tension: 80,
|
||||||
|
useNativeDriver: true,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
Animated.spring(scaleAnim, {
|
||||||
|
toValue: 1.3,
|
||||||
|
friction: 5,
|
||||||
|
tension: 150,
|
||||||
|
useNativeDriver: true,
|
||||||
|
}),
|
||||||
|
Animated.timing(opacityAnim, {
|
||||||
|
toValue: 1,
|
||||||
|
duration: 250,
|
||||||
|
useNativeDriver: true,
|
||||||
|
}),
|
||||||
|
]).start();
|
||||||
|
} else {
|
||||||
|
Animated.parallel([
|
||||||
|
Animated.spring(scaleAnim, {
|
||||||
|
toValue: 1,
|
||||||
|
friction: 5,
|
||||||
|
tension: 150,
|
||||||
|
useNativeDriver: true,
|
||||||
|
}),
|
||||||
|
Animated.timing(opacityAnim, {
|
||||||
|
toValue: 0.6,
|
||||||
|
duration: 150,
|
||||||
|
useNativeDriver: true,
|
||||||
|
}),
|
||||||
|
]).start();
|
||||||
|
}
|
||||||
|
}, [isFocused]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={onPress}
|
||||||
|
style={[styles.tabButton, { width: TAB_BUTTON_WIDTH }]}
|
||||||
|
activeOpacity={0.8}
|
||||||
|
>
|
||||||
|
<Animated.View
|
||||||
|
style={[
|
||||||
|
styles.iconContainer,
|
||||||
|
{
|
||||||
|
transform: [{ scale: scaleAnim }, { translateY: translateYAnim }],
|
||||||
|
opacity: opacityAnim,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{IconName && (
|
||||||
|
<IconName
|
||||||
|
size={20}
|
||||||
|
color={isFocused ? colors.primary : '#888'}
|
||||||
|
strokeWidth={2}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Animated.View>
|
||||||
|
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
styles.label,
|
||||||
|
{
|
||||||
|
color: isFocused ? colors.primary : '#888',
|
||||||
|
fontWeight: isFocused ? '600' : '400',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{route.name === 'BrandDay' ? 'Coupons' : route.name}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const CustomTabBar = ({ state, descriptors, navigation }: any) => {
|
||||||
|
return (
|
||||||
|
<View style={styles.tabBar}>
|
||||||
|
{state.routes.map((route: any, index: number) => {
|
||||||
|
const isFocused = state.index === index;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TabButton
|
||||||
|
key={route.key}
|
||||||
|
route={route}
|
||||||
|
isFocused={isFocused}
|
||||||
|
onPress={() => navigation.navigate(route.name)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function BottomTab() {
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Tab.Navigator
|
||||||
|
tabBar={(props) => <CustomTabBar {...props} />}
|
||||||
|
screenOptions={{
|
||||||
|
headerShown: false,
|
||||||
|
tabBarHideOnKeyboard: true,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Tab.Screen name="Home" component={Screens.Home} />
|
||||||
|
<Tab.Screen name={NavigationString.Auction} component={Screens.Auction} />
|
||||||
|
<Tab.Screen name={NavigationString.Tender} component={Screens.Tender} />
|
||||||
|
</Tab.Navigator>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
},
|
||||||
|
tabBar: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
height: TAB_BAR_HEIGHT,
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
borderTopWidth: 1,
|
||||||
|
borderTopColor: '#eee',
|
||||||
|
elevation: 5,
|
||||||
|
},
|
||||||
|
tabButton: {
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
height: '100%',
|
||||||
|
},
|
||||||
|
iconContainer: {
|
||||||
|
marginBottom: 4,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
fontSize: 11,
|
||||||
|
textAlign: 'center',
|
||||||
|
includeFontPadding: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { createStackNavigator } from '@react-navigation/stack';
|
||||||
|
import NavigationString from '../Constant/NavigationString';
|
||||||
|
import Screens from '../Screens';
|
||||||
|
import colors from '../Constant/Color';
|
||||||
|
import BottomTab from './Bottom';
|
||||||
|
|
||||||
|
export default function MainStack() {
|
||||||
|
const Stack = createStackNavigator();
|
||||||
|
const option = { headerShown: false };
|
||||||
|
const fadeTransition = {
|
||||||
|
gestureDirection: 'horizontal',
|
||||||
|
transitionSpec: {
|
||||||
|
open: { animation: 'timing', config: { duration: 300 } },
|
||||||
|
close: { animation: 'timing', config: { duration: 300 } },
|
||||||
|
},
|
||||||
|
cardStyleInterpolator: ({ current }) => ({
|
||||||
|
cardStyle: {
|
||||||
|
opacity: current.progress,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack.Navigator
|
||||||
|
screenOptions={{
|
||||||
|
cardStyle: { backgroundColor: colors.white },
|
||||||
|
...fadeTransition
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Stack.Screen name={NavigationString.Splash} component={Screens.Splash} options={option} />
|
||||||
|
<Stack.Screen name={NavigationString.Onboarding} component={Screens.Onboarding} options={option} />
|
||||||
|
<Stack.Screen name={NavigationString.Login} component={Screens.Login} options={option} />
|
||||||
|
<Stack.Screen name={NavigationString.Home} component={BottomTab} options={option} />
|
||||||
|
<Stack.Screen name={NavigationString.PurchaseList} component={Screens.PurchaseList} options={option} />
|
||||||
|
<Stack.Screen name={NavigationString.Emd} component={Screens.Emd} options={option} />
|
||||||
|
<Stack.Screen name={NavigationString.StartBidding} component={Screens.StartBidding} options={option} />
|
||||||
|
<Stack.Screen name={NavigationString.PurchaseAuctionList} component={Screens.PurchaseAuctionList} options={option} />
|
||||||
|
|
||||||
|
</Stack.Navigator>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import MainStack from './MainStack';
|
||||||
|
export default function Routes() {
|
||||||
|
return <>{MainStack()} </>
|
||||||
|
}
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
import {
|
||||||
|
check,
|
||||||
|
request,
|
||||||
|
RESULTS,
|
||||||
|
PermissionStatus,
|
||||||
|
openSettings,
|
||||||
|
PERMISSIONS,
|
||||||
|
Permission,
|
||||||
|
} from 'react-native-permissions';
|
||||||
|
import { Platform } from 'react-native';
|
||||||
|
|
||||||
|
// Normalize permission for platform
|
||||||
|
const normalizePermission = (perm: string): string => {
|
||||||
|
return Platform.select({
|
||||||
|
ios: (PERMISSIONS.IOS as Record<string, string>)[perm] || perm,
|
||||||
|
android: (PERMISSIONS.ANDROID as Record<string, string>)[perm] || perm,
|
||||||
|
default: perm,
|
||||||
|
})!;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generic permission requester
|
||||||
|
export const requestSinglePermission = async (
|
||||||
|
permission: string
|
||||||
|
): Promise<boolean> => {
|
||||||
|
const normalized = normalizePermission(permission);
|
||||||
|
|
||||||
|
const status: PermissionStatus = await check(normalized);
|
||||||
|
|
||||||
|
if (status === RESULTS.GRANTED) {
|
||||||
|
console.log(`✅ ${permission} already granted.`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === RESULTS.BLOCKED) {
|
||||||
|
console.log(`🚫 ${permission} is blocked. Prompt user to go to settings.`);
|
||||||
|
openSettings();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === RESULTS.DENIED || status === RESULTS.LIMITED) {
|
||||||
|
const result: PermissionStatus = await request(normalized);
|
||||||
|
if (result === RESULTS.GRANTED) {
|
||||||
|
console.log(`✅ ${permission} granted after request.`);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
console.log(`❌ ${permission} denied.`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Fallback definition
|
||||||
|
const POST_NOTIFICATIONS =
|
||||||
|
Platform.OS === 'android' ? 'android.permission.POST_NOTIFICATIONS' : '';
|
||||||
|
|
||||||
|
export const requestNotificationPermission = async (): Promise<boolean> => {
|
||||||
|
|
||||||
|
if (Platform.OS !== 'android' || !POST_NOTIFICATIONS) {
|
||||||
|
console.log('Notification permission not needed on this platform.');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const status: PermissionStatus = await check(POST_NOTIFICATIONS);
|
||||||
|
|
||||||
|
if (status === RESULTS.GRANTED) {
|
||||||
|
console.log('🔔 Notification permission already granted.');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === RESULTS.BLOCKED) {
|
||||||
|
console.log('🔕 Notification permission is blocked. Prompt user to open settings.');
|
||||||
|
openSettings();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === RESULTS.DENIED) {
|
||||||
|
const result: PermissionStatus = await request(POST_NOTIFICATIONS);
|
||||||
|
if (result === RESULTS.GRANTED) {
|
||||||
|
console.log('✅ Notification permission granted.');
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
console.log('❌ Notification permission denied.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const isAnyPermissionGranted = async (permissions: Permission[]): Promise<boolean> => {
|
||||||
|
for (const permission of permissions) {
|
||||||
|
const result = await check(permission);
|
||||||
|
|
||||||
|
if (result === RESULTS.GRANTED) {
|
||||||
|
return true; // At least one permission is granted
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false; // No permissions granted
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function checkPermission(permission:any) {
|
||||||
|
try {
|
||||||
|
const result = await check(permission);
|
||||||
|
return result === RESULTS.GRANTED;
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Permission check error:', error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { createSlice } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
auction: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const auctionSlice = createSlice({
|
||||||
|
name: 'auction',
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
setAuction: (state, { payload }) => {
|
||||||
|
state.auction = payload;
|
||||||
|
},
|
||||||
|
removeAuction: (state) => {
|
||||||
|
state.auction = '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
export const { setAuction , removeAuction} = auctionSlice.actions;
|
||||||
|
export default auctionSlice.reducer;
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { createSlice } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
token: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const tokenSlice = createSlice({
|
||||||
|
name: 'token',
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
setToken: (state, { payload }) => {
|
||||||
|
state.token = payload;
|
||||||
|
},
|
||||||
|
removeToken: (state) => {
|
||||||
|
state.token = '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
export const { setToken , removeToken} = tokenSlice.actions;
|
||||||
|
export default tokenSlice.reducer;
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
|
export interface AuctionItem {
|
||||||
|
auctionNumber: string;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ DEFAULT = EMPTY ARRAY
|
||||||
|
const initialState: AuctionItem[] = [];
|
||||||
|
|
||||||
|
export const selectPurchaseAuctionSlice = createSlice({
|
||||||
|
name: 'selectPurchaseAuction',
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
togglePurchaseAuction: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<AuctionItem>
|
||||||
|
) => {
|
||||||
|
const index = state.findIndex(
|
||||||
|
item => item.auctionNumber === action.payload.auctionNumber
|
||||||
|
);
|
||||||
|
|
||||||
|
if (index !== -1) {
|
||||||
|
state.splice(index, 1);
|
||||||
|
} else {
|
||||||
|
state.push(action.payload);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
clearPurchaseAuction: () => [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const {
|
||||||
|
togglePurchaseAuction,
|
||||||
|
clearPurchaseAuction,
|
||||||
|
} = selectPurchaseAuctionSlice.actions;
|
||||||
|
|
||||||
|
export default selectPurchaseAuctionSlice.reducer;
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import {configureStore} from '@reduxjs/toolkit';
|
||||||
|
import {combineReducers} from 'redux';
|
||||||
|
import {persistStore, persistReducer} from 'redux-persist';
|
||||||
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||||
|
import {tokenSlice} from './Slices/Token';
|
||||||
|
import {auctionSlice} from './Slices/SelectedAuction';
|
||||||
|
import selectPurchaseAuctionReducer from './Slices/selectPurchaseAuction'
|
||||||
|
|
||||||
|
const rootReducer = combineReducers({
|
||||||
|
token: tokenSlice.reducer,
|
||||||
|
auction: auctionSlice.reducer,
|
||||||
|
selectPurchaseAuction: selectPurchaseAuctionReducer,
|
||||||
|
});
|
||||||
|
const persistConfig = {
|
||||||
|
key: 'root',
|
||||||
|
storage: AsyncStorage,
|
||||||
|
whitelist: ['token'],
|
||||||
|
};
|
||||||
|
const persistReducers = persistReducer(persistConfig, rootReducer);
|
||||||
|
export const store = configureStore({
|
||||||
|
reducer: persistReducers,
|
||||||
|
middleware: getDefaultMiddleware =>
|
||||||
|
getDefaultMiddleware({
|
||||||
|
serializableCheck: false,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
export const persistor = persistStore(store);
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
import React, { useState, useRef } from 'react';
|
||||||
|
import {
|
||||||
|
View,
|
||||||
|
Text,
|
||||||
|
StyleSheet,
|
||||||
|
TouchableOpacity,
|
||||||
|
Animated,
|
||||||
|
Dimensions,
|
||||||
|
} from 'react-native';
|
||||||
|
import { InsideHeader } from '../../Component/Index';
|
||||||
|
import {
|
||||||
|
useFetchLiveAuction,
|
||||||
|
useFetchPurchaseAuction,
|
||||||
|
} from '../../Services/BBPS/Hooks';
|
||||||
|
import colors from '../../Constant/Color';
|
||||||
|
import LiveAuction from './LiveAuction';
|
||||||
|
import PurchaseAuction from './PurchaseAuction';
|
||||||
|
import { ShoppingCart } from 'lucide-react-native';
|
||||||
|
import { useNavigation } from '@react-navigation/native';
|
||||||
|
import NavigationString from '../../Constant/NavigationString';
|
||||||
|
import FontsFamily from '../../Constant/FontsFamily';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
const { width } = Dimensions.get('screen')
|
||||||
|
const TAB_WIDTH = 140;
|
||||||
|
const TAB_HEIGHT = 44;
|
||||||
|
|
||||||
|
const Auction = () => {
|
||||||
|
const Navigation = useNavigation() as any
|
||||||
|
const { data: liveAuctionData = {} }: any = useFetchLiveAuction();
|
||||||
|
const { data: purchaseAuctionData = {} } = useFetchPurchaseAuction();
|
||||||
|
const SelectedPurchaseList = useSelector((state: any) => state.selectPurchaseAuction);
|
||||||
|
|
||||||
|
|
||||||
|
const liveCount = Array.isArray(liveAuctionData?.result)
|
||||||
|
? liveAuctionData?.pages[0]?.totalCount
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
const purchaseCount = Array.isArray(purchaseAuctionData?.data)
|
||||||
|
? purchaseAuctionData.data.length
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
const [selectedTab, setSelectedTab] = useState<'live' | 'purchase'>('live');
|
||||||
|
|
||||||
|
const translateX = useRef(new Animated.Value(0)).current;
|
||||||
|
|
||||||
|
const onTabPress = (tab: 'live' | 'purchase', index: number) => {
|
||||||
|
setSelectedTab(tab);
|
||||||
|
Animated.spring(translateX, {
|
||||||
|
toValue: index * TAB_WIDTH,
|
||||||
|
useNativeDriver: true,
|
||||||
|
friction: 8,
|
||||||
|
}).start();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<InsideHeader title="Auction" showArrow />
|
||||||
|
|
||||||
|
{/* Switch Tabs */}
|
||||||
|
<View style={{ flexDirection: 'row', justifyContent: 'space-around', alignItems: 'center' }}>
|
||||||
|
<View style={styles.switchWrapper}>
|
||||||
|
<Animated.View
|
||||||
|
pointerEvents="none"
|
||||||
|
style={[
|
||||||
|
styles.activeTab,
|
||||||
|
{
|
||||||
|
transform: [{ translateX }],
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.tab}
|
||||||
|
activeOpacity={0.8}
|
||||||
|
onPress={() => onTabPress('live', 0)}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
styles.tabText,
|
||||||
|
selectedTab === 'live' && styles.activeText,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
Live ({liveCount})
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.tab}
|
||||||
|
activeOpacity={0.8}
|
||||||
|
onPress={() => onTabPress('purchase', 1)}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
styles.tabText,
|
||||||
|
selectedTab === 'purchase' && styles.activeText,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
Purchased ({purchaseCount})
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
{/* Cart */}
|
||||||
|
<View>
|
||||||
|
{SelectedPurchaseList.length && <View style={{ borderWidth: 2, borderColor: colors.white, backgroundColor: colors.primaryLight, position: 'absolute', borderRadius: 50, zIndex: 20, right: 0, top: -10, paddingHorizontal:2 }}>
|
||||||
|
<Text style={{ fontSize: 12, color: colors.black }}>{SelectedPurchaseList.length}</Text>
|
||||||
|
</View>}
|
||||||
|
<TouchableOpacity style={{ padding: 10, backgroundColor: colors.primaryLight, borderRadius: 20 }} onPress={() => { Navigation.navigate(NavigationString.PurchaseList) }}>
|
||||||
|
<ShoppingCart size={22} color={colors.black} strokeWidth={2} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
{/* Content */}
|
||||||
|
{selectedTab === 'live' ? <LiveAuction /> : <PurchaseAuction />}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Auction;
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: colors.white,
|
||||||
|
},
|
||||||
|
|
||||||
|
/* ---------- Switch UI ---------- */
|
||||||
|
|
||||||
|
switchWrapper: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
marginVertical: 14,
|
||||||
|
backgroundColor: '#f1f5f9',
|
||||||
|
borderRadius: 18,
|
||||||
|
padding: 2,
|
||||||
|
height: TAB_HEIGHT,
|
||||||
|
},
|
||||||
|
|
||||||
|
tab: {
|
||||||
|
width: TAB_WIDTH,
|
||||||
|
height: TAB_HEIGHT - 4,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
zIndex: 2,
|
||||||
|
},
|
||||||
|
|
||||||
|
tabText: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: '#64748b',
|
||||||
|
lineHeight: 18,
|
||||||
|
fontFamily: FontsFamily.poppinsSemiBold
|
||||||
|
},
|
||||||
|
|
||||||
|
activeText: {
|
||||||
|
color: colors.primary,
|
||||||
|
},
|
||||||
|
|
||||||
|
activeTab: {
|
||||||
|
position: 'absolute',
|
||||||
|
top: 2,
|
||||||
|
left: 2,
|
||||||
|
width: TAB_WIDTH,
|
||||||
|
height: TAB_HEIGHT - 4,
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
borderRadius: 16,
|
||||||
|
shadowColor: '#000',
|
||||||
|
shadowOpacity: 0.1,
|
||||||
|
shadowRadius: 6,
|
||||||
|
elevation: 3,
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
import React, { useCallback, useState, useEffect } from 'react';
|
||||||
|
import { View, FlatList, ActivityIndicator, RefreshControl, Text } from 'react-native';
|
||||||
|
import Animated, { FadeInDown, FadeOutUp } from 'react-native-reanimated';
|
||||||
|
import LiveAuctionTile from './LiveAuctionTile';
|
||||||
|
import { useNavigation } from '@react-navigation/native';
|
||||||
|
import { useFetchLiveAuction } from '../../Services/BBPS/Hooks';
|
||||||
|
import { SearchInput } from '../../Component/Serach/SeacrhInput';
|
||||||
|
import { Skelton } from '../../Component/Index';
|
||||||
|
import colors from '../../Constant/Color';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import { selectPurchaseAuctionSlice, togglePurchaseAuction } from '../../Redux/Slices/selectPurchaseAuction';
|
||||||
|
|
||||||
|
const LiveAuction = () => {
|
||||||
|
const navigation: any = useNavigation();
|
||||||
|
const Dispatch = useDispatch()
|
||||||
|
const SelectedPurchaseList = useSelector((state: any) => state.selectPurchaseAuction);
|
||||||
|
const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading, isRefetching, refetch }: any = useFetchLiveAuction();
|
||||||
|
const [showRefreshBanner, setShowRefreshBanner] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isRefetching) {
|
||||||
|
setShowRefreshBanner(true);
|
||||||
|
} else {
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
setShowRefreshBanner(false);
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}
|
||||||
|
}, [isRefetching]);
|
||||||
|
|
||||||
|
const loadMore = useCallback(() => {
|
||||||
|
if (hasNextPage && !isFetchingNextPage) {
|
||||||
|
fetchNextPage();
|
||||||
|
}
|
||||||
|
}, [hasNextPage, isFetchingNextPage]);
|
||||||
|
|
||||||
|
const renderFooter = useCallback(() => {
|
||||||
|
if (!isFetchingNextPage) return null;
|
||||||
|
return (
|
||||||
|
<View style={{ paddingVertical: 12 }}>
|
||||||
|
<ActivityIndicator size="small" color={colors.primary} />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}, [isFetchingNextPage]);
|
||||||
|
|
||||||
|
const renderItem = useCallback(
|
||||||
|
({ item, index }: any) => {
|
||||||
|
const isPurchase = SelectedPurchaseList.findIndex((prev: any) => prev.auctionNumber === item.auctionNumber)
|
||||||
|
return <LiveAuctionTile
|
||||||
|
data={item}
|
||||||
|
index={index}
|
||||||
|
auctionId={item.auctionId}
|
||||||
|
auctionDispatchDate={item.auctionDispatchDate}
|
||||||
|
isPurchased={isPurchase !== -1 ? true : false}
|
||||||
|
purchaseEnd={item.purchaseEnd}
|
||||||
|
auctionOfWork={item.auctionOfWork}
|
||||||
|
auctionFees={item.auctionFees}
|
||||||
|
auctionNumber={item.auctionNumber}
|
||||||
|
onPurchasePress={() => { Dispatch(togglePurchaseAuction(item)) }}
|
||||||
|
onDetailsPress={() => { }}
|
||||||
|
/>
|
||||||
|
},
|
||||||
|
[SelectedPurchaseList]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return <Skelton />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={{ flex: 1 }}>
|
||||||
|
<SearchInput onChange={() => { }} placeholder="Search...." />
|
||||||
|
|
||||||
|
{showRefreshBanner && (
|
||||||
|
<Animated.View
|
||||||
|
entering={FadeInDown.duration(200)}
|
||||||
|
exiting={FadeOutUp.duration(500)}
|
||||||
|
style={{
|
||||||
|
alignSelf: 'center',
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 8,
|
||||||
|
paddingVertical: 8,
|
||||||
|
paddingHorizontal: 14,
|
||||||
|
marginTop: 6,
|
||||||
|
borderRadius: 20,
|
||||||
|
backgroundColor: '#FFFFFF',
|
||||||
|
shadowColor: '#000',
|
||||||
|
shadowOpacity: 0.06,
|
||||||
|
shadowRadius: 8,
|
||||||
|
shadowOffset: { width: 0, height: 3 },
|
||||||
|
elevation: 4,
|
||||||
|
zIndex: 10, // Ensure it stays on top
|
||||||
|
position: 'absolute',
|
||||||
|
top: 60, // Adjust based on your SearchInput height
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ActivityIndicator size="small" color={colors.primary} />
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
fontSize: 13,
|
||||||
|
color: '#374151',
|
||||||
|
fontWeight: '500',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Refreshing auctions
|
||||||
|
</Text>
|
||||||
|
</Animated.View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<FlatList
|
||||||
|
data={data?.result ?? []}
|
||||||
|
keyExtractor={(item) => String(item.auctionId)}
|
||||||
|
renderItem={renderItem}
|
||||||
|
showsVerticalScrollIndicator={false}
|
||||||
|
contentContainerStyle={{ paddingBottom: 50, paddingTop: 10 }}
|
||||||
|
onEndReached={loadMore}
|
||||||
|
onEndReachedThreshold={0.2}
|
||||||
|
ListFooterComponent={renderFooter}
|
||||||
|
refreshControl={
|
||||||
|
<RefreshControl
|
||||||
|
refreshing={isRefetching}
|
||||||
|
onRefresh={refetch}
|
||||||
|
tintColor="transparent" // Set to transparent if you only want your custom banner to show
|
||||||
|
colors={[colors.primary]}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
initialNumToRender={6}
|
||||||
|
maxToRenderPerBatch={6}
|
||||||
|
windowSize={7}
|
||||||
|
removeClippedSubviews
|
||||||
|
updateCellsBatchingPeriod={50}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LiveAuction;
|
||||||
@@ -0,0 +1,242 @@
|
|||||||
|
import React, { memo } from 'react';
|
||||||
|
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
|
||||||
|
import moment from 'moment';
|
||||||
|
import colors from '../../Constant/Color';
|
||||||
|
import { LiveAuctionTileProps } from './types/types';
|
||||||
|
import AuctionDetail from './Modal/AuctionDetail';
|
||||||
|
import { Calendar, Tag, Info, ArrowRight } from 'lucide-react-native';
|
||||||
|
|
||||||
|
const LiveAuctionTile: React.FC<LiveAuctionTileProps> = ({
|
||||||
|
data,
|
||||||
|
isPurchased = false,
|
||||||
|
onPurchasePress,
|
||||||
|
auctionDispatchDate,
|
||||||
|
auctionOfWork,
|
||||||
|
purchaseEnd,
|
||||||
|
auctionFees,
|
||||||
|
auctionNumber,
|
||||||
|
}) => {
|
||||||
|
const [showDetail, setShowDetail] = React.useState(false);
|
||||||
|
const purchaseEnded = moment().isAfter(moment(purchaseEnd, 'DD-MM-YYYY HH:mm:ss'));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.cardWrapper}>
|
||||||
|
{/* Status Tag */}
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
styles.statusTag,
|
||||||
|
{ backgroundColor: isPurchased ? colors.error : purchaseEnded ? '#94a3b8' : '#4ade80' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.container}
|
||||||
|
activeOpacity={0.9}
|
||||||
|
onPress={() => setShowDetail(true)}
|
||||||
|
>
|
||||||
|
{/* Header */}
|
||||||
|
<View style={styles.header}>
|
||||||
|
<View style={styles.badge}>
|
||||||
|
<Tag size={14} color={colors.primary} />
|
||||||
|
<Text style={styles.badgeText}>#{auctionNumber}</Text>
|
||||||
|
</View>
|
||||||
|
<Text style={styles.priceText}>₹{auctionFees}</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Work Title */}
|
||||||
|
<Text style={styles.workTitle} numberOfLines={2}>
|
||||||
|
{auctionOfWork ?? 'General Auction Work'}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
{/* Info Grid */}
|
||||||
|
<View style={styles.infoGrid}>
|
||||||
|
<View style={styles.infoItem}>
|
||||||
|
<Calendar size={16} color="#64748b" />
|
||||||
|
<View style={styles.infoTextGroup}>
|
||||||
|
<Text style={styles.infoLabel}>Start</Text>
|
||||||
|
<Text style={styles.infoValue}>{auctionDispatchDate ?? '-'}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.infoItem}>
|
||||||
|
<Calendar size={16} color={purchaseEnded ? "#ef4444" : "#22c55e"} />
|
||||||
|
<View style={styles.infoTextGroup}>
|
||||||
|
<Text style={styles.infoLabel}>Deadline</Text>
|
||||||
|
<Text style={[styles.infoValue, purchaseEnded && { color: '#ef4444' }]}>
|
||||||
|
{moment(purchaseEnd, 'DD-MM-YYYY HH:mm:ss').format('DD MMM, YY')}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Action Bar */}
|
||||||
|
<View style={styles.actionBar}>
|
||||||
|
<TouchableOpacity style={styles.iconBtn} onPress={() => setShowDetail(true)}>
|
||||||
|
<Info size={18} color={colors.primary} />
|
||||||
|
<Text style={styles.iconBtnText}>Details</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[
|
||||||
|
styles.mainButton,
|
||||||
|
purchaseEnded
|
||||||
|
? styles.btnDisabled
|
||||||
|
: isPurchased
|
||||||
|
? styles.btnPurchased
|
||||||
|
: styles.btnActive,
|
||||||
|
]}
|
||||||
|
onPress={onPurchasePress}
|
||||||
|
disabled={purchaseEnded}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
styles.mainButtonText,
|
||||||
|
isPurchased && !purchaseEnded ? { color: colors.white } : { color: '#fff' },
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{purchaseEnded ? 'Expired' : isPurchased ? 'Remove' : 'Add to Cart'}
|
||||||
|
</Text>
|
||||||
|
{!purchaseEnded && (
|
||||||
|
<ArrowRight size={16} color={isPurchased ? colors.white : '#fff'} />
|
||||||
|
)}
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Auction Detail Modal */}
|
||||||
|
<AuctionDetail visible={showDetail} data={data} onClose={() => setShowDetail(false)} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(LiveAuctionTile);
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
cardWrapper: {
|
||||||
|
marginVertical: 10,
|
||||||
|
marginHorizontal: 16,
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
borderRadius: 24,
|
||||||
|
flexDirection: 'row',
|
||||||
|
overflow: 'hidden',
|
||||||
|
|
||||||
|
// Modern shadow
|
||||||
|
shadowColor: '#000',
|
||||||
|
shadowOffset: { width: 0, height: 10 },
|
||||||
|
shadowOpacity: 0.08,
|
||||||
|
shadowRadius: 20,
|
||||||
|
elevation: 8,
|
||||||
|
},
|
||||||
|
statusTag: {
|
||||||
|
width: 6,
|
||||||
|
height: '100%',
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
padding: 20,
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginBottom: 12,
|
||||||
|
},
|
||||||
|
badge: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: colors.primary + '25',
|
||||||
|
paddingHorizontal: 12,
|
||||||
|
paddingVertical: 5,
|
||||||
|
borderRadius: 12,
|
||||||
|
gap: 6,
|
||||||
|
},
|
||||||
|
badgeText: {
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: '700',
|
||||||
|
color: colors.primary,
|
||||||
|
},
|
||||||
|
priceText: {
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: '800',
|
||||||
|
color: '#0f172a',
|
||||||
|
},
|
||||||
|
workTitle: {
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: '600',
|
||||||
|
color: '#334155',
|
||||||
|
lineHeight: 24,
|
||||||
|
marginBottom: 16,
|
||||||
|
},
|
||||||
|
infoGrid: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
backgroundColor: '#f0f4f8',
|
||||||
|
paddingVertical: 12,
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
borderRadius: 16,
|
||||||
|
marginBottom: 18,
|
||||||
|
},
|
||||||
|
infoItem: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 10,
|
||||||
|
},
|
||||||
|
infoTextGroup: {
|
||||||
|
flexDirection: 'column',
|
||||||
|
},
|
||||||
|
infoLabel: {
|
||||||
|
fontSize: 10,
|
||||||
|
color: '#94a3b8',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
fontWeight: '700',
|
||||||
|
},
|
||||||
|
infoValue: {
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: '700',
|
||||||
|
color: '#1e293b',
|
||||||
|
},
|
||||||
|
actionBar: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 12,
|
||||||
|
},
|
||||||
|
iconBtn: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 6,
|
||||||
|
paddingHorizontal: 10,
|
||||||
|
paddingVertical: 6,
|
||||||
|
borderRadius: 10,
|
||||||
|
backgroundColor: '#e2e8f0',
|
||||||
|
},
|
||||||
|
iconBtnText: {
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: '700',
|
||||||
|
color: colors.primary,
|
||||||
|
},
|
||||||
|
mainButton: {
|
||||||
|
flex: 1,
|
||||||
|
height: 46,
|
||||||
|
borderRadius: 16,
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
gap: 8,
|
||||||
|
},
|
||||||
|
btnActive: {
|
||||||
|
backgroundColor: '#4ade80', // light green
|
||||||
|
},
|
||||||
|
btnPurchased: {
|
||||||
|
backgroundColor: colors.error,
|
||||||
|
borderWidth: 2,
|
||||||
|
borderColor: colors.error,
|
||||||
|
},
|
||||||
|
btnDisabled: {
|
||||||
|
backgroundColor: '#e2e8f0',
|
||||||
|
},
|
||||||
|
mainButtonText: {
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: '800',
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,345 @@
|
|||||||
|
import React, { memo, useState } from 'react';
|
||||||
|
import {
|
||||||
|
View,
|
||||||
|
Text,
|
||||||
|
StyleSheet,
|
||||||
|
TouchableOpacity,
|
||||||
|
ScrollView,
|
||||||
|
SafeAreaView,
|
||||||
|
Platform,
|
||||||
|
StatusBar,
|
||||||
|
} from 'react-native';
|
||||||
|
import Modal from 'react-native-modal';
|
||||||
|
import moment from 'moment';
|
||||||
|
import {
|
||||||
|
X,
|
||||||
|
Hash,
|
||||||
|
Briefcase,
|
||||||
|
Calendar,
|
||||||
|
CreditCard,
|
||||||
|
FileText,
|
||||||
|
Download,
|
||||||
|
Eye,
|
||||||
|
Layers,
|
||||||
|
ArrowLeft,
|
||||||
|
Info,
|
||||||
|
Clock
|
||||||
|
} from 'lucide-react-native';
|
||||||
|
import colors from '../../../Constant/Color';
|
||||||
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
|
import AuctionItemDetail from './AuctionItemDetail';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
visible: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
data?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AuctionFullDetail: React.FC<Props> = ({ visible, onClose, data }) => {
|
||||||
|
const inset = useSafeAreaInsets()
|
||||||
|
const [allBoolean, setAllBoolean] = useState({ showItemDetail: false })
|
||||||
|
const formatDate = (date?: string) => {
|
||||||
|
if (!date) return '-';
|
||||||
|
return moment(date, 'DD-MM-YYYY HH:mm:ss').format('DD MMM YYYY, hh:mm A');
|
||||||
|
};
|
||||||
|
|
||||||
|
const isLive = data?.purchaseEnd
|
||||||
|
? moment().isBefore(moment(data.purchaseEnd, 'DD-MM-YYYY HH:mm:ss'))
|
||||||
|
: false;
|
||||||
|
|
||||||
|
// Internal Components
|
||||||
|
const Section = ({ title, icon: Icon, children }: any) => (
|
||||||
|
<View style={styles.sectionContainer}>
|
||||||
|
<View style={styles.sectionHeader}>
|
||||||
|
<Icon size={20} color={colors.primary} />
|
||||||
|
<Text style={styles.sectionTitle}>{title}</Text>
|
||||||
|
</View>
|
||||||
|
{children}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
|
const InfoBox = ({ label, value, full = false }: any) => (
|
||||||
|
<View style={[styles.infoBox, full ? { width: '100%' } : { width: '48%' }]}>
|
||||||
|
<Text style={styles.label}>{label}</Text>
|
||||||
|
<Text style={styles.value}>{value || '-'}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isVisible={visible}
|
||||||
|
style={styles.fullModal}
|
||||||
|
animationIn="slideInRight"
|
||||||
|
animationOut="slideOutRight"
|
||||||
|
useNativeDriver
|
||||||
|
hideModalContentWhileAnimating
|
||||||
|
statusBarTranslucent
|
||||||
|
>
|
||||||
|
<SafeAreaView style={styles.safeArea}>
|
||||||
|
<StatusBar barStyle="dark-content" />
|
||||||
|
|
||||||
|
{/* Full Header */}
|
||||||
|
<View style={[styles.header, { paddingTop: inset.top }]}>
|
||||||
|
<TouchableOpacity onPress={onClose} style={styles.backBtn}>
|
||||||
|
<ArrowLeft size={24} color="#1e293b" />
|
||||||
|
</TouchableOpacity>
|
||||||
|
<View style={styles.headerTitleContainer}>
|
||||||
|
<Text style={styles.headerTitleText}>Auction Overview</Text>
|
||||||
|
<Text style={styles.headerIdText}>Ref: {data?.auctionNumber || 'N/A'}</Text>
|
||||||
|
</View>
|
||||||
|
<View style={styles.liveIndicator}>
|
||||||
|
<View style={[styles.dot, { backgroundColor: isLive ? '#22c55e' : '#94a3b8' }]} />
|
||||||
|
<Text style={styles.liveText}>{isLive ? 'LIVE' : 'CLOSED'}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Scrollable Body */}
|
||||||
|
<ScrollView
|
||||||
|
style={styles.scrollView}
|
||||||
|
contentContainerStyle={styles.scrollContent}
|
||||||
|
showsVerticalScrollIndicator={true}
|
||||||
|
>
|
||||||
|
{/* 1. Basic Info Grid */}
|
||||||
|
<Section title="Details" icon={Layers}>
|
||||||
|
<View style={styles.grid}>
|
||||||
|
<InfoBox label="Auction No." value={data?.auctionNumber} />
|
||||||
|
<InfoBox label="Pattern" value={data?.auctionPattern || 'Forward'} />
|
||||||
|
<InfoBox label="Mode" value={data?.auctionMode || 'Open'} />
|
||||||
|
<InfoBox label="Category" value={data?.categoryOfAuction || 'General'} />
|
||||||
|
<InfoBox label="EMD Type" value={data?.emd || 'Online'} />
|
||||||
|
<InfoBox label="EMD Figure" value={data?.emdFigure || 'Item Wise'} />
|
||||||
|
</View>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
{/* 2. Work Description */}
|
||||||
|
<Section title="Auction Details" icon={Briefcase}>
|
||||||
|
<View style={styles.grid}>
|
||||||
|
<InfoBox label="Auction Name" value={'Auction of cars'} />
|
||||||
|
<InfoBox label="Category Of Auction " value={'Live'} />
|
||||||
|
<InfoBox label="Auto Time Increment " value={'yes'} />
|
||||||
|
<InfoBox label="Auction Fees (In Figure) " value={'500'} />
|
||||||
|
<InfoBox label="Auction Fees (In Words)" value={'Five Hundred'} />
|
||||||
|
<InfoBox label="Auction Keyword" value={'Dev Furniture, Furnture'} />
|
||||||
|
</View>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
{/* 3. Timeline Section */}
|
||||||
|
<Section title="Key Dates" icon={Calendar}>
|
||||||
|
<View style={styles.timelineContainer}>
|
||||||
|
<View style={styles.timelineRow}>
|
||||||
|
<View style={styles.iconCircle}><Clock size={16} color="#64748b" /></View>
|
||||||
|
<View>
|
||||||
|
<Text style={styles.label}>Auction Purchase (Start)</Text>
|
||||||
|
<Text style={styles.value}>{formatDate(data?.auctionDispatchDate)}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View style={styles.timelineConnector} />
|
||||||
|
<View style={styles.timelineRow}>
|
||||||
|
<View style={[styles.iconCircle, { backgroundColor: '#fee2e2' }]}><Clock size={16} color="#ef4444" /></View>
|
||||||
|
<View>
|
||||||
|
<Text style={styles.label}>Purchase Deadline (End)</Text>
|
||||||
|
<Text style={styles.value}>{formatDate(data?.purchaseEnd)}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
{/* 4. Item Table Section */}
|
||||||
|
<Section title="Auction Items" icon={FileText}>
|
||||||
|
<View style={styles.tableWrapper}>
|
||||||
|
<View style={styles.tableHead}>
|
||||||
|
<Text style={[styles.tableLabel, { flex: 2 }]}>Item</Text>
|
||||||
|
<Text style={styles.tableLabel}>Qty</Text>
|
||||||
|
<Text style={[styles.tableLabel, { textAlign: 'right' }]}>View</Text>
|
||||||
|
</View>
|
||||||
|
{/* Dummy Item Row */}
|
||||||
|
<View style={styles.tableRow}>
|
||||||
|
<View style={{ flex: 2 }}>
|
||||||
|
<Text style={styles.itemMain}>Sick Sari / Laundry</Text>
|
||||||
|
<Text style={styles.itemSub}>Category: Sari</Text>
|
||||||
|
</View>
|
||||||
|
<Text style={styles.itemQty}>120,000</Text>
|
||||||
|
<TouchableOpacity style={styles.actionIcon} onPress={() => { setAllBoolean((prev: any) => ({ ...prev, showItemDetail: true })) }}>
|
||||||
|
<Eye size={18} color={colors.primary} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
{/* 5. Downloads */}
|
||||||
|
<Section title="Downloads" icon={Download}>
|
||||||
|
<TouchableOpacity style={styles.fileCard}>
|
||||||
|
<View style={styles.fileIcon}><FileText size={20} color="#ef4444" /></View>
|
||||||
|
<View style={{ flex: 1 }}>
|
||||||
|
<Text style={styles.fileName}>Auction_Notice_Main.pdf</Text>
|
||||||
|
<Text style={styles.fileSize}>1.2 MB • PDF</Text>
|
||||||
|
</View>
|
||||||
|
<Download size={20} color="#94a3b8" />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
{/* Sticky Footer */}
|
||||||
|
<View style={styles.footer}>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[styles.mainBtn, !isLive && styles.btnDisabled]}
|
||||||
|
disabled={!isLive}
|
||||||
|
>
|
||||||
|
<Text style={styles.mainBtnText}>
|
||||||
|
{isLive ? 'Add to Cart / Purchase' : 'Auction Closed'}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
<AuctionItemDetail data={{
|
||||||
|
productName: "sick sari",
|
||||||
|
timeDuration: "112 YEAR",
|
||||||
|
category: "laundry",
|
||||||
|
subCategory: "sari",
|
||||||
|
reservePriceFig: "120000",
|
||||||
|
reservePriceWords: "One Lakh Twenty Thousand",
|
||||||
|
auctionStartFig: "120000",
|
||||||
|
auctionStartWords: "One Lakh Twenty Thousand",
|
||||||
|
bidVariationFig: "200",
|
||||||
|
bidVariationWords: "Two Hundred",
|
||||||
|
emdFig: "2000",
|
||||||
|
emdWords: "Two Thousand",
|
||||||
|
description: "Auction of cars",
|
||||||
|
documentName: "BBPS screens.pdf"
|
||||||
|
}} onClose={() => { setAllBoolean((prev: any) => ({ ...prev, showItemDetail: false })) }} visible={allBoolean.showItemDetail} />
|
||||||
|
</SafeAreaView>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
fullModal: {
|
||||||
|
margin: 0,
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
},
|
||||||
|
safeArea: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
padding: 16,
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: '#f1f5f9',
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
},
|
||||||
|
backBtn: {
|
||||||
|
padding: 8,
|
||||||
|
marginRight: 8,
|
||||||
|
},
|
||||||
|
headerTitleContainer: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
headerTitleText: {
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: '800',
|
||||||
|
color: '#0f172a',
|
||||||
|
},
|
||||||
|
headerIdText: {
|
||||||
|
fontSize: 13,
|
||||||
|
color: '#64748b',
|
||||||
|
},
|
||||||
|
liveIndicator: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: '#f8fafc',
|
||||||
|
paddingHorizontal: 10,
|
||||||
|
paddingVertical: 5,
|
||||||
|
borderRadius: 20,
|
||||||
|
gap: 6,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: '#e2e8f0',
|
||||||
|
},
|
||||||
|
dot: { width: 8, height: 8, borderRadius: 4 },
|
||||||
|
liveText: { fontSize: 11, fontWeight: '800', color: '#475569' },
|
||||||
|
|
||||||
|
scrollView: { flex: 1, backgroundColor: '#f8fafc' },
|
||||||
|
scrollContent: { padding: 16, paddingBottom: 100 },
|
||||||
|
|
||||||
|
sectionContainer: {
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
borderRadius: 16,
|
||||||
|
padding: 16,
|
||||||
|
marginBottom: 16,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: '#eef2f6',
|
||||||
|
},
|
||||||
|
sectionHeader: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 10,
|
||||||
|
marginBottom: 20,
|
||||||
|
paddingBottom: 10,
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: '#f8fafc',
|
||||||
|
},
|
||||||
|
sectionTitle: {
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: '800',
|
||||||
|
color: '#1e293b',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
letterSpacing: 0.5,
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
},
|
||||||
|
infoBox: { marginBottom: 16 },
|
||||||
|
label: { fontSize: 12, color: '#94a3b8', fontWeight: '600', marginBottom: 4 },
|
||||||
|
value: { fontSize: 15, fontWeight: '700', color: '#334155' },
|
||||||
|
spacer: { height: 12 },
|
||||||
|
|
||||||
|
timelineContainer: { paddingLeft: 8 },
|
||||||
|
timelineRow: { flexDirection: 'row', alignItems: 'center', gap: 15 },
|
||||||
|
iconCircle: { width: 34, height: 34, borderRadius: 10, backgroundColor: '#f1f5f9', alignItems: 'center', justifyContent: 'center' },
|
||||||
|
timelineConnector: { width: 2, height: 20, backgroundColor: '#f1f5f9', marginLeft: 16 },
|
||||||
|
|
||||||
|
tableWrapper: { backgroundColor: '#f8fafc', borderRadius: 12, padding: 12 },
|
||||||
|
tableHead: { flexDirection: 'row', borderBottomWidth: 1, borderBottomColor: '#e2e8f0', paddingBottom: 8, marginBottom: 12 },
|
||||||
|
tableLabel: { fontSize: 11, fontWeight: '800', color: '#94a3b8', flex: 1 },
|
||||||
|
tableRow: { flexDirection: 'row', alignItems: 'center' },
|
||||||
|
itemMain: { fontSize: 14, fontWeight: '700', color: '#1e293b' },
|
||||||
|
itemSub: { fontSize: 12, color: '#64748b' },
|
||||||
|
itemQty: { flex: 1, fontSize: 14, fontWeight: '700', color: '#334155' },
|
||||||
|
actionIcon: { padding: 6 },
|
||||||
|
|
||||||
|
fileCard: { flexDirection: 'row', alignItems: 'center', gap: 12, backgroundColor: '#f8fafc', padding: 12, borderRadius: 12 },
|
||||||
|
fileIcon: { width: 40, height: 40, borderRadius: 8, backgroundColor: '#fee2e2', alignItems: 'center', justifyContent: 'center' },
|
||||||
|
fileName: { fontSize: 14, fontWeight: '700', color: '#334155' },
|
||||||
|
fileSize: { fontSize: 12, color: '#94a3b8' },
|
||||||
|
|
||||||
|
footer: {
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
padding: 20,
|
||||||
|
paddingBottom: Platform.OS === 'ios' ? 34 : 20,
|
||||||
|
borderTopWidth: 1,
|
||||||
|
borderTopColor: '#f1f5f9',
|
||||||
|
},
|
||||||
|
mainBtn: {
|
||||||
|
backgroundColor: colors.primary,
|
||||||
|
height: 56,
|
||||||
|
borderRadius: 16,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
shadowColor: colors.primary,
|
||||||
|
shadowOffset: { width: 0, height: 4 },
|
||||||
|
shadowOpacity: 0.2,
|
||||||
|
shadowRadius: 8,
|
||||||
|
elevation: 4,
|
||||||
|
},
|
||||||
|
btnDisabled: { backgroundColor: '#cbd5e1' },
|
||||||
|
mainBtnText: { color: '#fff', fontSize: 16, fontWeight: '800' },
|
||||||
|
});
|
||||||
|
|
||||||
|
export default memo(AuctionFullDetail);
|
||||||
@@ -0,0 +1,274 @@
|
|||||||
|
import React, { memo } from 'react';
|
||||||
|
import {
|
||||||
|
View,
|
||||||
|
Text,
|
||||||
|
StyleSheet,
|
||||||
|
TouchableOpacity,
|
||||||
|
ScrollView,
|
||||||
|
SafeAreaView,
|
||||||
|
Platform,
|
||||||
|
StatusBar,
|
||||||
|
Dimensions
|
||||||
|
} from 'react-native';
|
||||||
|
import Modal from 'react-native-modal';
|
||||||
|
import {
|
||||||
|
X,
|
||||||
|
Package,
|
||||||
|
Clock,
|
||||||
|
Tag,
|
||||||
|
IndianRupee,
|
||||||
|
FileText,
|
||||||
|
Download,
|
||||||
|
ArrowLeft,
|
||||||
|
Info,
|
||||||
|
Layers,
|
||||||
|
ChevronRight
|
||||||
|
} from 'lucide-react-native';
|
||||||
|
import colors from '../../../Constant/Color';
|
||||||
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
|
|
||||||
|
const { width } = Dimensions.get('window');
|
||||||
|
|
||||||
|
// --- Exact Dummy Data Mapping ---
|
||||||
|
const ITEM_DUMMY_DATA = {
|
||||||
|
productName: "sick sari",
|
||||||
|
timeDuration: "112 YEAR",
|
||||||
|
category: "laundry",
|
||||||
|
subCategory: "sari",
|
||||||
|
reservePriceFig: "120000",
|
||||||
|
reservePriceWords: "One Lakh Twenty Thousand",
|
||||||
|
auctionStartFig: "120000",
|
||||||
|
auctionStartWords: "One Lakh Twenty Thousand",
|
||||||
|
bidVariationFig: "200",
|
||||||
|
bidVariationWords: "Two Hundred",
|
||||||
|
emdFig: "2000",
|
||||||
|
emdWords: "Two Thousand",
|
||||||
|
description: "Auction of cars",
|
||||||
|
documentName: "BBPS screens.pdf"
|
||||||
|
};
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
visible: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
data?: typeof ITEM_DUMMY_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AuctionItemDetail: React.FC<Props> = ({ visible, onClose, data = ITEM_DUMMY_DATA }) => {
|
||||||
|
const inset = useSafeAreaInsets()
|
||||||
|
const Section = ({ title, icon: Icon, children }: any) => (
|
||||||
|
<View style={styles.sectionCard}>
|
||||||
|
<View style={styles.sectionHeader}>
|
||||||
|
<View style={styles.iconCircle}>
|
||||||
|
<Icon size={18} color={colors.primary} strokeWidth={2.5} />
|
||||||
|
</View>
|
||||||
|
<Text style={styles.sectionTitle}>{title}</Text>
|
||||||
|
</View>
|
||||||
|
{children}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
|
const PriceTile = ({ label, fig, words, full = false }: any) => (
|
||||||
|
<View style={[styles.priceTile, full ? { width: '100%' } : { width: '48%' }]}>
|
||||||
|
<Text style={styles.label}>{label}</Text>
|
||||||
|
<Text style={styles.priceFig}>₹{Number(fig).toLocaleString('en-IN')}</Text>
|
||||||
|
<Text style={styles.priceWords}>{words}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isVisible={visible}
|
||||||
|
style={styles.fullModal}
|
||||||
|
animationIn="slideInUp"
|
||||||
|
animationOut="slideOutDown"
|
||||||
|
backdropOpacity={1}
|
||||||
|
useNativeDriver
|
||||||
|
statusBarTranslucent
|
||||||
|
>
|
||||||
|
<SafeAreaView style={styles.safeArea}>
|
||||||
|
|
||||||
|
{/* Navbar */}
|
||||||
|
<View style={[styles.header, { paddingTop: inset.top }]}>
|
||||||
|
<TouchableOpacity onPress={onClose} style={styles.backBtn}>
|
||||||
|
<ArrowLeft size={24} color="#1e293b" />
|
||||||
|
</TouchableOpacity>
|
||||||
|
<View>
|
||||||
|
<Text style={styles.headerTitle}>Item Information</Text>
|
||||||
|
<Text style={styles.headerSub}>Detail View</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
style={styles.scrollView}
|
||||||
|
contentContainerStyle={styles.scrollContent}
|
||||||
|
showsVerticalScrollIndicator={false}
|
||||||
|
>
|
||||||
|
{/* Product Identity Card */}
|
||||||
|
<Section title="Product Specification" icon={Package}>
|
||||||
|
<View style={styles.identityHeader}>
|
||||||
|
<Text style={styles.productNameText}>{data.productName.toUpperCase()}</Text>
|
||||||
|
<View style={styles.categoryBadge}>
|
||||||
|
<Text style={styles.categoryText}>{data.category}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.specGrid}>
|
||||||
|
<View style={styles.specItem}>
|
||||||
|
<Text style={styles.label}>Sub Category</Text>
|
||||||
|
<Text style={styles.specValue}>{data.subCategory}</Text>
|
||||||
|
</View>
|
||||||
|
<View style={styles.specItem}>
|
||||||
|
<Text style={styles.label}>Duration</Text>
|
||||||
|
<Text style={styles.specValue}>{data.timeDuration}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
{/* Pricing & Financials */}
|
||||||
|
<Section title="Reserve & Bid Values" icon={IndianRupee}>
|
||||||
|
<PriceTile
|
||||||
|
label="Reserve Price"
|
||||||
|
fig={data.reservePriceFig}
|
||||||
|
words={data.reservePriceWords}
|
||||||
|
full
|
||||||
|
/>
|
||||||
|
<View style={styles.hDivider} />
|
||||||
|
<PriceTile
|
||||||
|
label="Auction Start Value"
|
||||||
|
fig={data.auctionStartFig}
|
||||||
|
words={data.auctionStartWords}
|
||||||
|
full
|
||||||
|
/>
|
||||||
|
<View style={styles.hDivider} />
|
||||||
|
<View style={styles.row}>
|
||||||
|
<PriceTile label="Bid Variation" fig={data.bidVariationFig} words={data.bidVariationWords} />
|
||||||
|
<PriceTile label="EMD Amount" fig={data.emdFig} words={data.emdWords} />
|
||||||
|
</View>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
{/* Description Card */}
|
||||||
|
<Section title="Brief Description" icon={Info}>
|
||||||
|
<Text style={styles.descContent}>{data.description}</Text>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
{/* Documents Card */}
|
||||||
|
<Section title="View Documents" icon={FileText}>
|
||||||
|
<View style={styles.docTableHead}>
|
||||||
|
<Text style={styles.tableLabel}>Sr No.</Text>
|
||||||
|
<Text style={[styles.tableLabel, { flex: 2 }]}>Document Name</Text>
|
||||||
|
<Text style={styles.tableLabel}>Action</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<TouchableOpacity style={styles.docRow} activeOpacity={0.7}>
|
||||||
|
<Text style={styles.docIndex}>1</Text>
|
||||||
|
<View style={{ flex: 2 }}>
|
||||||
|
<Text style={styles.docTitle} numberOfLines={1}>{data.documentName}</Text>
|
||||||
|
</View>
|
||||||
|
<View style={styles.downloadIconBox}>
|
||||||
|
<Download size={18} color={colors.primary} />
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
</SafeAreaView>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
fullModal: { margin: 0, backgroundColor: '#f8fafc' },
|
||||||
|
safeArea: { flex: 1 },
|
||||||
|
header: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
padding: 16,
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: '#f1f5f9',
|
||||||
|
},
|
||||||
|
backBtn: { padding: 8, marginRight: 8 },
|
||||||
|
headerTitle: { fontSize: 18, fontWeight: '800', color: '#0f172a' },
|
||||||
|
headerSub: { fontSize: 12, color: '#94a3b8', fontWeight: '600', textTransform: 'uppercase' },
|
||||||
|
|
||||||
|
scrollView: { flex: 1 },
|
||||||
|
scrollContent: { padding: 16, paddingBottom: 50 },
|
||||||
|
|
||||||
|
sectionCard: {
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
borderRadius: 24,
|
||||||
|
padding: 20,
|
||||||
|
marginBottom: 16,
|
||||||
|
...Platform.select({
|
||||||
|
ios: { shadowColor: '#000', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.05, shadowRadius: 10 },
|
||||||
|
android: { elevation: 3 }
|
||||||
|
})
|
||||||
|
},
|
||||||
|
sectionHeader: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 12,
|
||||||
|
marginBottom: 20,
|
||||||
|
},
|
||||||
|
iconCircle: {
|
||||||
|
width: 36,
|
||||||
|
height: 36,
|
||||||
|
borderRadius: 12,
|
||||||
|
backgroundColor: colors.primary + '10',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
sectionTitle: {
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: '800',
|
||||||
|
color: '#334155',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
letterSpacing: 0.5,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Product Identity
|
||||||
|
identityHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 15 },
|
||||||
|
productNameText: { fontSize: 20, fontWeight: '900', color: '#1e293b', flex: 1 },
|
||||||
|
categoryBadge: { backgroundColor: '#f1f5f9', paddingHorizontal: 12, paddingVertical: 6, borderRadius: 20 },
|
||||||
|
categoryText: { fontSize: 11, fontWeight: '800', color: '#64748b', textTransform: 'uppercase' },
|
||||||
|
specGrid: { flexDirection: 'row', gap: 24, marginTop: 5 },
|
||||||
|
specItem: { flex: 1 },
|
||||||
|
specValue: { fontSize: 15, fontWeight: '700', color: '#334155', marginTop: 2 },
|
||||||
|
|
||||||
|
// Pricing
|
||||||
|
priceTile: { marginBottom: 16 },
|
||||||
|
label: { fontSize: 11, color: '#94a3b8', fontWeight: '700', textTransform: 'uppercase', marginBottom: 4 },
|
||||||
|
priceFig: { fontSize: 22, fontWeight: '900', color: colors.black },
|
||||||
|
priceWords: { fontSize: 13, color: '#64748b', fontStyle: 'italic', marginTop: 2 },
|
||||||
|
hDivider: { height: 1, backgroundColor: '#f1f5f9', marginBottom: 16 },
|
||||||
|
row: { flexDirection: 'row', justifyContent: 'space-between' },
|
||||||
|
|
||||||
|
// Description
|
||||||
|
descContent: { fontSize: 15, color: '#475569', lineHeight: 22, fontWeight: '500' },
|
||||||
|
|
||||||
|
// Table
|
||||||
|
docTableHead: { flexDirection: 'row', paddingBottom: 10, borderBottomWidth: 1, borderBottomColor: '#f1f5f9', marginBottom: 10 },
|
||||||
|
tableLabel: { fontSize: 11, fontWeight: '800', color: '#94a3b8', flex: 1 },
|
||||||
|
docRow: { flexDirection: 'row', alignItems: 'center', backgroundColor: '#f8fafc', padding: 12, borderRadius: 12 },
|
||||||
|
docIndex: { flex: 1, fontSize: 14, fontWeight: '700', color: '#64748b' },
|
||||||
|
docTitle: { fontSize: 14, fontWeight: '700', color: '#1e293b' },
|
||||||
|
downloadIconBox: { backgroundColor: '#fff', padding: 8, borderRadius: 10, borderWidth: 1, borderColor: '#e2e8f0' },
|
||||||
|
|
||||||
|
// Footer
|
||||||
|
footer: {
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 0, width: '100%',
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
padding: 20,
|
||||||
|
paddingBottom: Platform.OS === 'ios' ? 34 : 20,
|
||||||
|
borderTopWidth: 1, borderTopColor: '#f1f5f9'
|
||||||
|
},
|
||||||
|
primaryBtn: {
|
||||||
|
backgroundColor: colors.primary,
|
||||||
|
height: 56, borderRadius: 18,
|
||||||
|
flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 10
|
||||||
|
},
|
||||||
|
primaryBtnText: { color: '#fff', fontSize: 16, fontWeight: '800' }
|
||||||
|
});
|
||||||
|
|
||||||
|
export default memo(AuctionItemDetail);
|
||||||
@@ -0,0 +1,259 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import {
|
||||||
|
Modal,
|
||||||
|
View,
|
||||||
|
Text,
|
||||||
|
StyleSheet,
|
||||||
|
TouchableOpacity,
|
||||||
|
Pressable,
|
||||||
|
} from 'react-native';
|
||||||
|
import DatePicker from 'react-native-date-picker';
|
||||||
|
import moment from 'moment';
|
||||||
|
import colors from '../../../Constant/Color';
|
||||||
|
import FontsFamily from '../../../Constant/FontsFamily';
|
||||||
|
|
||||||
|
type AuctionStatus = 'RUNNING' | 'ENDED' | 'NOT_STARTED' | null;
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
visible: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
onApply: (filters: {
|
||||||
|
fromDate: Date | null;
|
||||||
|
toDate: Date | null;
|
||||||
|
status: AuctionStatus;
|
||||||
|
}) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FilterModal: React.FC<Props> = ({ visible, onClose, onApply }) => {
|
||||||
|
const [fromDate, setFromDate] = useState<Date | null>(null);
|
||||||
|
const [toDate, setToDate] = useState<Date | null>(null);
|
||||||
|
const [status, setStatus] = useState<AuctionStatus>(null);
|
||||||
|
|
||||||
|
const [openPicker, setOpenPicker] = useState(false);
|
||||||
|
const [pickerType, setPickerType] = useState<'FROM' | 'TO'>('FROM');
|
||||||
|
|
||||||
|
const openDatePicker = (type: 'FROM' | 'TO') => {
|
||||||
|
setPickerType(type);
|
||||||
|
setOpenPicker(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleConfirm = (date: Date) => {
|
||||||
|
if (pickerType === 'FROM') {
|
||||||
|
setFromDate(date);
|
||||||
|
} else {
|
||||||
|
setToDate(date);
|
||||||
|
}
|
||||||
|
setOpenPicker(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleApply = () => {
|
||||||
|
onApply({ fromDate, toDate, status });
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal
|
||||||
|
visible={visible}
|
||||||
|
animationType="slide"
|
||||||
|
transparent
|
||||||
|
statusBarTranslucent
|
||||||
|
onRequestClose={onClose}
|
||||||
|
>
|
||||||
|
{/* Overlay */}
|
||||||
|
<Pressable style={styles.overlay} onPress={onClose} />
|
||||||
|
|
||||||
|
{/* Content */}
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Text style={styles.title}>Filter Auctions</Text>
|
||||||
|
|
||||||
|
{/* ===== DATE RANGE ===== */}
|
||||||
|
<Text style={styles.sectionTitle}>Date Range</Text>
|
||||||
|
|
||||||
|
<View style={styles.dateRow}>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.dateBox}
|
||||||
|
onPress={() => openDatePicker('FROM')}
|
||||||
|
>
|
||||||
|
<Text style={styles.dateLabel}>From</Text>
|
||||||
|
<Text style={styles.dateValue}>
|
||||||
|
{fromDate
|
||||||
|
? moment(fromDate).format('DD MMM YYYY')
|
||||||
|
: 'Select date'}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.dateBox}
|
||||||
|
onPress={() => openDatePicker('TO')}
|
||||||
|
>
|
||||||
|
<Text style={styles.dateLabel}>To</Text>
|
||||||
|
<Text style={styles.dateValue}>
|
||||||
|
{toDate
|
||||||
|
? moment(toDate).format('DD MMM YYYY')
|
||||||
|
: 'Select date'}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* ===== STATUS ===== */}
|
||||||
|
<Text style={styles.sectionTitle}>Auction Status</Text>
|
||||||
|
|
||||||
|
<View style={styles.statusRow}>
|
||||||
|
{[
|
||||||
|
{ label: 'Running', value: 'RUNNING' },
|
||||||
|
{ label: 'Ended', value: 'ENDED' },
|
||||||
|
{ label: 'Not Started', value: 'NOT_STARTED' },
|
||||||
|
].map(item => {
|
||||||
|
const active = status === item.value;
|
||||||
|
return (
|
||||||
|
<TouchableOpacity
|
||||||
|
key={item.value}
|
||||||
|
onPress={() => setStatus(item.value as AuctionStatus)}
|
||||||
|
style={[
|
||||||
|
styles.statusPill,
|
||||||
|
active && styles.statusActive,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
styles.statusText,
|
||||||
|
active && styles.statusActiveText,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* ===== FOOTER ===== */}
|
||||||
|
<View style={styles.footer}>
|
||||||
|
{/* <TouchableOpacity style={styles.cancelBtn} onPress={onClose}>
|
||||||
|
<Text style={styles.cancelText}>Cancel</Text>
|
||||||
|
</TouchableOpacity> */}
|
||||||
|
|
||||||
|
<TouchableOpacity style={styles.applyBtn} onPress={handleApply}>
|
||||||
|
<Text style={styles.applyText}>Apply</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
{/* ===== DATE PICKER ===== */}
|
||||||
|
<DatePicker
|
||||||
|
modal
|
||||||
|
open={openPicker}
|
||||||
|
date={
|
||||||
|
pickerType === 'FROM'
|
||||||
|
? fromDate || new Date()
|
||||||
|
: toDate || new Date()
|
||||||
|
}
|
||||||
|
mode="date"
|
||||||
|
onConfirm={handleConfirm}
|
||||||
|
onCancel={() => setOpenPicker(false)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FilterModal;
|
||||||
|
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
overlay: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: 'rgba(0,0,0,0.4)',
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 0,
|
||||||
|
width: '100%',
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
borderTopLeftRadius: 22,
|
||||||
|
borderTopRightRadius: 22,
|
||||||
|
padding: 20,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: '700',
|
||||||
|
marginBottom: 20,
|
||||||
|
},
|
||||||
|
sectionTitle: {
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: '600',
|
||||||
|
marginBottom: 10,
|
||||||
|
color: '#475569',
|
||||||
|
},
|
||||||
|
dateRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
marginBottom: 20,
|
||||||
|
},
|
||||||
|
dateBox: {
|
||||||
|
width: '48%',
|
||||||
|
padding: 14,
|
||||||
|
borderRadius: 12,
|
||||||
|
backgroundColor: '#F8FAFC',
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: '#E2E8F0',
|
||||||
|
},
|
||||||
|
dateLabel: {
|
||||||
|
fontSize: 11,
|
||||||
|
color: '#64748B',
|
||||||
|
marginBottom: 4,
|
||||||
|
},
|
||||||
|
dateValue: {
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: '600',
|
||||||
|
color: '#0F172A',
|
||||||
|
},
|
||||||
|
statusRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
gap: 10,
|
||||||
|
},
|
||||||
|
statusPill: {
|
||||||
|
paddingVertical: 10,
|
||||||
|
paddingHorizontal: 14,
|
||||||
|
borderRadius: 20,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: '#CBD5E1',
|
||||||
|
backgroundColor: '#F8FAFC',
|
||||||
|
},
|
||||||
|
statusActive: {
|
||||||
|
backgroundColor: colors.primary,
|
||||||
|
borderColor: colors.primary,
|
||||||
|
},
|
||||||
|
statusText: {
|
||||||
|
fontSize: 13,
|
||||||
|
color: '#334155',
|
||||||
|
fontFamily:FontsFamily.poppinsSemiBold
|
||||||
|
},
|
||||||
|
statusActiveText: {
|
||||||
|
color: '#fff',
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
marginTop: 24,
|
||||||
|
},
|
||||||
|
cancelBtn: {
|
||||||
|
padding: 12,
|
||||||
|
},
|
||||||
|
|
||||||
|
applyBtn: {
|
||||||
|
backgroundColor: colors.primary,
|
||||||
|
paddingHorizontal: 20,
|
||||||
|
paddingVertical: 12,
|
||||||
|
borderRadius: 12,
|
||||||
|
width: '100%'
|
||||||
|
},
|
||||||
|
applyText: {
|
||||||
|
color: '#fff',
|
||||||
|
fontSize: 16,
|
||||||
|
textAlign: 'center',
|
||||||
|
fontFamily: FontsFamily.poppinsSemiBold
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import React, { useEffect, useState, useCallback, memo } from 'react';
|
||||||
|
import { View, Text, StyleSheet, FlatList, RefreshControl } from 'react-native';
|
||||||
|
import LiveAuctionTile from './LiveAuctionTile';
|
||||||
|
import { useNavigation } from '@react-navigation/native';
|
||||||
|
import moment from 'moment';
|
||||||
|
import { useFetchPurchaseAuction } from '../../Services/BBPS/Hooks';
|
||||||
|
import PurchaseAuctionTile from './PurchaseAuctionTile';
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
import { setAuction } from '../../Redux/Slices/SelectedAuction';
|
||||||
|
import { SearchInput } from '../../Component/Serach/SeacrhInput';
|
||||||
|
import { Skelton } from '../../Component/Index';
|
||||||
|
import { allBooleanProps } from './types/types';
|
||||||
|
import FilterModal from './Modal/FilterModal';
|
||||||
|
|
||||||
|
|
||||||
|
const PurchaseAuction = () => {
|
||||||
|
const navigation = useNavigation() as any;
|
||||||
|
const Dispatch = useDispatch()
|
||||||
|
const { data = {}, isPending } = useFetchPurchaseAuction();
|
||||||
|
const [allBoolean, setAllBoolean] = useState<allBooleanProps>({ isShowFilter: false })
|
||||||
|
const format = 'DD-MM-YYYY HH:mm:ss';
|
||||||
|
|
||||||
|
function isAuctionActive(item: any): boolean {
|
||||||
|
const now = moment();
|
||||||
|
const start = moment(item?.keyDates?.auctionBidding?.startDateTime, format);
|
||||||
|
const end = moment(item?.keyDates?.auctionBidding?.endDateTime, format);
|
||||||
|
|
||||||
|
if (!start.isValid() || !end.isValid()) return false;
|
||||||
|
|
||||||
|
return now.isBetween(start, end, undefined, '[]');
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAuctionEnd(item: any): boolean {
|
||||||
|
const now = moment();
|
||||||
|
const end = moment(item?.keyDates?.auctionBidding?.endDateTime, format);
|
||||||
|
|
||||||
|
if (!end.isValid()) return false;
|
||||||
|
|
||||||
|
return now.isAfter(end);
|
||||||
|
}
|
||||||
|
console.log(allBoolean);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={{ flex: 1 }}>
|
||||||
|
<SearchInput onChange={() => { }} placeholder='Search....' filterPress={() => { setAllBoolean((prev: any) => ({ ...prev, isShowFilter: !prev.isShowFilter })) }} />
|
||||||
|
{isPending ? <Skelton /> : <FlatList
|
||||||
|
data={data?.data.slice(0, 10) || []}
|
||||||
|
keyExtractor={(_, index) => index.toString()}
|
||||||
|
showsVerticalScrollIndicator={false}
|
||||||
|
contentContainerStyle={{ paddingBottom: 200, paddingHorizontal: 10 }}
|
||||||
|
renderItem={({ item, index }) => (
|
||||||
|
<PurchaseAuctionTile
|
||||||
|
item={item}
|
||||||
|
onPressDetails={() => {
|
||||||
|
navigation.navigate('AuctionDetail', { item, onAddToPurchase: () => { }, routeFrom: 'PurchasedAuction' });
|
||||||
|
}}
|
||||||
|
onStartBidding={() => {
|
||||||
|
Dispatch(setAuction(item))
|
||||||
|
// navigation.navigate(NavigationString, { data: item })
|
||||||
|
}}
|
||||||
|
buttonEnable={isAuctionActive(item)}
|
||||||
|
isAuctionEnd={isAuctionEnd(item)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>}
|
||||||
|
{allBoolean.isShowFilter && <View style={{ flex: 1 }}>
|
||||||
|
<FilterModal visible={allBoolean.isShowFilter} onClose={() => { setAllBoolean((prev: any) => ({ ...prev, isShowFilter: false })) }} onApply={() => { }} />
|
||||||
|
</View>}
|
||||||
|
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PurchaseAuction;
|
||||||
|
|
||||||
@@ -0,0 +1,277 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { View, Text, FlatList, StyleSheet, TouchableOpacity, Alert } from 'react-native';
|
||||||
|
import { InsideHeader, Loader } from '../../Component/Index';
|
||||||
|
import colors from '../../Constant/Color';
|
||||||
|
import { CreditCard, Calendar, User, DollarSign, ArrowRight } from 'lucide-react-native';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import { useOfflineAuctionPurchase } from '../../Services/BBPS/Hooks';
|
||||||
|
|
||||||
|
const dummyAuctionList = [
|
||||||
|
{
|
||||||
|
auctionId: 1,
|
||||||
|
auctionNumber: 'AUC-2025-001',
|
||||||
|
auctionPattern: 'Single Stage',
|
||||||
|
auctionDispatchDate: '25 Feb 2025',
|
||||||
|
auctionDispatcherNumber: 'DISP-001',
|
||||||
|
auctionFees: 2500,
|
||||||
|
gst: 18,
|
||||||
|
gstAmount: 450,
|
||||||
|
portalCharge: 100,
|
||||||
|
payableAmount: 3050,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
auctionId: 2,
|
||||||
|
auctionNumber: 'AUC-2025-002',
|
||||||
|
auctionPattern: 'Two Stage',
|
||||||
|
auctionDispatchDate: '28 Feb 2025',
|
||||||
|
auctionDispatcherNumber: 'DISP-002',
|
||||||
|
auctionFees: 1800,
|
||||||
|
gst: 18,
|
||||||
|
gstAmount: 324,
|
||||||
|
portalCharge: 80,
|
||||||
|
payableAmount: 2204,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
auctionId: 3,
|
||||||
|
auctionNumber: 'AUC-2025-003',
|
||||||
|
auctionPattern: 'Single Stage',
|
||||||
|
auctionDispatchDate: '01 Mar 2025',
|
||||||
|
auctionDispatcherNumber: 'DISP-003',
|
||||||
|
auctionFees: 3200,
|
||||||
|
gst: 18,
|
||||||
|
gstAmount: 576,
|
||||||
|
portalCharge: 120,
|
||||||
|
payableAmount: 3896,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const PurchaseAuctionList = () => {
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [list, setList] = useState(dummyAuctionList);
|
||||||
|
const SelectedPurchaseList = useSelector((state: any) => state.selectPurchaseAuction);
|
||||||
|
console.log(SelectedPurchaseList,'iiiiiiiiiiiiiiiiiiiiiiii');
|
||||||
|
|
||||||
|
const { mutate, isPending } = useOfflineAuctionPurchase()
|
||||||
|
const calculateTotalAmount = (item: any) => {
|
||||||
|
const gstAmount =
|
||||||
|
(Number(item.auctionFees) + Number(item.portalCharge)) *
|
||||||
|
(Number(item.gst) / 100);
|
||||||
|
|
||||||
|
const payableAmount =
|
||||||
|
Number(item.auctionFees) + Number(item.portalCharge) + gstAmount;
|
||||||
|
return payableAmount
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOfflinePurchase = async () => {
|
||||||
|
const body = {
|
||||||
|
auctionDetails: SelectedPurchaseList.map((b: any) => ({
|
||||||
|
auctionDispatchDate: b?.auctionDispatchDate,
|
||||||
|
auctionDispatcherNumber: b?.auctionDispatcherNumber,
|
||||||
|
auctionFees: b?.auctionFees,
|
||||||
|
auctionId: b?.auctionId,
|
||||||
|
auctionNumber: b?.auctionNumber,
|
||||||
|
auctionPattern: b?.auctionPattern,
|
||||||
|
gst: b?.gst,
|
||||||
|
gstAmount: b?.gstAmount,
|
||||||
|
payableAmount: calculateTotalAmount(b),
|
||||||
|
portalCharge: b?.portalCharge
|
||||||
|
})),
|
||||||
|
orderNo: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
// console.log(body);
|
||||||
|
mutate(body, {
|
||||||
|
onSuccess: (res) => {
|
||||||
|
console.log(res, '---------------------purchsase response');
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
console.log(error, "================purchse Error");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.log("purchase offline error", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePurchase = (type: 'online' | 'offline') => {
|
||||||
|
setLoading(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
setLoading(false);
|
||||||
|
type === 'online' ? Alert.alert('Online Purchase Successful!') : handleOfflinePurchase();
|
||||||
|
|
||||||
|
}, 1200);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderItem = ({ item }: { item: typeof dummyAuctionList[0] }) => (
|
||||||
|
<View style={styles.card}>
|
||||||
|
<View style={styles.cardHeader}>
|
||||||
|
<Text style={styles.title}>{item.auctionNumber}</Text>
|
||||||
|
<Text style={styles.pattern}>{item.auctionPattern}</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.cardRow}>
|
||||||
|
<Calendar size={18} color={colors.primary} />
|
||||||
|
<Text style={styles.text}>Dispatch Date: {item.auctionDispatchDate}</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.cardRow}>
|
||||||
|
<User size={18} color={colors.primary} />
|
||||||
|
<Text style={styles.text}>Dispatcher No: {item.auctionDispatcherNumber}</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.cardRow}>
|
||||||
|
<DollarSign size={18} color={colors.primary} />
|
||||||
|
<Text style={styles.text}>Auction Fees: ₹{item.auctionFees}</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.cardRow}>
|
||||||
|
<DollarSign size={18} color={colors.primary} />
|
||||||
|
<Text style={styles.text}>GST ({item.gst}%): ₹{item.gstAmount}</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.cardRow}>
|
||||||
|
<DollarSign size={18} color={colors.primary} />
|
||||||
|
<Text style={styles.text}>Portal Charge: ₹{item.portalCharge}</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.totalRow}>
|
||||||
|
<CreditCard size={20} color={colors.white} />
|
||||||
|
<Text style={styles.total}>Total: ₹{item.payableAmount}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={{ flex: 1, backgroundColor: colors.white }}>
|
||||||
|
<InsideHeader title="Purchase List" showArrow />
|
||||||
|
<View style={styles.screen}>
|
||||||
|
<FlatList
|
||||||
|
data={SelectedPurchaseList}
|
||||||
|
renderItem={renderItem}
|
||||||
|
keyExtractor={(item) => item.auctionId.toString()}
|
||||||
|
contentContainerStyle={styles.container}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.bottomButtonContainer}>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[styles.button, { marginRight: 8 }]}
|
||||||
|
onPress={() => handlePurchase('online')}
|
||||||
|
>
|
||||||
|
<Text style={styles.buttonText}>Purchase Online</Text>
|
||||||
|
<ArrowRight size={18} color="#fff" style={{ marginLeft: 6 }} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[styles.button, { marginLeft: 8 }]}
|
||||||
|
onPress={() => handlePurchase('offline')}
|
||||||
|
>
|
||||||
|
<Text style={styles.buttonText}>Purchase Offline</Text>
|
||||||
|
<ArrowRight size={18} color="#fff" style={{ marginLeft: 6 }} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{loading && (
|
||||||
|
<View style={styles.loadingOverlay}>
|
||||||
|
<Loader />
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PurchaseAuctionList;
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
screen: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
padding: 16,
|
||||||
|
paddingBottom: 120,
|
||||||
|
},
|
||||||
|
card: {
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
borderRadius: 16,
|
||||||
|
padding: 16,
|
||||||
|
marginBottom: 16,
|
||||||
|
elevation: 5,
|
||||||
|
shadowColor: '#000',
|
||||||
|
shadowOpacity: 0.12,
|
||||||
|
shadowOffset: { width: 0, height: 3 },
|
||||||
|
shadowRadius: 8,
|
||||||
|
},
|
||||||
|
cardHeader: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
marginBottom: 12,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: '700',
|
||||||
|
color: '#0F172A',
|
||||||
|
},
|
||||||
|
pattern: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: colors.primary,
|
||||||
|
fontWeight: '600',
|
||||||
|
},
|
||||||
|
cardRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 6,
|
||||||
|
marginBottom: 4,
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: '#555',
|
||||||
|
},
|
||||||
|
totalRow: {
|
||||||
|
marginTop: 10,
|
||||||
|
backgroundColor: colors.primary,
|
||||||
|
borderRadius: 12,
|
||||||
|
paddingVertical: 10,
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 8,
|
||||||
|
},
|
||||||
|
total: {
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: '700',
|
||||||
|
color: '#fff',
|
||||||
|
},
|
||||||
|
bottomButtonContainer: {
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
|
flexDirection: 'row',
|
||||||
|
backgroundColor: colors.white,
|
||||||
|
width: '100%',
|
||||||
|
paddingVertical: 20,
|
||||||
|
paddingHorizontal: 10,
|
||||||
|
elevation: 5
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: colors.primary,
|
||||||
|
paddingVertical: 14,
|
||||||
|
borderRadius: 12,
|
||||||
|
alignItems: 'center',
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
buttonText: {
|
||||||
|
color: '#fff',
|
||||||
|
fontWeight: '700',
|
||||||
|
fontSize: 14,
|
||||||
|
},
|
||||||
|
loadingOverlay: {
|
||||||
|
...StyleSheet.absoluteFillObject,
|
||||||
|
backgroundColor: 'rgba(0,0,0,0.2)',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
zIndex: 10,
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,274 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
View,
|
||||||
|
Text,
|
||||||
|
StyleSheet,
|
||||||
|
TouchableOpacity,
|
||||||
|
Platform,
|
||||||
|
} from 'react-native';
|
||||||
|
import Animated, {
|
||||||
|
FadeInUp,
|
||||||
|
useAnimatedStyle,
|
||||||
|
useSharedValue,
|
||||||
|
withTiming,
|
||||||
|
} from 'react-native-reanimated';
|
||||||
|
import moment from 'moment';
|
||||||
|
import CountdownTimer from '../../Component/Counter/CountdownTimer';
|
||||||
|
import colors from '../../Constant/Color';
|
||||||
|
|
||||||
|
const PurchaseAuctionTile = ({
|
||||||
|
item,
|
||||||
|
onPressDetails,
|
||||||
|
onStartBidding,
|
||||||
|
buttonEnable,
|
||||||
|
isAuctionEnd,
|
||||||
|
}: any) => {
|
||||||
|
const scale = useSharedValue(1);
|
||||||
|
const elevation = useSharedValue(8);
|
||||||
|
|
||||||
|
const animatedStyle = useAnimatedStyle(() => {
|
||||||
|
return {
|
||||||
|
transform: [{ scale: scale.value }],
|
||||||
|
elevation: elevation.value, // Android-safe
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handlePressIn = () => {
|
||||||
|
scale.value = withTiming(0.97, { duration: 120 });
|
||||||
|
elevation.value = withTiming(3, { duration: 120 });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePressOut = () => {
|
||||||
|
scale.value = withTiming(1, { duration: 160 });
|
||||||
|
elevation.value = withTiming(8, { duration: 160 });
|
||||||
|
};
|
||||||
|
|
||||||
|
const statusColor = isAuctionEnd
|
||||||
|
? '#EF4444'
|
||||||
|
: buttonEnable
|
||||||
|
? colors.primary
|
||||||
|
: '#94A3B8';
|
||||||
|
|
||||||
|
const lightStatusBg = isAuctionEnd
|
||||||
|
? '#FEF2F2'
|
||||||
|
: buttonEnable
|
||||||
|
? '#EFF6FF'
|
||||||
|
: '#F8FAFC';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Animated.View
|
||||||
|
entering={FadeInUp.duration(400)}
|
||||||
|
style={[styles.shadowContainer, animatedStyle]}
|
||||||
|
>
|
||||||
|
<TouchableOpacity
|
||||||
|
activeOpacity={1}
|
||||||
|
onPress={onPressDetails}
|
||||||
|
onPressIn={handlePressIn}
|
||||||
|
onPressOut={handlePressOut}
|
||||||
|
style={styles.cardInternal}
|
||||||
|
>
|
||||||
|
<View style={[styles.statusAccent, { backgroundColor: statusColor }]} />
|
||||||
|
|
||||||
|
<View style={styles.mainInfo}>
|
||||||
|
{/* Header */}
|
||||||
|
<View style={styles.headerRow}>
|
||||||
|
<View>
|
||||||
|
<View style={[styles.statusBadge, { backgroundColor: lightStatusBg }]}>
|
||||||
|
<View style={[styles.statusDot, { backgroundColor: statusColor }]} />
|
||||||
|
<Text style={[styles.statusText, { color: statusColor }]}>
|
||||||
|
{isAuctionEnd ? 'ENDED' : buttonEnable ? 'LIVE' : 'PENDING'}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<Text style={styles.title}>{item.auctionNumber}</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.amountContainer}>
|
||||||
|
<Text style={styles.amount}>
|
||||||
|
₹{item.auctionFees?.toLocaleString()}
|
||||||
|
</Text>
|
||||||
|
<Text style={styles.feeLabel}>Auction Fee</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Date */}
|
||||||
|
<View style={styles.dateRow}>
|
||||||
|
<Text style={styles.dateText}>
|
||||||
|
<Text style={styles.dateLabel}>Bidding Ends: </Text>
|
||||||
|
{moment(item?.keyDates?.auctionBidding?.endDateTime).format(
|
||||||
|
'DD MMM, hh:mm A'
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Footer */}
|
||||||
|
<View style={styles.footer}>
|
||||||
|
<View style={styles.footerTop}>
|
||||||
|
<Text style={styles.footerLabel}>Remaining Time</Text>
|
||||||
|
{buttonEnable && !isAuctionEnd ? (
|
||||||
|
<CountdownTimer
|
||||||
|
endDate={item.keyDates.auctionBidding.endDateTime}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Text style={styles.endedText}>--:--:--</Text>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<TouchableOpacity
|
||||||
|
disabled={!buttonEnable}
|
||||||
|
onPress={onStartBidding}
|
||||||
|
activeOpacity={0.85}
|
||||||
|
style={[
|
||||||
|
styles.actionButton,
|
||||||
|
{ backgroundColor: statusColor },
|
||||||
|
!buttonEnable && styles.disabledButton,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Text style={styles.addText}>
|
||||||
|
{isAuctionEnd
|
||||||
|
? 'Bidding Closed'
|
||||||
|
: !buttonEnable
|
||||||
|
? 'Payment Required'
|
||||||
|
: 'Proceed to Start Bidding'}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</Animated.View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PurchaseAuctionTile;
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
shadowContainer: {
|
||||||
|
marginHorizontal: 5,
|
||||||
|
marginTop: 14,
|
||||||
|
marginBottom: 6,
|
||||||
|
borderRadius: 16,
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
elevation: 8,
|
||||||
|
},
|
||||||
|
cardInternal: {
|
||||||
|
borderRadius: 16,
|
||||||
|
overflow: 'hidden',
|
||||||
|
flexDirection: 'row',
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
},
|
||||||
|
statusAccent: {
|
||||||
|
width: 5,
|
||||||
|
height: '100%',
|
||||||
|
},
|
||||||
|
mainInfo: {
|
||||||
|
flex: 1,
|
||||||
|
padding: 16,
|
||||||
|
},
|
||||||
|
headerRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'flex-start',
|
||||||
|
marginBottom: 12,
|
||||||
|
},
|
||||||
|
statusBadge: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingHorizontal: 8,
|
||||||
|
paddingVertical: 3,
|
||||||
|
borderRadius: 6,
|
||||||
|
marginBottom: 6,
|
||||||
|
alignSelf: 'flex-start',
|
||||||
|
},
|
||||||
|
statusDot: {
|
||||||
|
width: 6,
|
||||||
|
height: 6,
|
||||||
|
borderRadius: 3,
|
||||||
|
marginRight: 5,
|
||||||
|
},
|
||||||
|
statusText: {
|
||||||
|
fontSize: 10,
|
||||||
|
fontWeight: '800',
|
||||||
|
letterSpacing: 0.5,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontWeight: '800',
|
||||||
|
fontSize: 16,
|
||||||
|
color: '#1E293B',
|
||||||
|
},
|
||||||
|
amountContainer: {
|
||||||
|
alignItems: 'flex-end',
|
||||||
|
},
|
||||||
|
amount: {
|
||||||
|
fontWeight: '900',
|
||||||
|
fontSize: 18,
|
||||||
|
color: '#0F172A',
|
||||||
|
},
|
||||||
|
feeLabel: {
|
||||||
|
fontSize: 10,
|
||||||
|
color: '#64748B',
|
||||||
|
fontWeight: '500',
|
||||||
|
},
|
||||||
|
dateRow: {
|
||||||
|
backgroundColor: '#F8FAFC',
|
||||||
|
paddingHorizontal: 10,
|
||||||
|
paddingVertical: 8,
|
||||||
|
borderRadius: 10,
|
||||||
|
marginBottom: 16,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: '#F1F5F9',
|
||||||
|
},
|
||||||
|
dateText: {
|
||||||
|
fontSize: 12,
|
||||||
|
color: '#334155',
|
||||||
|
fontWeight: '600',
|
||||||
|
},
|
||||||
|
dateLabel: {
|
||||||
|
fontWeight: '400',
|
||||||
|
color: '#64748B',
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
borderTopWidth: 1,
|
||||||
|
borderTopColor: '#F1F5F9',
|
||||||
|
paddingTop: 12,
|
||||||
|
},
|
||||||
|
footerTop: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginBottom: 12,
|
||||||
|
},
|
||||||
|
footerLabel: {
|
||||||
|
fontSize: 11,
|
||||||
|
fontWeight: '600',
|
||||||
|
color: '#94A3B8',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
},
|
||||||
|
endedText: {
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: '700',
|
||||||
|
color: '#CBD5E1',
|
||||||
|
},
|
||||||
|
actionButton: {
|
||||||
|
width: '100%',
|
||||||
|
height: 48,
|
||||||
|
borderRadius: 12,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
...Platform.select({
|
||||||
|
ios: {
|
||||||
|
shadowColor: '#000',
|
||||||
|
shadowOffset: { width: 0, height: 2 },
|
||||||
|
shadowOpacity: 0.1,
|
||||||
|
shadowRadius: 4,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
disabledButton: {
|
||||||
|
backgroundColor: '#E2E8F0',
|
||||||
|
},
|
||||||
|
addText: {
|
||||||
|
color: '#fff',
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: '800',
|
||||||
|
letterSpacing: 0.2,
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,220 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { View, Text, StyleSheet, ScrollView, TouchableOpacity } from 'react-native';
|
||||||
|
import Animated, { FadeIn, FadeOut, Layout, useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated';
|
||||||
|
import { ChevronDown, Trash2, Info } from 'lucide-react-native';
|
||||||
|
import { InsideHeader } from '../../Component/Index';
|
||||||
|
import colors from '../../Constant/Color';
|
||||||
|
import { useNavigation } from '@react-navigation/native';
|
||||||
|
import NavigationString from '../../Constant/NavigationString';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import { togglePurchaseAuction } from '../../Redux/Slices/selectPurchaseAuction';
|
||||||
|
|
||||||
|
const PurchaseList = () => {
|
||||||
|
const Navigation: any = useNavigation()
|
||||||
|
const Dispatch = useDispatch()
|
||||||
|
const [expandedIndex, setExpandedIndex] = useState<number | null>(null);
|
||||||
|
const SelectedPurchaseList = useSelector((state: any) => state.selectPurchaseAuction);
|
||||||
|
const toggleExpand = (index: number) => {
|
||||||
|
setExpandedIndex(prev => (prev === index ? null : index));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.screen}>
|
||||||
|
<InsideHeader title="Purchase" showArrow />
|
||||||
|
|
||||||
|
<ScrollView contentContainerStyle={styles.container}>
|
||||||
|
{/* Header */}
|
||||||
|
<View style={styles.header}>
|
||||||
|
<Text style={styles.heading}>Purchase List</Text>
|
||||||
|
<Info size={20} color="#0F172A" />
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Cards */}
|
||||||
|
{SelectedPurchaseList.map((item: any, index: any) => {
|
||||||
|
const expanded = expandedIndex === index;
|
||||||
|
return (
|
||||||
|
<Animated.View
|
||||||
|
key={item.auctionId}
|
||||||
|
layout={Layout.springify()}
|
||||||
|
style={styles.card}
|
||||||
|
>
|
||||||
|
<TouchableOpacity
|
||||||
|
activeOpacity={0.9}
|
||||||
|
onPress={() => toggleExpand(index)}
|
||||||
|
>
|
||||||
|
<CardHeader
|
||||||
|
title={item.auctionOfWork}
|
||||||
|
expanded={expanded}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Text style={styles.subTitle}>
|
||||||
|
Auction No: {item.auctionNumber}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Text style={styles.subTitle}>
|
||||||
|
Status:{' '}
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
color: item.status ? '#16A34A' : '#DC2626',
|
||||||
|
fontWeight: '700',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{item.status ? 'Open' : 'Closed'}
|
||||||
|
</Text>
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
{/* Expanded Content */}
|
||||||
|
{expanded && (
|
||||||
|
<Animated.View
|
||||||
|
entering={FadeIn.duration(220)}
|
||||||
|
exiting={FadeOut.duration(180)}
|
||||||
|
layout={Layout.springify()}
|
||||||
|
style={styles.details}
|
||||||
|
>
|
||||||
|
{renderRow('Auction Mode', item.auctionMode)}
|
||||||
|
{renderRow('Pattern', item.auctionPattern)}
|
||||||
|
{renderRow('Fees', `₹ ${item.auctionFees}`)}
|
||||||
|
{renderRow('Department', item.deptName)}
|
||||||
|
{renderRow('Purchase End', item.purchaseEnd)}
|
||||||
|
|
||||||
|
<TouchableOpacity style={styles.removeButton} onPress={() => { Dispatch(togglePurchaseAuction(item)) }}>
|
||||||
|
<Trash2 size={18} color="#fff" />
|
||||||
|
<Text style={styles.removeText}>Remove</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</Animated.View>
|
||||||
|
)}
|
||||||
|
</Animated.View>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
{/* Footer */}
|
||||||
|
<TouchableOpacity disabled={Boolean(!SelectedPurchaseList.length)} style={[styles.proceedButton, { backgroundColor: Boolean(!SelectedPurchaseList.length) ? colors.lightText : colors.primary }]} onPress={() => { Navigation.navigate(NavigationString.PurchaseAuctionList) }}>
|
||||||
|
<Text style={styles.proceedText}>Proceed to Purchase</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PurchaseList;
|
||||||
|
|
||||||
|
const CardHeader = ({ title, expanded }: {
|
||||||
|
title: string;
|
||||||
|
expanded: boolean;
|
||||||
|
}) => {
|
||||||
|
const rotation = useSharedValue(expanded ? 1 : 0);
|
||||||
|
rotation.value = withTiming(expanded ? 1 : 0, { duration: 220 });
|
||||||
|
const arrowStyle = useAnimatedStyle(() => ({
|
||||||
|
transform: [{ rotate: `${rotation.value * 180}deg` }],
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.cardHeader}>
|
||||||
|
<Text style={styles.title}>{title}</Text>
|
||||||
|
<Animated.View style={arrowStyle}>
|
||||||
|
<ChevronDown size={22} color={colors.black} />
|
||||||
|
</Animated.View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderRow = (label: string, value: string) => (
|
||||||
|
<View style={styles.row}>
|
||||||
|
<Text style={styles.label}>{label}</Text>
|
||||||
|
<Text style={styles.value}>{value}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
screen: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: '#F8FAFC',
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
padding: 16,
|
||||||
|
paddingBottom: 110,
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
marginBottom: 14,
|
||||||
|
},
|
||||||
|
heading: {
|
||||||
|
fontSize: 22,
|
||||||
|
fontWeight: '800',
|
||||||
|
color: colors.black,
|
||||||
|
},
|
||||||
|
card: {
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
borderRadius: 18,
|
||||||
|
padding: 16,
|
||||||
|
marginBottom: 14,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: '#E5E7EB',
|
||||||
|
},
|
||||||
|
cardHeader: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: '700',
|
||||||
|
color: '#0F172A',
|
||||||
|
flex: 1,
|
||||||
|
marginRight: 12,
|
||||||
|
},
|
||||||
|
subTitle: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: '#64748B',
|
||||||
|
marginTop: 6,
|
||||||
|
},
|
||||||
|
details: {
|
||||||
|
marginTop: 16,
|
||||||
|
paddingTop: 14,
|
||||||
|
borderTopWidth: 1,
|
||||||
|
borderTopColor: '#E5E7EB',
|
||||||
|
},
|
||||||
|
row: {
|
||||||
|
marginVertical: 6,
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
fontSize: 13,
|
||||||
|
color: '#64748B',
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: '600',
|
||||||
|
color: '#0F172A',
|
||||||
|
},
|
||||||
|
removeButton: {
|
||||||
|
marginTop: 18,
|
||||||
|
backgroundColor: colors.primary,
|
||||||
|
borderRadius: 14,
|
||||||
|
paddingVertical: 12,
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'center',
|
||||||
|
gap: 8,
|
||||||
|
},
|
||||||
|
removeText: {
|
||||||
|
color: '#fff',
|
||||||
|
fontWeight: '700',
|
||||||
|
},
|
||||||
|
proceedButton: {
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 16,
|
||||||
|
left: 16,
|
||||||
|
right: 16,
|
||||||
|
backgroundColor: colors.primary,
|
||||||
|
paddingVertical: 16,
|
||||||
|
borderRadius: 18,
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
proceedText: {
|
||||||
|
color: '#fff',
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: '800',
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
export interface LiveAuctionTileProps {
|
||||||
|
data: any
|
||||||
|
onPurchasePress?: () => void;
|
||||||
|
onDetailsPress?: () => void;
|
||||||
|
index: number;
|
||||||
|
auctionDispatchDate?: string;
|
||||||
|
auctionOfWork?: string;
|
||||||
|
purchaseEnd?: string;
|
||||||
|
auctionFees?: string | number;
|
||||||
|
isPurchased?: boolean;
|
||||||
|
auctionId?: any;
|
||||||
|
auctionNumber?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface allBooleanProps {
|
||||||
|
showDetail?: boolean
|
||||||
|
isShowFilter?: boolean
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { Text, View } from 'react-native';
|
||||||
|
import moment from 'moment';
|
||||||
|
import LottieView from 'lottie-react-native';
|
||||||
|
import colors from '../../Constant/Color';
|
||||||
|
|
||||||
|
const AuctionTimer = ({ SelectedAuction, setAllBoolean }: any) => {
|
||||||
|
const [timeLeft, setTimeLeft] = useState('00:00:00');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!SelectedAuction?.keyDates?.auctionBidding?.endDateTime) return;
|
||||||
|
|
||||||
|
const format = 'DD-MM-YYYY HH:mm:ss';
|
||||||
|
const endTime = moment(
|
||||||
|
SelectedAuction.keyDates.auctionBidding.endDateTime,
|
||||||
|
format
|
||||||
|
);
|
||||||
|
|
||||||
|
const timer = setInterval(() => {
|
||||||
|
const now = moment();
|
||||||
|
const diff = moment.duration(endTime.diff(now));
|
||||||
|
|
||||||
|
if (diff.asSeconds() <= 0) {
|
||||||
|
setTimeLeft('00:00:00');
|
||||||
|
clearInterval(timer);
|
||||||
|
|
||||||
|
setAllBoolean((prev: any) => ({
|
||||||
|
...prev,
|
||||||
|
timeEnd: true,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hours = String(Math.floor(diff.asHours())).padStart(2, '0');
|
||||||
|
const minutes = String(diff.minutes()).padStart(2, '0');
|
||||||
|
const seconds = String(diff.seconds()).padStart(2, '0');
|
||||||
|
|
||||||
|
setTimeLeft(`${hours}:${minutes}:${seconds}`);
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
return () => clearInterval(timer);
|
||||||
|
}, [SelectedAuction]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 5,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<LottieView
|
||||||
|
source={require('../../lottie/clock.json')}
|
||||||
|
autoPlay
|
||||||
|
loop
|
||||||
|
style={{ width: 20, height: 20 }}
|
||||||
|
/>
|
||||||
|
<Text style={{ fontSize: 18, color: colors.black }}>{timeLeft}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AuctionTimer;
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
|
||||||
|
import LottieView from 'lottie-react-native';
|
||||||
|
import Animated, { FadeInUp } from 'react-native-reanimated';
|
||||||
|
import colors from '../../../Constant/Color';
|
||||||
|
|
||||||
|
const EmdComplete = ({ onNext }: { onNext?: () => void }) => {
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<LottieView
|
||||||
|
source={require('../../../lottie/confetti.json')}
|
||||||
|
autoPlay
|
||||||
|
loop={true}
|
||||||
|
style={styles.confetti}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<LottieView
|
||||||
|
source={require('../../../lottie/Success.json')}
|
||||||
|
autoPlay
|
||||||
|
loop={true}
|
||||||
|
style={styles.success}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Animated.Text entering={FadeInUp.delay(400).duration(700)} style={styles.title}>
|
||||||
|
EMD Complete
|
||||||
|
</Animated.Text>
|
||||||
|
|
||||||
|
<Animated.Text entering={FadeInUp.delay(700).duration(700)} style={styles.subText} >
|
||||||
|
Your submission was successful
|
||||||
|
</Animated.Text>
|
||||||
|
|
||||||
|
<Animated.View entering={FadeInUp.delay(900).duration(700)} style={styles.nextWrapper} >
|
||||||
|
<TouchableOpacity activeOpacity={0.85} style={styles.nextBtn} onPress={onNext} >
|
||||||
|
<Text style={styles.nextText}>Next</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</Animated.View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EmdComplete;
|
||||||
|
|
||||||
|
/* ---------------- Styles ---------------- */
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: '#F8FAFC',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
|
||||||
|
confetti: {
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
},
|
||||||
|
|
||||||
|
success: {
|
||||||
|
width: 220,
|
||||||
|
height: 220,
|
||||||
|
marginTop: -100
|
||||||
|
},
|
||||||
|
|
||||||
|
title: {
|
||||||
|
marginTop: 16,
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: '900',
|
||||||
|
color: '#0F172A',
|
||||||
|
letterSpacing: 0.5,
|
||||||
|
},
|
||||||
|
|
||||||
|
subText: {
|
||||||
|
marginTop: 8,
|
||||||
|
fontSize: 14,
|
||||||
|
color: '#475569',
|
||||||
|
},
|
||||||
|
|
||||||
|
/* ---------- Floating Next Button ---------- */
|
||||||
|
nextWrapper: {
|
||||||
|
position: 'absolute',
|
||||||
|
right: 20,
|
||||||
|
bottom: 30,
|
||||||
|
},
|
||||||
|
|
||||||
|
nextBtn: {
|
||||||
|
backgroundColor: colors.primary,
|
||||||
|
paddingHorizontal: 26,
|
||||||
|
paddingVertical: 14,
|
||||||
|
borderRadius: 28,
|
||||||
|
|
||||||
|
shadowColor: colors.primary,
|
||||||
|
shadowOffset: { width: 0, height: 8 },
|
||||||
|
shadowOpacity: 0.35,
|
||||||
|
shadowRadius: 14,
|
||||||
|
elevation: 6,
|
||||||
|
},
|
||||||
|
|
||||||
|
nextText: {
|
||||||
|
color: '#FFFFFF',
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: '800',
|
||||||
|
letterSpacing: 0.4,
|
||||||
|
},
|
||||||
|
});
|
||||||