Compare commits

..

25 Commits

Author SHA1 Message Date
Mekan1206
ceabc0079c Refactor ProfileScreen to improve account deletion UI; update styles for danger items and remove alert for app information. 2025-09-17 18:11:30 +05:00
Mekan1206
9b26ddaa37 Add PDF download button to CardRequisiteOrderDetailsScreen for enhanced functionality. 2025-09-17 15:05:31 +05:00
Mekan1206
0d12a01812 Refactor multiple screens to replace SafeAreaView imports with react-native-safe-area-context, enhancing layout consistency across the application. 2025-09-17 14:55:18 +05:00
388d5e9a82 Remove card year validation logic from EditProfileModal for simplification and future refactoring. 2025-09-11 16:28:20 +05:00
99bdb01644 Update VerificationScreen to use react-native-confirmation-code-field for enhanced OTP input; add bundleIdentifier in app.json and include new dependency in package.json and package-lock.json. 2025-09-11 15:37:38 +05:00
a41851dfcc Refactor VerificationScreen to include KeyboardAvoidingView for improved keyboard handling; update text in CardTransactionOrderDetailsScreen for better localization. 2025-09-11 15:28:40 +05:00
Mekan1206
27f43a77fd Update app.json to change splash screen background color to white; refactor MainNavigator and screens to utilize dynamic padding with useSafeAreaInsets, enhancing layout consistency across Home, Menu, and Profile screens. 2025-09-11 13:11:19 +05:00
Mekan1206
27f16f3c38 Refactor MenuScreen to use useSafeAreaInsets for dynamic padding, replacing SafeAreaView with View for improved layout handling. 2025-09-11 12:45:47 +05:00
Mekan1206
2eb41db2e5 AAA Refactor app structure to use SafeAreaProvider, update StatusBar configuration, and enhance tab navigator styles. Add expo-system-ui dependency for improved status bar handling. 2025-09-11 12:39:19 +05:00
Mekan1206
61bcb6aa3f Remove dark mode configuration from splash screen settings in app.json 2025-09-11 12:12:56 +05:00
66f5271640 Add expo-splash-screen dependency and configure splash screen settings in app.json 2025-09-11 10:47:38 +05:00
db6a9e2bc0 wip 2025-09-11 10:42:07 +05:00
14db29ab61 wip 2025-09-11 10:40:42 +05:00
5ca3bb4f17 Update .gitignore to include .android and .ios directories for better build management 2025-09-11 10:32:51 +05:00
61fb0b2d96 Update app configuration and dependencies: add splash screen settings, update expo and related packages, and remove unused splash icon. 2025-09-11 10:24:11 +05:00
daae154aee Fix loading state management in HomeScreen by ensuring loadingTransactions is set to false after fetching transactions. 2025-09-11 01:54:16 +05:00
6751cc506e Add subtitle to Kart hereketleri section in HomeScreen and update styles for improved layout 2025-09-11 01:50:26 +05:00
056b4a5627 Implement transaction fetching and display in HomeScreen, update TransactionList for localization and improved data handling 2025-09-11 01:19:11 +05:00
d9b8e3f7ac wip 2025-09-10 21:06:10 +05:00
5f59609d6f wip 2025-09-10 21:05:54 +05:00
b9b604167b Add copyright notice and link to ProfileScreen, including styling for copyright text and link. 2025-09-10 21:04:08 +05:00
4630b195b9 Refactor transaction display in HomeScreen by integrating TransactionList component and removing redundant code for rendering transactions. 2025-09-10 20:49:24 +05:00
4efaf2543e Add transaction display feature to HomeScreen 2025-09-10 20:34:38 +05:00
b925b48dd4 a 2025-07-16 16:30:55 +05:00
Mekan1206
3380c9d85b add android and ios folders to git ignore 2025-07-15 22:37:36 +05:00
45 changed files with 1096 additions and 341 deletions

5
.gitignore vendored
View File

@@ -35,3 +35,8 @@ yarn-error.*
# typescript
*.tsbuildinfo
android/
ios/
.android/
.ios/

17
App.js
View File

@@ -2,15 +2,18 @@ import React from 'react';
import { AuthProvider } from './src/contexts/AuthContext';
import RootNavigator from './src/navigation/RootNavigator';
import { BaseEnumsProvider } from './src/contexts/BaseEnumsContext';
import { StatusBar } from 'react-native';
import { StatusBar } from 'expo-status-bar';
import { SafeAreaProvider } from 'react-native-safe-area-context';
export default function App() {
return (
<AuthProvider>
<BaseEnumsProvider>
<StatusBar barStyle="dark-content" backgroundColor="#fff"/>
<RootNavigator />
</BaseEnumsProvider>
</AuthProvider>
<SafeAreaProvider>
<AuthProvider>
<BaseEnumsProvider>
<StatusBar style="dark" />
<RootNavigator />
</BaseEnumsProvider>
</AuthProvider>
</SafeAreaProvider>
);
}

View File

@@ -10,10 +10,14 @@
"splash": {
"image": "./assets/splash-icon.png",
"resizeMode": "contain",
"backgroundColor": "#17b69b"
"backgroundColor": "#ffffff"
},
"ios": {
"supportsTablet": true
"supportsTablet": true,
"statusBar": {
"barStyle": "dark-content"
},
"bundleIdentifier": "com.nurmuhammet.ali.tbbankonline"
},
"android": {
"adaptiveIcon": {
@@ -21,7 +25,11 @@
"backgroundColor": "#17b69b"
},
"edgeToEdgeEnabled": true,
"package": "com.nurmuhammet.ali.tbbankonline"
"package": "com.nurmuhammet.ali.tbbankonline",
"statusBar": {
"barStyle": "dark-content",
"backgroundColor": "#ffffff"
}
},
"web": {
"favicon": "./assets/favicon.png"
@@ -30,6 +38,18 @@
"eas": {
"projectId": "280bed78-9335-4b73-a686-15a9f726a7ad"
}
}
},
"plugins": [
"expo-font",
[
"expo-splash-screen",
{
"backgroundColor": "#ffffff",
"image": "./assets/splash-icon.png",
"imageWidth": 200
}
],
"expo-system-ui"
]
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 156 KiB

619
package-lock.json generated
View File

@@ -15,11 +15,16 @@
"@react-navigation/bottom-tabs": "^7.4.2",
"@react-navigation/native": "^7.1.14",
"@react-navigation/stack": "^7.4.2",
"expo": "53.0.19",
"expo": "53.0.22",
"expo-font": "~13.3.2",
"expo-image-picker": "~16.1.4",
"expo-splash-screen": "^31.0.8",
"expo-status-bar": "~2.2.3",
"expo-system-ui": "~5.0.11",
"react": "19.0.0",
"react-native": "0.79.5",
"react-native-confirmation-code-field": "^8.0.1",
"react-native-gesture-handler": "~2.24.0",
"react-native-modal-datetime-picker": "^15.0.1",
"react-native-safe-area-context": "5.4.0",
"react-native-screens": "^4.11.1",
@@ -31,9 +36,9 @@
}
},
"node_modules/@0no-co/graphql.web": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.1.2.tgz",
"integrity": "sha512-N2NGsU5FLBhT8NZ+3l2YrzZSHITjNXNuDhC4iDiikv0IujaJ0Xc6xIxQZ/Ek3Cb+rgPjnLHYyJm11tInuJn+cw==",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.2.0.tgz",
"integrity": "sha512-/1iHy9TTr63gE1YcR5idjx8UREz1s0kFhydf3bBLCXyqjhkIc6igAzTOx3zPifCwFR87tsh/4Pa9cNts6d2otw==",
"peerDependencies": {
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0"
},
@@ -106,12 +111,12 @@
}
},
"node_modules/@babel/generator": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz",
"integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==",
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz",
"integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==",
"dependencies": {
"@babel/parser": "^7.28.0",
"@babel/types": "^7.28.0",
"@babel/parser": "^7.28.3",
"@babel/types": "^7.28.2",
"@jridgewell/gen-mapping": "^0.3.12",
"@jridgewell/trace-mapping": "^0.3.28",
"jsesc": "^3.0.2"
@@ -147,16 +152,16 @@
}
},
"node_modules/@babel/helper-create-class-features-plugin": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz",
"integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==",
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz",
"integrity": "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.27.1",
"@babel/helper-annotate-as-pure": "^7.27.3",
"@babel/helper-member-expression-to-functions": "^7.27.1",
"@babel/helper-optimise-call-expression": "^7.27.1",
"@babel/helper-replace-supers": "^7.27.1",
"@babel/helper-skip-transparent-expression-wrappers": "^7.27.1",
"@babel/traverse": "^7.27.1",
"@babel/traverse": "^7.28.3",
"semver": "^6.3.1"
},
"engines": {
@@ -333,13 +338,13 @@
}
},
"node_modules/@babel/helper-wrap-function": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.27.1.tgz",
"integrity": "sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==",
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.3.tgz",
"integrity": "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g==",
"dependencies": {
"@babel/template": "^7.27.1",
"@babel/traverse": "^7.27.1",
"@babel/types": "^7.27.1"
"@babel/template": "^7.27.2",
"@babel/traverse": "^7.28.3",
"@babel/types": "^7.28.2"
},
"engines": {
"node": ">=6.9.0"
@@ -436,11 +441,11 @@
}
},
"node_modules/@babel/parser": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz",
"integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==",
"version": "7.28.4",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz",
"integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==",
"dependencies": {
"@babel/types": "^7.28.0"
"@babel/types": "^7.28.4"
},
"bin": {
"parser": "bin/babel-parser.js"
@@ -784,9 +789,9 @@
}
},
"node_modules/@babel/plugin-transform-block-scoping": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.0.tgz",
"integrity": "sha512-gKKnwjpdx5sER/wl0WN0efUBFzF/56YZO0RJrSYP4CljXnP31ByY7fol89AzomdlLNzI36AvOTmYHsnZTCkq8Q==",
"version": "7.28.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.4.tgz",
"integrity": "sha512-1yxmvN0MJHOhPVmAsmoW5liWwoILobu/d/ShymZmj867bAdxGbehIrew1DuLpw2Ukv+qDSSPQdYW1dLNE7t11A==",
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -813,16 +818,16 @@
}
},
"node_modules/@babel/plugin-transform-classes": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.0.tgz",
"integrity": "sha512-IjM1IoJNw72AZFlj33Cu8X0q2XK/6AaVC3jQu+cgQ5lThWD5ajnuUAml80dqRmOhmPkTH8uAwnpMu9Rvj0LTRA==",
"version": "7.28.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.4.tgz",
"integrity": "sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.27.3",
"@babel/helper-compilation-targets": "^7.27.2",
"@babel/helper-globals": "^7.28.0",
"@babel/helper-plugin-utils": "^7.27.1",
"@babel/helper-replace-supers": "^7.27.1",
"@babel/traverse": "^7.28.0"
"@babel/traverse": "^7.28.4"
},
"engines": {
"node": ">=6.9.0"
@@ -1008,15 +1013,15 @@
}
},
"node_modules/@babel/plugin-transform-object-rest-spread": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.0.tgz",
"integrity": "sha512-9VNGikXxzu5eCiQjdE4IZn8sb9q7Xsk5EXLDBKUYg1e/Tve8/05+KJEtcxGxAgCY5t/BpKQM+JEL/yT4tvgiUA==",
"version": "7.28.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.4.tgz",
"integrity": "sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew==",
"dependencies": {
"@babel/helper-compilation-targets": "^7.27.2",
"@babel/helper-plugin-utils": "^7.27.1",
"@babel/plugin-transform-destructuring": "^7.28.0",
"@babel/plugin-transform-parameters": "^7.27.7",
"@babel/traverse": "^7.28.0"
"@babel/traverse": "^7.28.4"
},
"engines": {
"node": ">=6.9.0"
@@ -1189,9 +1194,9 @@
}
},
"node_modules/@babel/plugin-transform-regenerator": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.0.tgz",
"integrity": "sha512-LOAozRVbqxEVjSKfhGnuLoE4Kz4Oc5UJzuvFUhSsQzdCdaAQu06mG8zDv2GFSerM62nImUZ7K92vxnQcLSDlCQ==",
"version": "7.28.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.4.tgz",
"integrity": "sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA==",
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1203,9 +1208,9 @@
}
},
"node_modules/@babel/plugin-transform-runtime": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.28.0.tgz",
"integrity": "sha512-dGopk9nZrtCs2+nfIem25UuHyt5moSJamArzIoh9/vezUQPmYDOzjaHDCkAzuGJibCIkPup8rMT2+wYB6S73cA==",
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.28.3.tgz",
"integrity": "sha512-Y6ab1kGqZ0u42Zv/4a7l0l72n9DKP/MKoKWaUSBylrhNZO2prYuqFOLbn5aW5SIFXwSH93yfjbgllL8lxuGKLg==",
"dependencies": {
"@babel/helper-module-imports": "^7.27.1",
"@babel/helper-plugin-utils": "^7.27.1",
@@ -1356,16 +1361,16 @@
}
},
"node_modules/@babel/traverse": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz",
"integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==",
"version": "7.28.4",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz",
"integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==",
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.0",
"@babel/generator": "^7.28.3",
"@babel/helper-globals": "^7.28.0",
"@babel/parser": "^7.28.0",
"@babel/parser": "^7.28.4",
"@babel/template": "^7.27.2",
"@babel/types": "^7.28.0",
"@babel/types": "^7.28.4",
"debug": "^4.3.1"
},
"engines": {
@@ -1391,9 +1396,9 @@
}
},
"node_modules/@babel/types": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.0.tgz",
"integrity": "sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==",
"version": "7.28.4",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz",
"integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==",
"dependencies": {
"@babel/helper-string-parser": "^7.27.1",
"@babel/helper-validator-identifier": "^7.27.1"
@@ -1406,7 +1411,6 @@
"version": "2.0.17",
"resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz",
"integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==",
"peer": true,
"dependencies": {
"@types/hammerjs": "^2.0.36"
},
@@ -1415,9 +1419,9 @@
}
},
"node_modules/@expo/cli": {
"version": "0.24.20",
"resolved": "https://registry.npmjs.org/@expo/cli/-/cli-0.24.20.tgz",
"integrity": "sha512-uF1pOVcd+xizNtVTuZqNGzy7I6IJon5YMmQidsURds1Ww96AFDxrR/NEACqeATNAmY60m8wy1VZZpSg5zLNkpw==",
"version": "0.24.21",
"resolved": "https://registry.npmjs.org/@expo/cli/-/cli-0.24.21.tgz",
"integrity": "sha512-DT6K9vgFHqqWL/19mU1ofRcPoO1pn4qmgi76GtuiNU4tbBe/02mRHwFsQw7qRfFAT28If5e/wiwVozgSuZVL8g==",
"dependencies": {
"@0no-co/graphql.web": "^1.0.8",
"@babel/runtime": "^7.20.0",
@@ -1433,10 +1437,11 @@
"@expo/package-manager": "^1.8.6",
"@expo/plist": "^0.3.5",
"@expo/prebuild-config": "^9.0.11",
"@expo/schema-utils": "^0.1.0",
"@expo/spawn-async": "^1.7.2",
"@expo/ws-tunnel": "^1.0.1",
"@expo/xcpretty": "^4.3.0",
"@react-native/dev-middleware": "0.79.5",
"@react-native/dev-middleware": "0.79.6",
"@urql/core": "^5.0.6",
"@urql/exchange-retry": "^1.3.0",
"accepts": "^1.3.8",
@@ -1485,6 +1490,56 @@
"expo-internal": "build/bin/cli"
}
},
"node_modules/@expo/cli/node_modules/@react-native/debugger-frontend": {
"version": "0.79.6",
"resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.79.6.tgz",
"integrity": "sha512-lIK/KkaH7ueM22bLO0YNaQwZbT/oeqhaghOvmZacaNVbJR1Cdh/XAqjT8FgCS+7PUnbxA8B55NYNKGZG3O2pYw==",
"engines": {
"node": ">=18"
}
},
"node_modules/@expo/cli/node_modules/@react-native/dev-middleware": {
"version": "0.79.6",
"resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.79.6.tgz",
"integrity": "sha512-BK3GZBa9c7XSNR27EDRtxrgyyA3/mf1j3/y+mPk7Ac0Myu85YNrXnC9g3mL5Ytwo0g58TKrAIgs1fF2Q5Mn6mQ==",
"dependencies": {
"@isaacs/ttlcache": "^1.4.1",
"@react-native/debugger-frontend": "0.79.6",
"chrome-launcher": "^0.15.2",
"chromium-edge-launcher": "^0.2.0",
"connect": "^3.6.5",
"debug": "^2.2.0",
"invariant": "^2.2.4",
"nullthrows": "^1.1.1",
"open": "^7.0.3",
"serve-static": "^1.16.2",
"ws": "^6.2.3"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@expo/cli/node_modules/@react-native/dev-middleware/node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/@expo/cli/node_modules/@react-native/dev-middleware/node_modules/ws": {
"version": "6.2.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz",
"integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==",
"dependencies": {
"async-limiter": "~1.0.0"
}
},
"node_modules/@expo/cli/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/@expo/cli/node_modules/semver": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
@@ -1715,9 +1770,9 @@
}
},
"node_modules/@expo/osascript": {
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/@expo/osascript/-/osascript-2.2.5.tgz",
"integrity": "sha512-Bpp/n5rZ0UmpBOnl7Li3LtM7la0AR3H9NNesqL+ytW5UiqV/TbonYW3rDZY38u4u/lG7TnYflVIVQPD+iqZJ5w==",
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/@expo/osascript/-/osascript-2.3.6.tgz",
"integrity": "sha512-EMi4/YgWN/dHSkE9A4A5EJE8WrGnp71+BcVAKY80KMYtq5s6mOhLyDm8ytvHeUISUrPcPTRZ/6c8JMd6F8Ggmg==",
"dependencies": {
"@expo/spawn-async": "^1.7.2",
"exec-async": "^2.2.0"
@@ -1727,11 +1782,11 @@
}
},
"node_modules/@expo/package-manager": {
"version": "1.8.6",
"resolved": "https://registry.npmjs.org/@expo/package-manager/-/package-manager-1.8.6.tgz",
"integrity": "sha512-gcdICLuL+nHKZagPIDC5tX8UoDDB8vNA5/+SaQEqz8D+T2C4KrEJc2Vi1gPAlDnKif834QS6YluHWyxjk0yZlQ==",
"version": "1.9.7",
"resolved": "https://registry.npmjs.org/@expo/package-manager/-/package-manager-1.9.7.tgz",
"integrity": "sha512-k3uky8Qzlv21rxuPvP2KUTAy8NI0b/LP7BSXcwJpS/rH7RmiAqUXgzPar3I1OmKGgxpod78Y9Mae//F8d3aiOQ==",
"dependencies": {
"@expo/json-file": "^9.1.5",
"@expo/json-file": "^10.0.7",
"@expo/spawn-async": "^1.7.2",
"chalk": "^4.0.0",
"npm-package-arg": "^11.0.0",
@@ -1739,6 +1794,23 @@
"resolve-workspace-root": "^2.0.0"
}
},
"node_modules/@expo/package-manager/node_modules/@babel/code-frame": {
"version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
"integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
"dependencies": {
"@babel/highlight": "^7.10.4"
}
},
"node_modules/@expo/package-manager/node_modules/@expo/json-file": {
"version": "10.0.7",
"resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-10.0.7.tgz",
"integrity": "sha512-z2OTC0XNO6riZu98EjdNHC05l51ySeTto6GP7oSQrCvQgG9ARBwD1YvMQaVZ9wU7p/4LzSf1O7tckL3B45fPpw==",
"dependencies": {
"@babel/code-frame": "~7.10.4",
"json5": "^2.2.3"
}
},
"node_modules/@expo/plist": {
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.3.5.tgz",
@@ -1750,22 +1822,27 @@
}
},
"node_modules/@expo/prebuild-config": {
"version": "9.0.11",
"resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-9.0.11.tgz",
"integrity": "sha512-0DsxhhixRbCCvmYskBTq8czsU0YOBsntYURhWPNpkl0IPVpeP9haE5W4OwtHGzXEbmHdzaoDwNmVcWjS/mqbDw==",
"version": "9.0.12",
"resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-9.0.12.tgz",
"integrity": "sha512-AKH5Scf+gEMgGxZZaimrJI2wlUJlRoqzDNn7/rkhZa5gUTnO4l6slKak2YdaH+nXlOWCNfAQWa76NnpQIfmv6Q==",
"dependencies": {
"@expo/config": "~11.0.13",
"@expo/config-plugins": "~10.1.2",
"@expo/config-types": "^53.0.5",
"@expo/image-utils": "^0.7.6",
"@expo/json-file": "^9.1.5",
"@react-native/normalize-colors": "0.79.5",
"@react-native/normalize-colors": "0.79.6",
"debug": "^4.3.1",
"resolve-from": "^5.0.0",
"semver": "^7.6.0",
"xml2js": "0.6.0"
}
},
"node_modules/@expo/prebuild-config/node_modules/@react-native/normalize-colors": {
"version": "0.79.6",
"resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.79.6.tgz",
"integrity": "sha512-0v2/ruY7eeKun4BeKu+GcfO+SHBdl0LJn4ZFzTzjHdWES0Cn+ONqKljYaIv8p9MV2Hx/kcdEvbY4lWI34jC/mQ=="
},
"node_modules/@expo/prebuild-config/node_modules/semver": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
@@ -1777,6 +1854,11 @@
"node": ">=10"
}
},
"node_modules/@expo/schema-utils": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/@expo/schema-utils/-/schema-utils-0.1.6.tgz",
"integrity": "sha512-arKkNlPZYD0uUxGPbSwV/6v4owmZcUNrXk9Vq/o+p2Eqt1DM9jIjbXwrqlmvkTsPkjUtNLDGB0EDiosVJ4OvDg=="
},
"node_modules/@expo/sdk-runtime-versions": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@expo/sdk-runtime-versions/-/sdk-runtime-versions-1.0.0.tgz",
@@ -2184,21 +2266,81 @@
}
},
"node_modules/@react-native/babel-plugin-codegen": {
"version": "0.79.5",
"resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.79.5.tgz",
"integrity": "sha512-Rt/imdfqXihD/sn0xnV4flxxb1aLLjPtMF1QleQjEhJsTUPpH4TFlfOpoCvsrXoDl4OIcB1k4FVM24Ez92zf5w==",
"version": "0.79.6",
"resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.79.6.tgz",
"integrity": "sha512-CS5OrgcMPixOyUJ/Sk/HSsKsKgyKT5P7y3CojimOQzWqRZBmoQfxdST4ugj7n1H+ebM2IKqbgovApFbqXsoX0g==",
"dependencies": {
"@babel/traverse": "^7.25.3",
"@react-native/codegen": "0.79.5"
"@react-native/codegen": "0.79.6"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@react-native/babel-plugin-codegen/node_modules/@react-native/codegen": {
"version": "0.79.6",
"resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.79.6.tgz",
"integrity": "sha512-iRBX8Lgbqypwnfba7s6opeUwVyaR23mowh9ILw7EcT2oLz3RqMmjJdrbVpWhGSMGq2qkPfqAH7bhO8C7O+xfjQ==",
"dependencies": {
"@babel/core": "^7.25.2",
"@babel/parser": "^7.25.3",
"glob": "^7.1.1",
"hermes-parser": "0.25.1",
"invariant": "^2.2.4",
"nullthrows": "^1.1.1",
"yargs": "^17.6.2"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"@babel/core": "*"
}
},
"node_modules/@react-native/babel-plugin-codegen/node_modules/brace-expansion": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/@react-native/babel-plugin-codegen/node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"deprecated": "Glob versions prior to v9 are no longer supported",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@react-native/babel-plugin-codegen/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/@react-native/babel-preset": {
"version": "0.79.5",
"resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.79.5.tgz",
"integrity": "sha512-GDUYIWslMLbdJHEgKNfrOzXk8EDKxKzbwmBXUugoiSlr6TyepVZsj3GZDLEFarOcTwH1EXXHJsixihk8DCRQDA==",
"version": "0.79.6",
"resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.79.6.tgz",
"integrity": "sha512-H+FRO+r2Ql6b5IwfE0E7D52JhkxjeGSBSUpCXAI5zQ60zSBJ54Hwh2bBJOohXWl4J+C7gKYSAd2JHMUETu+c/A==",
"dependencies": {
"@babel/core": "^7.25.2",
"@babel/plugin-proposal-export-default-from": "^7.24.7",
@@ -2241,7 +2383,7 @@
"@babel/plugin-transform-typescript": "^7.25.2",
"@babel/plugin-transform-unicode-regex": "^7.24.7",
"@babel/template": "^7.25.0",
"@react-native/babel-plugin-codegen": "0.79.5",
"@react-native/babel-plugin-codegen": "0.79.6",
"babel-plugin-syntax-hermes-parser": "0.25.1",
"babel-plugin-transform-flow-enums": "^0.0.2",
"react-refresh": "^0.14.0"
@@ -2602,8 +2744,7 @@
"node_modules/@types/hammerjs": {
"version": "2.0.46",
"resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.46.tgz",
"integrity": "sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==",
"peer": true
"integrity": "sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw=="
},
"node_modules/@types/istanbul-lib-coverage": {
"version": "2.0.6",
@@ -2955,9 +3096,9 @@
}
},
"node_modules/babel-preset-expo": {
"version": "13.2.3",
"resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-13.2.3.tgz",
"integrity": "sha512-wQJn92lqj8GKR7Ojg/aW4+GkqI6ZdDNTDyOqhhl7A9bAqk6t0ukUOWLDXQb4p0qKJjMDV1F6gNWasI2KUbuVTQ==",
"version": "13.2.4",
"resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-13.2.4.tgz",
"integrity": "sha512-3IKORo3KR+4qtLdCkZNDj8KeA43oBn7RRQejFGWfiZgu/NeaRUSri8YwYjZqybm7hn3nmMv9OLahlvXBX23o5Q==",
"dependencies": {
"@babel/helper-module-imports": "^7.25.9",
"@babel/plugin-proposal-decorators": "^7.12.9",
@@ -2973,7 +3114,7 @@
"@babel/plugin-transform-runtime": "^7.24.7",
"@babel/preset-react": "^7.22.15",
"@babel/preset-typescript": "^7.23.0",
"@react-native/babel-preset": "0.79.5",
"@react-native/babel-preset": "0.79.6",
"babel-plugin-react-native-web": "~0.19.13",
"babel-plugin-syntax-hermes-parser": "^0.25.1",
"babel-plugin-transform-flow-enums": "^0.0.2",
@@ -3108,9 +3249,9 @@
}
},
"node_modules/browserslist": {
"version": "4.25.1",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz",
"integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==",
"version": "4.25.4",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.4.tgz",
"integrity": "sha512-4jYpcjabC606xJ3kw2QwGEZKX0Aw7sgQdZCvIK9dhVSPh76BKo+C+btT1RRofH7B+8iNpEbgGNVWiLki5q93yg==",
"funding": [
{
"type": "opencollective",
@@ -3126,8 +3267,8 @@
}
],
"dependencies": {
"caniuse-lite": "^1.0.30001726",
"electron-to-chromium": "^1.5.173",
"caniuse-lite": "^1.0.30001737",
"electron-to-chromium": "^1.5.211",
"node-releases": "^2.0.19",
"update-browserslist-db": "^1.1.3"
},
@@ -3221,9 +3362,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001726",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001726.tgz",
"integrity": "sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw==",
"version": "1.0.30001741",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001741.tgz",
"integrity": "sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw==",
"funding": [
{
"type": "opencollective",
@@ -3435,15 +3576,15 @@
}
},
"node_modules/compression": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz",
"integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==",
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz",
"integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==",
"dependencies": {
"bytes": "3.1.2",
"compressible": "~2.0.18",
"debug": "2.6.9",
"negotiator": "~0.6.4",
"on-headers": "~1.0.2",
"on-headers": "~1.1.0",
"safe-buffer": "5.2.1",
"vary": "~1.1.2"
},
@@ -3510,11 +3651,11 @@
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="
},
"node_modules/core-js-compat": {
"version": "3.43.0",
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.43.0.tgz",
"integrity": "sha512-2GML2ZsCc5LR7hZYz4AXmjQw8zuy2T//2QntwdnpuYI7jteT6GVYJL7F6C2C57R7gSYrcqVW3lAALefdbhBLDA==",
"version": "3.45.1",
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.45.1.tgz",
"integrity": "sha512-tqTt5T4PzsMIZ430XGviK4vzYSoeNJ6CXODi6c/voxOT6IZqBht5/EKaSNnYiEjjRYxjVz7DQIsOsY0XNi8PIA==",
"dependencies": {
"browserslist": "^4.25.0"
"browserslist": "^4.25.3"
},
"funding": {
"type": "opencollective",
@@ -3796,9 +3937,9 @@
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
},
"node_modules/electron-to-chromium": {
"version": "1.5.179",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.179.tgz",
"integrity": "sha512-UWKi/EbBopgfFsc5k61wFpV7WrnnSlSzW/e2XcBmS6qKYTivZlLtoll5/rdqRTxGglGHkmkW0j0pFNJG10EUIQ=="
"version": "1.5.217",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.217.tgz",
"integrity": "sha512-Pludfu5iBxp9XzNl0qq2G87hdD17ZV7h5T4n6rQXDi3nCyloBV3jreE9+8GC6g4X/5yxqVgXEURpcLtM0WS4jA=="
},
"node_modules/emoji-regex": {
"version": "9.2.2",
@@ -3906,25 +4047,25 @@
"integrity": "sha512-87OpwcEiMia/DeiKFzaQNBNFeN3XkkpYIh9FyOqq5mS2oKv3CBE67PXoEKcr6nodWdXNogTiQ0jE2NGuoffXPw=="
},
"node_modules/expo": {
"version": "53.0.19",
"resolved": "https://registry.npmjs.org/expo/-/expo-53.0.19.tgz",
"integrity": "sha512-hZWEKw6h5dlfKy6+c3f2exx5x3Loio8p0b2s/Pk1eQfTffqpkQRVVlOzcTWU1RSuMfc47ZMpr97pUJWQczOGsQ==",
"version": "53.0.22",
"resolved": "https://registry.npmjs.org/expo/-/expo-53.0.22.tgz",
"integrity": "sha512-sJ2I4W/e5iiM4u/wYCe3qmW4D7WPCRqByPDD0hJcdYNdjc9HFFFdO4OAudZVyC/MmtoWZEIH5kTJP1cw9FjzYA==",
"dependencies": {
"@babel/runtime": "^7.20.0",
"@expo/cli": "0.24.20",
"@expo/cli": "0.24.21",
"@expo/config": "~11.0.13",
"@expo/config-plugins": "~10.1.2",
"@expo/fingerprint": "0.13.4",
"@expo/metro-config": "0.20.17",
"@expo/vector-icons": "^14.0.0",
"babel-preset-expo": "~13.2.3",
"babel-preset-expo": "~13.2.4",
"expo-asset": "~11.1.7",
"expo-constants": "~17.1.7",
"expo-file-system": "~18.1.11",
"expo-font": "~13.3.2",
"expo-keep-awake": "~14.1.4",
"expo-modules-autolinking": "2.1.14",
"expo-modules-core": "2.4.2",
"expo-modules-core": "2.5.0",
"react-native-edge-to-edge": "1.6.0",
"whatwg-url-without-unicode": "8.0.0-3"
},
@@ -4046,13 +4187,171 @@
}
},
"node_modules/expo-modules-core": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-2.4.2.tgz",
"integrity": "sha512-RCb0wniYCJkxwpXrkiBA/WiNGxzYsEpL0sB50gTnS/zEfX3DImS2npc4lfZ3hPZo1UF9YC6OSI9Do+iacV0NUg==",
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-2.5.0.tgz",
"integrity": "sha512-aIbQxZE2vdCKsolQUl6Q9Farlf8tjh/ROR4hfN1qT7QBGPl1XrJGnaOKkcgYaGrlzCPg/7IBe0Np67GzKMZKKQ==",
"dependencies": {
"invariant": "^2.2.4"
}
},
"node_modules/expo-splash-screen": {
"version": "31.0.8",
"resolved": "https://registry.npmjs.org/expo-splash-screen/-/expo-splash-screen-31.0.8.tgz",
"integrity": "sha512-NNgNhrqkuJAt98k9CKJ9JeCInv5g/xRhe1fWbciQrqTQWPXeIGg3tn5T5HNFwfRD1aKr2uOs1Ctly8rE6i5vtQ==",
"dependencies": {
"@expo/prebuild-config": "^10.0.8"
},
"peerDependencies": {
"expo": "*"
}
},
"node_modules/expo-splash-screen/node_modules/@babel/code-frame": {
"version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
"integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
"dependencies": {
"@babel/highlight": "^7.10.4"
}
},
"node_modules/expo-splash-screen/node_modules/@expo/config": {
"version": "12.0.8",
"resolved": "https://registry.npmjs.org/@expo/config/-/config-12.0.8.tgz",
"integrity": "sha512-yFadXa5Cmja57EVOSyEYV1hF7kCaSbPnd1twx0MfvTr1Yj2abIbrEu2MUZqcvElNQOtgADnLRP0YJiuEdgoO5A==",
"dependencies": {
"@babel/code-frame": "~7.10.4",
"@expo/config-plugins": "~54.0.0",
"@expo/config-types": "^54.0.7",
"@expo/json-file": "^10.0.7",
"deepmerge": "^4.3.1",
"getenv": "^2.0.0",
"glob": "^10.4.2",
"require-from-string": "^2.0.2",
"resolve-from": "^5.0.0",
"resolve-workspace-root": "^2.0.0",
"semver": "^7.6.0",
"slugify": "^1.3.4",
"sucrase": "3.35.0"
}
},
"node_modules/expo-splash-screen/node_modules/@expo/config-plugins": {
"version": "11.0.7",
"resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-11.0.7.tgz",
"integrity": "sha512-kak5m27fPTzwmzYPbaYL6I67OFnhdrzV0h5JcoljrEC7uM3R18V/RrnEMzv10XQk+s+qmPfMkr0aK9YYgGqR6g==",
"dependencies": {
"@expo/config-types": "^54.0.7",
"@expo/json-file": "~10.0.6",
"@expo/plist": "^0.4.6",
"@expo/sdk-runtime-versions": "^1.0.0",
"chalk": "^4.1.2",
"debug": "^4.3.5",
"getenv": "^2.0.0",
"glob": "^10.4.2",
"resolve-from": "^5.0.0",
"semver": "^7.5.4",
"slash": "^3.0.0",
"slugify": "^1.6.6",
"xcode": "^3.0.1",
"xml2js": "0.6.0"
}
},
"node_modules/expo-splash-screen/node_modules/@expo/config-types": {
"version": "54.0.7",
"resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-54.0.7.tgz",
"integrity": "sha512-f0UehgPd2gUqUtQ6euHAL6MqTT/A07r847Ztw2yZYWTUr0hRZr4nCP4U+lr8/pPtsHQYMKoPB1mOeAaTO25ruw=="
},
"node_modules/expo-splash-screen/node_modules/@expo/config/node_modules/@expo/config-plugins": {
"version": "54.0.0",
"resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-54.0.0.tgz",
"integrity": "sha512-b2yFNKwaiHjDfh7K+zhMvRKA09gdaP/raqIzB112qeacVJaT66vka8m6cffYbbiXMe1srqofSJvyvr+g3H6+nA==",
"dependencies": {
"@expo/config-types": "^54.0.7",
"@expo/json-file": "~10.0.6",
"@expo/plist": "^0.4.6",
"@expo/sdk-runtime-versions": "^1.0.0",
"chalk": "^4.1.2",
"debug": "^4.3.5",
"getenv": "^2.0.0",
"glob": "^10.4.2",
"resolve-from": "^5.0.0",
"semver": "^7.5.4",
"slash": "^3.0.0",
"slugify": "^1.6.6",
"xcode": "^3.0.1",
"xml2js": "0.6.0"
}
},
"node_modules/expo-splash-screen/node_modules/@expo/image-utils": {
"version": "0.8.7",
"resolved": "https://registry.npmjs.org/@expo/image-utils/-/image-utils-0.8.7.tgz",
"integrity": "sha512-SXOww4Wq3RVXLyOaXiCCuQFguCDh8mmaHBv54h/R29wGl4jRY8GEyQEx8SypV/iHt1FbzsU/X3Qbcd9afm2W2w==",
"dependencies": {
"@expo/spawn-async": "^1.7.2",
"chalk": "^4.0.0",
"getenv": "^2.0.0",
"jimp-compact": "0.16.1",
"parse-png": "^2.1.0",
"resolve-from": "^5.0.0",
"resolve-global": "^1.0.0",
"semver": "^7.6.0",
"temp-dir": "~2.0.0",
"unique-string": "~2.0.0"
}
},
"node_modules/expo-splash-screen/node_modules/@expo/json-file": {
"version": "10.0.7",
"resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-10.0.7.tgz",
"integrity": "sha512-z2OTC0XNO6riZu98EjdNHC05l51ySeTto6GP7oSQrCvQgG9ARBwD1YvMQaVZ9wU7p/4LzSf1O7tckL3B45fPpw==",
"dependencies": {
"@babel/code-frame": "~7.10.4",
"json5": "^2.2.3"
}
},
"node_modules/expo-splash-screen/node_modules/@expo/plist": {
"version": "0.4.6",
"resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.4.6.tgz",
"integrity": "sha512-6yklhtUWohs1rBSC8dGyBBpElEbosjXN0zJN/+1/B121n7pPWvd9y/UGJm+2x7b81VnW3AHmWVnbU/u0INQsqA==",
"dependencies": {
"@xmldom/xmldom": "^0.8.8",
"base64-js": "^1.2.3",
"xmlbuilder": "^15.1.1"
}
},
"node_modules/expo-splash-screen/node_modules/@expo/prebuild-config": {
"version": "10.0.8",
"resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-10.0.8.tgz",
"integrity": "sha512-9ibcRuWngmMnoYe25XXfZEWcPCdx6LiyzqHqpojsvHBI+sMsyZPf4b/5y/zmeJy3PKjR4LSzMRonEitTfUSL/A==",
"dependencies": {
"@expo/config": "~12.0.7",
"@expo/config-plugins": "~11.0.7",
"@expo/config-types": "^54.0.7",
"@expo/image-utils": "^0.8.6",
"@expo/json-file": "^10.0.6",
"@react-native/normalize-colors": "0.81.1",
"debug": "^4.3.1",
"resolve-from": "^5.0.0",
"semver": "^7.6.0",
"xml2js": "0.6.0"
},
"peerDependencies": {
"expo": "*"
}
},
"node_modules/expo-splash-screen/node_modules/@react-native/normalize-colors": {
"version": "0.81.1",
"resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.81.1.tgz",
"integrity": "sha512-TsaeZlE8OYFy3PSWc+1VBmAzI2T3kInzqxmwXoGU4w1d4XFkQFg271Ja9GmDi9cqV3CnBfqoF9VPwRxVlc/l5g=="
},
"node_modules/expo-splash-screen/node_modules/semver": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/expo-status-bar": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/expo-status-bar/-/expo-status-bar-2.2.3.tgz",
@@ -4066,6 +4365,32 @@
"react-native": "*"
}
},
"node_modules/expo-system-ui": {
"version": "5.0.11",
"resolved": "https://registry.npmjs.org/expo-system-ui/-/expo-system-ui-5.0.11.tgz",
"integrity": "sha512-PG5VdaG5cwBe1Rj02mJdnsihKl9Iw/w/a6+qh2mH3f2K/IvQ+Hf7aG2kavSADtkGNCNj7CEIg7Rn4DQz/SE5rQ==",
"license": "MIT",
"dependencies": {
"@react-native/normalize-colors": "0.79.6",
"debug": "^4.3.2"
},
"peerDependencies": {
"expo": "*",
"react-native": "*",
"react-native-web": "*"
},
"peerDependenciesMeta": {
"react-native-web": {
"optional": true
}
}
},
"node_modules/expo-system-ui/node_modules/@react-native/normalize-colors": {
"version": "0.79.6",
"resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.79.6.tgz",
"integrity": "sha512-0v2/ruY7eeKun4BeKu+GcfO+SHBdl0LJn4ZFzTzjHdWES0Cn+ONqKljYaIv8p9MV2Hx/kcdEvbY4lWI34jC/mQ==",
"license": "MIT"
},
"node_modules/exponential-backoff": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.2.tgz",
@@ -4271,6 +4596,17 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/global-dirs": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz",
"integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==",
"dependencies": {
"ini": "^1.3.4"
},
"engines": {
"node": ">=4"
}
},
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
@@ -4312,7 +4648,6 @@
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
"peer": true,
"dependencies": {
"react-is": "^16.7.0"
}
@@ -4320,8 +4655,7 @@
"node_modules/hoist-non-react-statics/node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"peer": true
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/hosted-git-info": {
"version": "7.0.2",
@@ -5807,9 +6141,9 @@
}
},
"node_modules/on-headers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
"integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
"engines": {
"node": ">= 0.8"
}
@@ -6400,6 +6734,23 @@
}
}
},
"node_modules/react-native-confirmation-code-field": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/react-native-confirmation-code-field/-/react-native-confirmation-code-field-8.0.1.tgz",
"integrity": "sha512-pwrhQtspFEp0y/gxZRp8hrra+UjAYfwAsW5Sn3yYtdUZVyRjAZPBnUb3rOc+z5L5o2ml03z5P3uvTK0yQwXsmA==",
"peerDependencies": {
"react": ">=16.4.0",
"react-native": ">=0.64.0"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"react-native": {
"optional": true
}
}
},
"node_modules/react-native-edge-to-edge": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/react-native-edge-to-edge/-/react-native-edge-to-edge-1.6.0.tgz",
@@ -6410,10 +6761,9 @@
}
},
"node_modules/react-native-gesture-handler": {
"version": "2.27.1",
"resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.27.1.tgz",
"integrity": "sha512-57TUWerhdz589OcDD21e/YlL923Ma4OIpyWsP0hy7gItBCPm5d7qIUW7Yo/cS2wo1qDdOhJaNlvlBD1lDou1fA==",
"peer": true,
"version": "2.24.0",
"resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.24.0.tgz",
"integrity": "sha512-ZdWyOd1C8axKJHIfYxjJKCcxjWEpUtUWgTOVY2wynbiveSQDm8X/PDyAKXSer/GOtIpjudUbACOndZXCN3vHsw==",
"dependencies": {
"@egjs/hammerjs": "^2.0.17",
"hoist-non-react-statics": "^3.3.0",
@@ -6598,9 +6948,9 @@
"integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A=="
},
"node_modules/regenerate-unicode-properties": {
"version": "10.2.0",
"resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz",
"integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==",
"version": "10.2.2",
"resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz",
"integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==",
"dependencies": {
"regenerate": "^1.4.2"
},
@@ -6614,16 +6964,16 @@
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
},
"node_modules/regexpu-core": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz",
"integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==",
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.3.1.tgz",
"integrity": "sha512-DzcswPr252wEr7Qz8AyAVbfyBDKLoYp6eRA1We2Fa9qirRFSdtkP5sHr3yglDKy2BbA0fd2T+j/CUSKes3FeVQ==",
"dependencies": {
"regenerate": "^1.4.2",
"regenerate-unicode-properties": "^10.2.0",
"regenerate-unicode-properties": "^10.2.2",
"regjsgen": "^0.8.0",
"regjsparser": "^0.12.0",
"unicode-match-property-ecmascript": "^2.0.0",
"unicode-match-property-value-ecmascript": "^2.1.0"
"unicode-match-property-value-ecmascript": "^2.2.1"
},
"engines": {
"node": ">=4"
@@ -6720,6 +7070,17 @@
"node": ">=8"
}
},
"node_modules/resolve-global": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/resolve-global/-/resolve-global-1.0.0.tgz",
"integrity": "sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==",
"dependencies": {
"global-dirs": "^0.1.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/resolve-workspace-root": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/resolve-workspace-root/-/resolve-workspace-root-2.0.0.tgz",
@@ -7602,9 +7963,9 @@
}
},
"node_modules/unicode-match-property-value-ecmascript": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz",
"integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==",
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz",
"integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==",
"engines": {
"node": ">=4"
}

View File

@@ -16,11 +16,16 @@
"@react-navigation/bottom-tabs": "^7.4.2",
"@react-navigation/native": "^7.1.14",
"@react-navigation/stack": "^7.4.2",
"expo": "53.0.19",
"expo": "53.0.22",
"expo-font": "~13.3.2",
"expo-image-picker": "~16.1.4",
"expo-splash-screen": "^31.0.8",
"expo-status-bar": "~2.2.3",
"expo-system-ui": "~5.0.11",
"react": "19.0.0",
"react-native": "0.79.5",
"react-native-confirmation-code-field": "^8.0.1",
"react-native-gesture-handler": "~2.24.0",
"react-native-modal-datetime-picker": "^15.0.1",
"react-native-safe-area-context": "5.4.0",
"react-native-screens": "^4.11.1",

View File

@@ -104,13 +104,9 @@ const EditProfileModal = ({
}
// Card year validation (optional, 4 digits and reasonable year)
if (formData.card_year) {
const year = parseInt(formData.card_year.trim(), 10);
const currentYear = new Date().getFullYear();
if (!/^[0-9]{4}$/.test(formData.card_year.trim()) || year < currentYear - 10 || year > currentYear + 10) {
newErrors.card_year = `Ýyl ${currentYear - 10}-den ${currentYear + 10}-e çenli bolmaly`;
}
}
// if (formData.card_year) {
// }
setErrors(newErrors);
return Object.keys(newErrors).length === 0;

View File

@@ -0,0 +1,132 @@
import React from 'react';
import { View, Text, FlatList, StyleSheet } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { COLORS } from '../constants/colors';
const TransactionList = ({ transactions }) => {
if (!transactions || transactions.length === 0) {
return (
<View style={styles.container}>
<Text style={styles.emptyText}>Soňky 10 günde kart hereketleri ýok...</Text>
</View>
);
}
const groupedTransactions = transactions.reduce((acc, transaction) => {
const date = new Date(transaction.date).toLocaleDateString('tk', {
year: 'numeric',
month: 'long',
day: 'numeric',
});
if (!acc[date]) {
acc[date] = [];
}
acc[date].push(transaction);
return acc;
}, {});
const renderTransactionItem = ({ item }) => (
<View style={styles.transactionItem}>
<View style={styles.transactionIcon}>
<Ionicons
name={item.type === 'credit' ? 'arrow-down' : 'arrow-up'}
size={20}
color={item.type === 'credit' ? COLORS.success : COLORS.danger}
/>
</View>
<View style={styles.transactionDetails}>
<Text style={styles.transactionTitle}>{item.description}</Text>
<Text style={styles.transactionDate}>{item.time ?? '-'}</Text>
</View>
<Text
style={[
styles.transactionAmount,
{ color: item.type === 'credit' ? COLORS.success : COLORS.textPrimary },
]}
>
{item.type === 'credit' ? '+' : '-'}
{item.amount} {item.currency}
</Text>
</View>
);
const renderTransactionSection = ({ item: date }) => (
<View>
<Text style={styles.transactionDateHeader}>{date}</Text>
<FlatList
data={groupedTransactions[date]}
renderItem={renderTransactionItem}
keyExtractor={(item) => item.id?.toString()}
scrollEnabled={false}
ItemSeparatorComponent={() => <View style={styles.separator} />}
/>
</View>
);
return (
<FlatList
data={Object.keys(groupedTransactions)}
renderItem={renderTransactionSection}
keyExtractor={(date) => date}
scrollEnabled={false}
ItemSeparatorComponent={() => <View style={styles.separator} />}
/>
);
};
const styles = StyleSheet.create({
container: {
padding: 16,
},
emptyText: {
textAlign: 'center',
color: COLORS.textSecondary,
},
transactionItem: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 12,
},
transactionIcon: {
width: 44,
height: 44,
borderRadius: 22,
backgroundColor: COLORS.background,
alignItems: 'center',
justifyContent: 'center',
marginRight: 16,
},
transactionDetails: {
flex: 1,
},
transactionTitle: {
fontSize: 16,
fontWeight: '600',
color: COLORS.textPrimary,
marginBottom: 2,
},
transactionDate: {
fontSize: 13,
color: COLORS.textSecondary,
},
transactionAmount: {
fontSize: 16,
fontWeight: '700',
},
transactionDateHeader: {
fontSize: 14,
fontWeight: '600',
color: COLORS.textSecondary,
backgroundColor: COLORS.white,
paddingTop: 16,
paddingBottom: 8,
paddingHorizontal: 4,
},
separator: {
height: 1,
backgroundColor: COLORS.gray[200],
marginLeft: 60,
},
});
export default TransactionList;

View File

@@ -2,7 +2,8 @@ import React from 'react';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { Ionicons } from '@expo/vector-icons';
import { COLORS } from '../constants/colors';
import { View, ActivityIndicator, Platform } from 'react-native';
import { View, ActivityIndicator, Platform, OS } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import HomeScreen from '../screens/Main/HomeScreen';
import MenuNavigator from './MenuNavigator';
@@ -11,6 +12,8 @@ import ProfileScreen from '../screens/Main/ProfileScreen';
const Tab = createBottomTabNavigator();
const MainNavigator = () => {
const insets = useSafeAreaInsets();
return (
<Tab.Navigator
screenOptions={({ route }) => ({
@@ -34,9 +37,17 @@ const MainNavigator = () => {
backgroundColor: COLORS.white,
borderTopWidth: 1,
borderTopColor: COLORS.gray[200],
paddingBottom: Platform.OS === 'android' ? 16 : 8,
paddingBottom: (insets.bottom || 16),
paddingTop: 8,
height: Platform.OS === 'android' ? 96 : 88,
height: Platform.OS === 'ios' ? 100 : (82 + (insets.bottom || 16)),
elevation: 8,
shadowColor: COLORS.gray[900],
shadowOffset: {
width: 0,
height: -2,
},
shadowOpacity: 0.1,
shadowRadius: 4,
},
tabBarLabelStyle: {
fontSize: 12,
@@ -44,7 +55,7 @@ const MainNavigator = () => {
marginTop: 4,
},
tabBarItemStyle: {
paddingVertical: 4,
paddingVertical: 2,
},
})}
>

View File

@@ -29,34 +29,32 @@ import CardOrderDetailsScreen from '../screens/Card/CardOrderDetailsScreen';
const Stack = createStackNavigator();
const MenuNavigator = () => (
<SafeAreaView style={{ flex: 1 }}>
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name="MenuHome" component={MenuScreen} />
<Stack.Screen name="LoanRemainingOrders" component={LoanRemainingOrdersScreen} />
<Stack.Screen name="CreateLoanRemainingOrder" component={CreateLoanRemainingOrderScreen} />
<Stack.Screen name="LoanPaidOffLetterOrders" component={LoanPaidOffLetterOrdersScreen} />
<Stack.Screen name="CreateLoanPaidOffLetterOrder" component={CreateLoanPaidOffLetterOrderScreen} />
<Stack.Screen name="LoanPaidOffLetterOrderDetails" component={LoanPaidOffLetterOrderDetailsScreen} />
<Stack.Screen name="LoanOrders" component={LoanOrdersScreen} />
<Stack.Screen name="CreateLoanOrder" component={CreateLoanOrderScreen} />
<Stack.Screen name="LoanOrderDetails" component={LoanOrderDetailsScreen} />
<Stack.Screen name="CardTransactionOrders" component={CardTransactionOrdersScreen} />
<Stack.Screen name="CreateCardTransactionOrder" component={CreateCardTransactionOrderScreen} />
<Stack.Screen name="CardTransactionOrderDetails" component={CardTransactionOrderDetailsScreen} />
<Stack.Screen name="CardBalanceOrders" component={CardBalanceOrdersScreen} />
<Stack.Screen name="CreateCardBalanceOrder" component={CreateCardBalanceOrderScreen} />
<Stack.Screen name="CardBalanceOrderDetails" component={CardBalanceOrderDetailsScreen} />
<Stack.Screen name="CardRequisiteOrders" component={CardRequisiteOrdersScreen} />
<Stack.Screen name="CreateCardRequisiteOrder" component={CreateCardRequisiteOrderScreen} />
<Stack.Screen name="CardRequisiteOrderDetails" component={CardRequisiteOrderDetailsScreen} />
<Stack.Screen name="CardPinOrders" component={CardPinOrdersScreen} />
<Stack.Screen name="CreateCardPinOrder" component={CreateCardPinOrderScreen} />
<Stack.Screen name="CardPinOrderDetails" component={CardPinOrderDetailsScreen} />
<Stack.Screen name="CardOrders" component={CardOrdersScreen} />
<Stack.Screen name="CreateCardOrder" component={CreateCardOrderScreen} />
<Stack.Screen name="CardOrderDetails" component={CardOrderDetailsScreen} />
</Stack.Navigator>
</SafeAreaView>
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name="MenuHome" component={MenuScreen} />
<Stack.Screen name="LoanRemainingOrders" component={LoanRemainingOrdersScreen} />
<Stack.Screen name="CreateLoanRemainingOrder" component={CreateLoanRemainingOrderScreen} />
<Stack.Screen name="LoanPaidOffLetterOrders" component={LoanPaidOffLetterOrdersScreen} />
<Stack.Screen name="CreateLoanPaidOffLetterOrder" component={CreateLoanPaidOffLetterOrderScreen} />
<Stack.Screen name="LoanPaidOffLetterOrderDetails" component={LoanPaidOffLetterOrderDetailsScreen} />
<Stack.Screen name="LoanOrders" component={LoanOrdersScreen} />
<Stack.Screen name="CreateLoanOrder" component={CreateLoanOrderScreen} />
<Stack.Screen name="LoanOrderDetails" component={LoanOrderDetailsScreen} />
<Stack.Screen name="CardTransactionOrders" component={CardTransactionOrdersScreen} />
<Stack.Screen name="CreateCardTransactionOrder" component={CreateCardTransactionOrderScreen} />
<Stack.Screen name="CardTransactionOrderDetails" component={CardTransactionOrderDetailsScreen} />
<Stack.Screen name="CardBalanceOrders" component={CardBalanceOrdersScreen} />
<Stack.Screen name="CreateCardBalanceOrder" component={CreateCardBalanceOrderScreen} />
<Stack.Screen name="CardBalanceOrderDetails" component={CardBalanceOrderDetailsScreen} />
<Stack.Screen name="CardRequisiteOrders" component={CardRequisiteOrdersScreen} />
<Stack.Screen name="CreateCardRequisiteOrder" component={CreateCardRequisiteOrderScreen} />
<Stack.Screen name="CardRequisiteOrderDetails" component={CardRequisiteOrderDetailsScreen} />
<Stack.Screen name="CardPinOrders" component={CardPinOrdersScreen} />
<Stack.Screen name="CreateCardPinOrder" component={CreateCardPinOrderScreen} />
<Stack.Screen name="CardPinOrderDetails" component={CardPinOrderDetailsScreen} />
<Stack.Screen name="CardOrders" component={CardOrdersScreen} />
<Stack.Screen name="CreateCardOrder" component={CreateCardOrderScreen} />
<Stack.Screen name="CardOrderDetails" component={CardOrderDetailsScreen} />
</Stack.Navigator>
);
export default MenuNavigator;

View File

@@ -3,7 +3,6 @@ import {
View,
Text,
StyleSheet,
SafeAreaView,
KeyboardAvoidingView,
ScrollView,
Platform,
@@ -12,6 +11,7 @@ import {
TouchableWithoutFeedback,
Keyboard,
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { StatusBar } from 'expo-status-bar';
import { useAuth } from '../../contexts/AuthContext';
import Button from '../../components/Button';

View File

@@ -3,7 +3,6 @@ import {
View,
Text,
StyleSheet,
SafeAreaView,
KeyboardAvoidingView,
ScrollView,
Platform,
@@ -11,6 +10,7 @@ import {
TouchableWithoutFeedback,
Keyboard,
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { StatusBar } from 'expo-status-bar';
import { useAuth } from '../../contexts/AuthContext';
import Button from '../../components/Button';

View File

@@ -3,27 +3,40 @@ import {
View,
Text,
StyleSheet,
SafeAreaView,
Alert,
TouchableOpacity,
TouchableWithoutFeedback,
Keyboard,
BackHandler,
Platform,
KeyboardAvoidingView,
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { StatusBar } from 'expo-status-bar';
import { useFocusEffect } from '@react-navigation/native';
import { useAuth } from '../../contexts/AuthContext';
import Button from '../../components/Button';
import Input from '../../components/Input';
import {
CodeField,
Cursor,
useBlurOnFulfill,
useClearByFocusCell,
} from 'react-native-confirmation-code-field';
import { COLORS } from '../../constants/colors';
const CELL_COUNT = 6;
const VerificationScreen = ({ navigation }) => {
const [code, setCode] = useState('');
const [countdown, setCountdown] = useState(60);
const [canResend, setCanResend] = useState(false);
const { verify, isLoading, pendingVerification, clearPendingVerification } = useAuth();
const countdownRef = useRef(null);
const ref = useBlurOnFulfill({ value: code, cellCount: CELL_COUNT });
const [props, getCellOnLayoutHandler] = useClearByFocusCell({
value: code,
setValue: setCode,
});
useEffect(() => {
if (!pendingVerification) {
@@ -73,18 +86,18 @@ const VerificationScreen = ({ navigation }) => {
}, 1000);
};
const handleVerify = async () => {
if (!code.trim()) {
const handleVerify = async (filledCode) => {
if (!filledCode.trim()) {
Alert.alert('Ýalňyşlyk', 'Tassyklama koduny giriziň');
return;
}
if (!/^\d{6}$/.test(code.trim())) {
if (!/^\d{6}$/.test(filledCode.trim())) {
Alert.alert('Ýalňyşlyk', 'Tassyklama kody 6 sanly bolmaly');
return;
}
const result = await verify(code.trim());
const result = await verify(filledCode.trim());
if (result.success) {
// Navigation will be handled by AuthContext
@@ -93,6 +106,8 @@ const VerificationScreen = ({ navigation }) => {
}
};
const handleVerifyWrapper = () => handleVerify(code);
const handleResendCode = async () => {
if (!canResend) return;
@@ -134,54 +149,66 @@ const VerificationScreen = ({ navigation }) => {
</TouchableOpacity>
</View>
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View style={styles.content}>
<View style={styles.logoContainer}>
<View style={styles.verificationIcon}>
<Text style={styles.verificationIconText}></Text>
<KeyboardAvoidingView
style={{ flex: 1 }}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
>
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View style={styles.content}>
<View style={styles.logoContainer}>
<View style={styles.verificationIcon}>
<Text style={styles.verificationIconText}></Text>
</View>
<Text style={styles.title}>Tassyklama</Text>
<Text style={styles.subtitle}>
{formatPhoneNumber(pendingVerification.phone)} belgisine iberilen
6 sanly tassyklama koduny giriziň
</Text>
</View>
<Text style={styles.title}>Tassyklama</Text>
<Text style={styles.subtitle}>
{formatPhoneNumber(pendingVerification.phone)} belgisine iberilen
6 sanly tassyklama koduny giriziň
</Text>
</View>
<View style={styles.formContainer}>
<Input
label="Tassyklama kody"
value={code}
onChangeText={setCode}
placeholder="123456"
keyboardType="numeric"
maxLength={6}
style={styles.codeInput}
textAlign="center"
returnKeyType="done"
onSubmitEditing={handleVerify}
/>
<View style={styles.formContainer}>
<CodeField
ref={ref}
{...props}
value={code}
onChangeText={setCode}
cellCount={CELL_COUNT}
rootStyle={styles.otpContainer}
keyboardType="number-pad"
textContentType="oneTimeCode"
renderCell={({ index, symbol, isFocused }) => (
<Text
key={index}
style={[styles.otpInput, isFocused && styles.otpInputHighlight]}
onLayout={getCellOnLayoutHandler(index)}>
{symbol || (isFocused ? <Cursor/> : null)}
</Text>
)}
onSubmitEditing={handleVerifyWrapper}
/>
<Button
title="Tassykla"
onPress={handleVerify}
loading={isLoading}
style={styles.verifyButton}
/>
<Button
title="Tassykla"
onPress={handleVerifyWrapper}
loading={isLoading}
style={styles.verifyButton}
/>
<View style={styles.resendContainer}>
{canResend ? (
<TouchableOpacity onPress={handleResendCode}>
<Text style={styles.resendText}>Kodu gaýtadan iber</Text>
</TouchableOpacity>
) : (
<Text style={styles.countdownText}>
Gaýtadan ibermek üçin {countdown} sekunt garaşyň
</Text>
)}
<View style={styles.resendContainer}>
{canResend ? (
<TouchableOpacity onPress={handleResendCode}>
<Text style={styles.resendText}>Kodu gaýtadan iber</Text>
</TouchableOpacity>
) : (
<Text style={styles.countdownText}>
Gaýtadan ibermek üçin {countdown} sekunt garaşyň
</Text>
)}
</View>
</View>
</View>
</View>
</TouchableWithoutFeedback>
</TouchableWithoutFeedback>
</KeyboardAvoidingView>
</SafeAreaView>
);
};
@@ -251,10 +278,25 @@ const styles = StyleSheet.create({
formContainer: {
alignItems: 'center',
},
codeInput: {
width: '100%',
otpContainer: {
width: '90%',
marginBottom: 32,
},
otpInput: {
width: 45,
height: 60,
lineHeight: 58,
borderWidth: 1,
borderRadius: 12,
borderColor: COLORS.gray[300],
color: COLORS.textPrimary,
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
},
otpInputHighlight: {
borderColor: COLORS.primary,
},
verifyButton: {
width: '100%',
marginBottom: 32,

View File

@@ -1,5 +1,6 @@
import React, { useEffect, useState } from 'react';
import { View, Text, StyleSheet, ActivityIndicator, TouchableOpacity, Alert, ScrollView, SafeAreaView, Modal } from 'react-native';
import { View, Text, StyleSheet, ActivityIndicator, TouchableOpacity, Alert, ScrollView, Modal } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons';
import { useNavigation, useRoute } from '@react-navigation/native';
import apiService from '../../services/apiService';

View File

@@ -1,5 +1,6 @@
import React, { useState, useCallback } from 'react';
import { View, Text, StyleSheet, FlatList, ActivityIndicator, TouchableOpacity, RefreshControl, SafeAreaView } from 'react-native';
import { View, Text, StyleSheet, FlatList, ActivityIndicator, TouchableOpacity, RefreshControl } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons';
import { useNavigation, useFocusEffect } from '@react-navigation/native';
import apiService from '../../services/apiService';

View File

@@ -1,5 +1,6 @@
import React, { useEffect, useState } from 'react';
import { View, Text, StyleSheet, ActivityIndicator, TouchableOpacity, ScrollView, SafeAreaView, Image, Alert, Linking } from 'react-native';
import { View, Text, StyleSheet, ActivityIndicator, TouchableOpacity, ScrollView, Image, Alert, Linking } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons';
import { useNavigation, useRoute } from '@react-navigation/native';
import { StatusBar } from 'expo-status-bar';

View File

@@ -1,5 +1,6 @@
import React, { useCallback, useState } from 'react';
import { View, Text, StyleSheet, FlatList, TouchableOpacity, ActivityIndicator, RefreshControl, SafeAreaView } from 'react-native';
import { View, Text, StyleSheet, FlatList, TouchableOpacity, ActivityIndicator, RefreshControl } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons';
import { useNavigation, useFocusEffect } from '@react-navigation/native';
import { StatusBar } from 'expo-status-bar';

View File

@@ -1,5 +1,6 @@
import React, { useEffect, useState } from 'react';
import { View, Text, StyleSheet, ActivityIndicator, TouchableOpacity, Alert, ScrollView, SafeAreaView, Image, Linking } from 'react-native';
import { View, Text, StyleSheet, ActivityIndicator, TouchableOpacity, Alert, ScrollView, Image, Linking } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons';
import { useNavigation, useRoute } from '@react-navigation/native';
import { StatusBar } from 'expo-status-bar';

View File

@@ -1,5 +1,6 @@
import React, { useState, useCallback } from 'react';
import { View, Text, StyleSheet, FlatList, ActivityIndicator, TouchableOpacity, RefreshControl, SafeAreaView } from 'react-native';
import { View, Text, StyleSheet, FlatList, ActivityIndicator, TouchableOpacity, RefreshControl } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons';
import { useNavigation, useFocusEffect } from '@react-navigation/native';
import { StatusBar } from 'expo-status-bar';

View File

@@ -1,5 +1,6 @@
import React, { useEffect, useState } from 'react';
import { View, Text, StyleSheet, ActivityIndicator, TouchableOpacity, Alert, ScrollView, SafeAreaView, Image } from 'react-native';
import { View, Text, StyleSheet, ActivityIndicator, TouchableOpacity, Alert, ScrollView, Image } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons';
import { useNavigation, useRoute } from '@react-navigation/native';
import { StatusBar } from 'expo-status-bar';
@@ -137,6 +138,9 @@ const CardRequisiteOrderDetailsScreen = () => {
</View>
{/* Actions */}
<TouchableOpacity style={styles.actionBtn} onPress={handleDownload}>
<Text style={styles.actionText}>Ýükle (PDF)</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.deleteBtn} onPress={handleDelete}>
<Text style={styles.deleteText}>Poz</Text>
</TouchableOpacity>

View File

@@ -1,5 +1,6 @@
import React, { useState, useCallback, useRef } from 'react';
import { View, Text, StyleSheet, FlatList, ActivityIndicator, TouchableOpacity, RefreshControl, SafeAreaView } from 'react-native';
import { View, Text, StyleSheet, FlatList, ActivityIndicator, TouchableOpacity, RefreshControl } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons';
import { useNavigation, useFocusEffect } from '@react-navigation/native';
import { StatusBar } from 'expo-status-bar';

View File

@@ -1,5 +1,6 @@
import React, { useEffect, useState } from 'react';
import { View, Text, StyleSheet, ActivityIndicator, TouchableOpacity, Alert, ScrollView, SafeAreaView, Modal } from 'react-native';
import { View, Text, StyleSheet, ActivityIndicator, TouchableOpacity, Alert, ScrollView, Modal } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons';
import { useNavigation, useRoute } from '@react-navigation/native';
import apiService from '../../services/apiService';
@@ -93,7 +94,7 @@ const CardTransactionOrderDetailsScreen = () => {
if (!order) {
return (
<View style={styles.centered}>
<Text>No data</Text>
<Text>Maglumat tapylmady</Text>
</View>
);
}

View File

@@ -1,5 +1,6 @@
import React, { useState, useCallback } from 'react';
import { View, Text, StyleSheet, FlatList, ActivityIndicator, TouchableOpacity, RefreshControl, SafeAreaView } from 'react-native';
import { View, Text, StyleSheet, FlatList, ActivityIndicator, TouchableOpacity, RefreshControl } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons';
import { useNavigation, useFocusEffect } from '@react-navigation/native';
import apiService from '../../services/apiService';

View File

@@ -1,5 +1,6 @@
import React, { useState, useEffect } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, ActivityIndicator, Alert, SafeAreaView, ScrollView } from 'react-native';
import { View, Text, StyleSheet, TouchableOpacity, ActivityIndicator, Alert, ScrollView } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons';
import { useNavigation } from '@react-navigation/native';
import apiService from '../../services/apiService';

View File

@@ -1,5 +1,6 @@
import React, { useState, useEffect, useRef } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, ActivityIndicator, Alert, SafeAreaView, ScrollView, Modal } from 'react-native';
import { View, Text, StyleSheet, TouchableOpacity, ActivityIndicator, Alert, ScrollView, Modal } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons';
import { useNavigation } from '@react-navigation/native';
import { StatusBar } from 'expo-status-bar';

View File

@@ -1,5 +1,6 @@
import React, { useState, useEffect } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, ActivityIndicator, Alert, SafeAreaView, ScrollView } from 'react-native';
import { View, Text, StyleSheet, TouchableOpacity, ActivityIndicator, Alert, ScrollView } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons';
import { useNavigation } from '@react-navigation/native';
import { StatusBar } from 'expo-status-bar';

View File

@@ -1,5 +1,6 @@
import React, { useState, useEffect } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, ActivityIndicator, Alert, SafeAreaView, ScrollView } from 'react-native';
import { View, Text, StyleSheet, TouchableOpacity, ActivityIndicator, Alert, ScrollView } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons';
import { useNavigation } from '@react-navigation/native';
import { StatusBar } from 'expo-status-bar';

View File

@@ -1,5 +1,6 @@
import React, { useState, useEffect } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, ActivityIndicator, Alert, SafeAreaView, ScrollView } from 'react-native';
import { View, Text, StyleSheet, TouchableOpacity, ActivityIndicator, Alert, ScrollView } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons';
import { useNavigation } from '@react-navigation/native';
import apiService from '../../services/apiService';

View File

@@ -1,5 +1,6 @@
import React, { useState, useEffect } from 'react';
import { Text, StyleSheet, TouchableOpacity, ActivityIndicator, Alert, ScrollView, SafeAreaView, View } from 'react-native';
import { Text, StyleSheet, TouchableOpacity, ActivityIndicator, Alert, ScrollView, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons';
import { useNavigation } from '@react-navigation/native';
import { COLORS } from '../../constants/colors';

View File

@@ -1,5 +1,6 @@
import React, { useState, useEffect } from 'react';
import { Text, StyleSheet, TouchableOpacity, ActivityIndicator, Alert, ScrollView, SafeAreaView } from 'react-native';
import { Text, StyleSheet, TouchableOpacity, ActivityIndicator, Alert, ScrollView } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons';
import { useNavigation } from '@react-navigation/native';
import { COLORS } from '../../constants/colors';

View File

@@ -1,5 +1,6 @@
import React, { useState, useEffect } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, ActivityIndicator, Alert, SafeAreaView } from 'react-native';
import { View, Text, StyleSheet, TouchableOpacity, ActivityIndicator, Alert } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons';
import { useNavigation } from '@react-navigation/native';
import apiService from '../../services/apiService';

View File

@@ -1,5 +1,6 @@
import React, { useEffect, useState } from 'react';
import { View, Text, StyleSheet, ActivityIndicator, TouchableOpacity, Alert, ScrollView, SafeAreaView } from 'react-native';
import { View, Text, StyleSheet, ActivityIndicator, TouchableOpacity, Alert, ScrollView } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons';
import { useNavigation, useRoute } from '@react-navigation/native';
import apiService from '../../services/apiService';

View File

@@ -1,5 +1,6 @@
import React, { useState, useCallback } from 'react';
import { View, Text, StyleSheet, FlatList, ActivityIndicator, TouchableOpacity, RefreshControl, SafeAreaView, Alert } from 'react-native';
import { View, Text, StyleSheet, FlatList, ActivityIndicator, TouchableOpacity, RefreshControl, Alert } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons';
import { useNavigation, useFocusEffect } from '@react-navigation/native';
import apiService from '../../services/apiService';

View File

@@ -1,5 +1,6 @@
import React, { useEffect, useState } from 'react';
import { View, Text, StyleSheet, ActivityIndicator, TouchableOpacity, Alert, ScrollView, SafeAreaView } from 'react-native';
import { View, Text, StyleSheet, ActivityIndicator, TouchableOpacity, Alert, ScrollView } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons';
import { useNavigation, useRoute } from '@react-navigation/native';
import apiService from '../../services/apiService';

View File

@@ -1,5 +1,6 @@
import React, { useState, useCallback } from 'react';
import { View, Text, StyleSheet, FlatList, ActivityIndicator, TouchableOpacity, RefreshControl, SafeAreaView, Alert } from 'react-native';
import { View, Text, StyleSheet, FlatList, ActivityIndicator, TouchableOpacity, RefreshControl, Alert } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons';
import { useNavigation, useFocusEffect } from '@react-navigation/native';
import apiService from '../../services/apiService';

View File

@@ -1,5 +1,6 @@
import React, { useEffect, useState } from 'react';
import { View, Text, StyleSheet, ActivityIndicator, TouchableOpacity, Alert } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons';
import { useNavigation, useRoute } from '@react-navigation/native';
import apiService from '../../services/apiService';
@@ -63,7 +64,7 @@ const LoanRemainingOrderDetailsScreen = () => {
}
return (
<View style={styles.container}>
<SafeAreaView style={styles.container}>
<TouchableOpacity style={styles.backBtn} onPress={() => navigation.goBack()}>
<Ionicons name="close" size={28} color={COLORS.textPrimary} />
</TouchableOpacity>
@@ -82,7 +83,7 @@ const LoanRemainingOrderDetailsScreen = () => {
<TouchableOpacity style={styles.deleteBtn} onPress={handleDelete}>
<Text style={styles.deleteText}>Poz</Text>
</TouchableOpacity>
</View>
</SafeAreaView>
);
};

View File

@@ -1,5 +1,6 @@
import React, { useState, useCallback } from 'react';
import { View, Text, StyleSheet, FlatList, ActivityIndicator, TouchableOpacity, RefreshControl, Modal, ScrollView, Alert, SafeAreaView, Pressable } from 'react-native';
import { View, Text, StyleSheet, FlatList, ActivityIndicator, TouchableOpacity, RefreshControl, Modal, ScrollView, Alert, Pressable } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons';
import { useNavigation, useFocusEffect } from '@react-navigation/native';
import apiService from '../../services/apiService';
@@ -133,7 +134,7 @@ const LoanRemainingOrdersScreen = () => {
renderItem={renderItem}
contentContainerStyle={orders.length === 0 && styles.emptyContainer}
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />}
ListEmptyComponent={<Text style={styles.emptyText}>No orders yet</Text>}
ListEmptyComponent={<Text style={styles.emptyText}>Sargyt ýok</Text>}
/>
{/* Floating Action Button */}

View File

@@ -3,21 +3,107 @@ import {
View,
Text,
StyleSheet,
SafeAreaView,
ScrollView,
TouchableOpacity,
ActivityIndicator,
RefreshControl,
FlatList,
} from 'react-native';
import { StatusBar } from 'expo-status-bar';
import { Ionicons } from '@expo/vector-icons';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { useAuth } from '../../contexts/AuthContext';
import { COLORS } from '../../constants/colors';
import MetricCard from '../../components/MetricCard';
import apiService from '../../services/apiService';
import TransactionList from '../../components/TransactionList';
const STATIC_TRANSACTIONS = [
{
id: 1,
type: 'debit',
description: 'Canva Design and Publishing',
date: '2025-09-07T10:30:00Z',
amount: 10.0,
currency: 'EUR',
},
{
id: 2,
type: 'debit',
description: 'Google',
date: '2025-09-07T11:00:00Z',
amount: 12.99,
currency: 'SAR',
},
{
id: 3,
type: 'debit',
description: 'Canva Design and Publishing',
date: '2025-09-05T14:15:00Z',
amount: 10.0,
currency: 'EUR',
},
{
id: 4,
type: 'credit',
description: 'Töleg alyňýan hasap',
date: '2025-09-03T09:00:00Z',
amount: 20.0,
currency: 'USD',
},
{
id: 5,
type: 'debit',
description: 'Комиссия за оплату',
date: '2025-09-03T09:01:00Z',
amount: 0.12,
currency: 'USD',
},
{
id: 6,
type: 'credit',
description: 'Hasaby doldurmak',
date: '2025-09-02T18:45:00Z',
amount: 10.0,
currency: 'USD',
},
{
id: 7,
type: 'debit',
description: 'Canva Design and Publishing',
date: '2025-09-01T12:00:00Z',
amount: 10.0,
currency: 'EUR',
},
{
id: 8,
type: 'debit',
description: 'Netflix',
date: '2025-08-28T16:00:00Z',
amount: 9.99,
currency: 'USD',
},
{
id: 9,
type: 'credit',
description: 'Bank Transfer',
date: '2025-08-25T08:20:00Z',
amount: 500.0,
currency: 'TMT',
},
{
id: 10,
type: 'debit',
description: 'Amazon',
date: '2025-08-23T19:55:00Z',
amount: 45.5,
currency: 'USD',
},
];
const HomeScreen = () => {
const { user, logout } = useAuth();
const insets = useSafeAreaInsets();
const [metrics, setMetrics] = useState(null);
const [loadingMetrics, setLoadingMetrics] = useState(true);
const [cardBalance, setCardBalance] = useState(null);
@@ -25,6 +111,8 @@ const HomeScreen = () => {
const [cardBalanceError, setCardBalanceError] = useState(null);
const [isBalanceVisible, setIsBalanceVisible] = useState(true);
const [refreshing, setRefreshing] = useState(false);
const [transactions, setTransactions] = useState([]);
const [loadingTransactions, setLoadingTransactions] = useState(true);
const showBalanceCard = !loadingCardBalance && cardBalanceError === null && cardBalance !== null;
@@ -50,16 +138,20 @@ const HomeScreen = () => {
}, []);
useEffect(() => {
const fetchCardBalance = async () => {
const fetchCardData = async () => {
// Ensure user has filled all required card & passport fields
if (!user?.passport_serie || !user?.passport_id || !user?.card_number || !user?.card_month || !user?.card_year) {
setLoadingCardBalance(false);
setLoadingTransactions(false);
return;
}
setLoadingCardBalance(true);
setLoadingTransactions(true);
try {
const res = await apiService.getCardBalanceQuickCheck();
if (res.success) {
// Try common balance keys else fallback to raw
const raw = res.data;
let balanceValue = null;
if (raw && typeof raw === 'object') {
@@ -73,12 +165,26 @@ const HomeScreen = () => {
}
} catch (e) {
console.warn('Error fetching card balance:', e);
setCardBalanceError('Error fetching balance');
} finally {
setLoadingCardBalance(false);
}
try {
const transRes = await apiService.getCardTransactionsLastMonth();
if (transRes.success) {
setTransactions(transRes.data || []);
} else {
console.warn('Failed to fetch transactions:', transRes.error);
}
} catch (e) {
console.warn('Error fetching transactions:', e);
} finally {
setLoadingTransactions(false);
}
};
fetchCardBalance();
fetchCardData();
}, [user]);
const handleRefresh = async () => {
@@ -100,6 +206,8 @@ const HomeScreen = () => {
if (!user?.passport_serie || !user?.passport_id || !user?.card_number || !user?.card_month || !user?.card_year) {
setCardBalance(null); // Clear previous balance if conditions are not met
setCardBalanceError(null);
setTransactions([]);
setLoadingTransactions(false);
return;
}
try {
@@ -119,13 +227,25 @@ const HomeScreen = () => {
} catch (e) {
console.warn('Error fetching card balance during refresh:', e);
}
try {
const transRes = await apiService.getCardTransactionsLastMonth();
if (transRes.success) {
setTransactions(transRes.data || []);
} else {
console.warn('Failed to fetch transactions during refresh:', transRes.error);
}
} catch (e) {
console.warn('Error fetching transactions during refresh:', e);
} finally {
setLoadingTransactions(false);
}
})(),
]);
setRefreshing(false);
};
return (
<SafeAreaView style={styles.container}>
<View style={styles.container}>
<StatusBar style="dark" />
<ScrollView
@@ -140,7 +260,7 @@ const HomeScreen = () => {
}
>
{/* Header */}
<View style={styles.header}>
<View style={[styles.header, { paddingTop: insets.top + 16 }]}>
<View>
<Text style={styles.greeting}>Salam,</Text>
<Text style={styles.userName}>{user?.name || 'Ulanyjy'}</Text>
@@ -149,7 +269,7 @@ const HomeScreen = () => {
{/* Metrics */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Metrics</Text>
<Text style={styles.sectionTitle}>Görkezijiler</Text>
{loadingMetrics ? (
<ActivityIndicator color={COLORS.primary} style={{ marginTop: 16 }} />
) : (
@@ -181,8 +301,27 @@ const HomeScreen = () => {
</View>
</View>
)}
{/* Card Transactions */}
{showBalanceCard && (
<View style={styles.section}>
<View style={styles.sectionHeader}>
<Text style={styles.sectionTitle}>Kart hereketleri</Text>
<Text style={styles.sectionSubtitle}>Soňky 10 günde</Text>
<TouchableOpacity>
{/* <Text style={styles.seeAllText}>Hemmesi</Text> */}
</TouchableOpacity>
</View>
{loadingTransactions ? (
<ActivityIndicator color={COLORS.primary} style={{ marginTop: 16 }} />
) : (
<TransactionList transactions={transactions} />
)}
</View>
)}
</ScrollView>
</SafeAreaView>
</View>
);
};
@@ -199,7 +338,6 @@ const styles = StyleSheet.create({
justifyContent: 'space-between',
alignItems: 'center',
paddingHorizontal: 24,
paddingTop: 16,
paddingBottom: 24,
backgroundColor: COLORS.white,
marginBottom: 16,
@@ -273,16 +411,16 @@ const styles = StyleSheet.create({
padding: 20,
},
sectionHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 16,
},
sectionTitle: {
fontSize: 18,
fontWeight: 'bold',
color: COLORS.textPrimary,
marginBottom: 16,
},
sectionSubtitle: {
fontSize: 14,
color: COLORS.textSecondary,
},
seeAllText: {
fontSize: 14,
@@ -310,58 +448,6 @@ const styles = StyleSheet.create({
color: COLORS.textSecondary,
textAlign: 'center',
},
transactionsList: {
gap: 16,
},
transactionItem: {
flexDirection: 'row',
alignItems: 'center',
},
transactionIcon: {
width: 40,
height: 40,
borderRadius: 20,
backgroundColor: COLORS.backgroundSecondary,
alignItems: 'center',
justifyContent: 'center',
marginRight: 12,
},
transactionDetails: {
flex: 1,
},
transactionTitle: {
fontSize: 16,
fontWeight: '600',
color: COLORS.textPrimary,
},
transactionDate: {
fontSize: 14,
color: COLORS.textSecondary,
},
transactionAmount: {
fontSize: 16,
fontWeight: 'bold',
color: COLORS.success,
},
servicesGrid: {
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'space-between',
},
serviceItem: {
width: '48%',
padding: 16,
backgroundColor: COLORS.backgroundSecondary,
borderRadius: 12,
alignItems: 'center',
marginBottom: 12,
},
serviceText: {
fontSize: 12,
color: COLORS.textSecondary,
textAlign: 'center',
marginTop: 8,
},
metricsGrid: {
flexDirection: 'row',
justifyContent: 'space-between',

View File

@@ -4,16 +4,17 @@ import {
View,
Text,
StyleSheet,
SafeAreaView,
ScrollView,
TouchableOpacity,
} from 'react-native';
import { StatusBar } from 'expo-status-bar';
import { Ionicons } from '@expo/vector-icons';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { COLORS } from '../../constants/colors';
const MenuScreen = () => {
const navigation = useNavigation();
const insets = useSafeAreaInsets();
const menuSections = [
{
@@ -66,10 +67,10 @@ const MenuScreen = () => {
};
return (
<SafeAreaView style={styles.container}>
<View style={styles.container}>
<StatusBar style="dark" />
<View style={styles.header}>
<View style={[styles.header, { paddingTop: insets.top + 16 }]}>
<Text style={styles.headerTitle}>Hyzmatlar</Text>
</View>
@@ -105,7 +106,7 @@ const MenuScreen = () => {
<View style={styles.bottomSpacing} />
</ScrollView>
</SafeAreaView>
</View>
);
};
@@ -116,7 +117,6 @@ const styles = StyleSheet.create({
},
header: {
paddingHorizontal: 24,
paddingTop: 16,
paddingBottom: 24,
borderBottomWidth: 1,
borderBottomColor: COLORS.gray[200],

View File

@@ -3,15 +3,16 @@ import {
View,
Text,
StyleSheet,
SafeAreaView,
ScrollView,
TouchableOpacity,
Alert,
ActivityIndicator,
RefreshControl,
Linking,
} from 'react-native';
import { StatusBar } from 'expo-status-bar';
import { Ionicons } from '@expo/vector-icons';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { useAuth } from '../../contexts/AuthContext';
import apiService from '../../services/apiService';
import EditProfileModal from '../../components/EditProfileModal';
@@ -19,6 +20,7 @@ import { COLORS } from '../../constants/colors';
const ProfileScreen = () => {
const { user, logout, fetchUserProfile } = useAuth();
const insets = useSafeAreaInsets();
const [isLoading, setIsLoading] = useState(false);
const [isRefreshing, setIsRefreshing] = useState(false);
const [profileData, setProfileData] = useState(null);
@@ -80,8 +82,8 @@ const ProfileScreen = () => {
items: [
// { id: 11, title: 'Ulanmak düzgünleri', icon: 'document-text', hasArrow: true },
// { id: 12, title: 'Gizlinlik syýasaty', icon: 'lock-open', hasArrow: true },
{ id: 13, title: 'Programma barada', icon: 'information-circle', value: 'v1.0.0', hasArrow: true },
{ id: 14, title: 'Hasaby poz', icon: 'trash', hasArrow: false, color: COLORS.red },
{ id: 13, title: 'Programma barada', icon: 'information-circle', value: 'v1.0.0', hasArrow: false },
{ id: 14, title: 'Hasaby poz', icon: 'trash', hasArrow: false, danger: true },
],
},
];
@@ -104,7 +106,7 @@ const ProfileScreen = () => {
handleSecuritySettings();
break;
case 13: // About App
Alert.alert('Üns beriň', 'Programma barada maglumatlar açylýar...');
// Alert.alert('Üns beriň', 'Programma barada maglumatlar açylýar...');
break;
case 14: // Delete Account
handleDeleteAccount();
@@ -278,10 +280,10 @@ const ProfileScreen = () => {
const currentUser = profileData || user;
return (
<SafeAreaView style={styles.container}>
<View style={styles.container}>
<StatusBar style="dark" />
<View style={styles.header}>
<View style={[styles.header, { paddingTop: insets.top + 16 }]}>
<Text style={styles.headerTitle}>Profil</Text>
</View>
@@ -335,16 +337,16 @@ const ProfileScreen = () => {
>
<View style={styles.profileItemLeft}>
<View style={styles.profileItemIcon}>
<Ionicons name={item.icon} size={20} color={item.color || COLORS.primary} />
<Ionicons name={item.icon} size={20} color={item.danger ? COLORS.error : COLORS.primary} />
</View>
<Text style={styles.profileItemTitle}>{item.title}</Text>
<Text style={item.danger ? styles.profileItemTitleDanger : styles.profileItemTitle}>{item.title}</Text>
</View>
<View style={styles.profileItemRight}>
{item.value && (
<Text style={styles.profileItemValue}>{item.value}</Text>
<Text style={item.danger ? styles.profileItemValueDanger : styles.profileItemValue}>{item.value}</Text>
)}
{item.hasArrow && (
<Ionicons name="chevron-forward" size={16} color={COLORS.gray[400]} />
<Ionicons name="chevron-forward" size={16} color={item.danger ? COLORS.error : COLORS.gray[400]} />
)}
</View>
</TouchableOpacity>
@@ -362,6 +364,21 @@ const ProfileScreen = () => {
</View>
<View style={styles.bottomSpacing} />
{/* Copyright */}
<Text style={styles.copyrightText}>
Copyright{' '}
<Text
style={styles.link}
onPress={() => Linking.openURL('https://webulgam.com')}
>
Webulgam IT Company
</Text>{' '}
© {new Date().getFullYear()}.
</Text>
<Text style={styles.copyrightText}>
Ähli hukular goralan.
</Text>
</ScrollView>
{/* Edit Profile Modal */}
@@ -372,7 +389,7 @@ const ProfileScreen = () => {
initialData={profileData || user}
isLoading={isUpdatingProfile}
/>
</SafeAreaView>
</View>
);
};
@@ -384,7 +401,6 @@ const styles = StyleSheet.create({
header: {
backgroundColor: COLORS.white,
paddingHorizontal: 24,
paddingTop: 16,
paddingBottom: 24,
borderBottomWidth: 1,
borderBottomColor: COLORS.gray[200],
@@ -526,6 +542,24 @@ const styles = StyleSheet.create({
bottomSpacing: {
height: 24,
},
copyrightText: {
fontSize: 12,
color: COLORS.textSecondary,
textAlign: 'center',
},
link: {
color: COLORS.primary,
},
profileItemTitleDanger: {
fontSize: 16,
color: COLORS.error,
fontWeight: 'bold',
},
profileItemValueDanger: {
fontSize: 14,
color: COLORS.error,
fontWeight: 'bold',
},
});
export default ProfileScreen;

View File

@@ -67,6 +67,37 @@ class ApiService {
return authService.getTransactions(page, limit);
}
async getCardTransactionsLastMonth() {
try {
const response = await authService.getCardTransactionsLastMonth();
if (response && response.data && Array.isArray(response.data.transactions)) {
const mappedTransactions = response.data.transactions.map((t, index) => ({
id: `${t.rrn}-${index}`,
type: t.sign === '-' ? 'debit' : 'credit',
description: t.binfo || t.opername,
date: `${t.operdate}`,
time: t.trantime,
amount: t.currOperSum,
currency: t.currCode,
})).reverse();
return {
success: true,
data: mappedTransactions,
};
}
console.warn('Invalid transaction data structure:', response);
return {
success: false,
error: 'Could not parse transaction data.',
};
} catch (error) {
return {
success: false,
error: error.message,
};
}
}
// Transfer and payments
async transferMoney(data) {
return authService.transferMoney(data);

View File

@@ -162,6 +162,10 @@ class AuthService {
return this.makeRequest(`/user/cards/${cardId}/unblock`, null, true);
}
async getCardTransactionsLastMonth() {
return this.makeRequest('/card-transactions-last-month', null, true, 'GET');
}
async changePassword(currentPassword, newPassword) {
return this.makeRequest('/user/change-password', {
current_password: currentPassword,