some fix
# Conflicts: # lib/presentation/navigation/app_router.dart # pubspec.yaml
BIN
android/app/src/main/res/drawable-hdpi/splash.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
android/app/src/main/res/drawable-mdpi/splash.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
android/app/src/main/res/drawable-v21/background.png
Normal file
|
After Width: | Height: | Size: 69 B |
@@ -1,12 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!-- Modify this file to customize your launch splash screen -->
|
|
||||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item android:drawable="?android:colorBackground" />
|
<item>
|
||||||
|
<bitmap android:gravity="fill" android:src="@drawable/background"/>
|
||||||
<!-- You can insert your own image assets here -->
|
</item>
|
||||||
<!-- <item>
|
<item>
|
||||||
<bitmap
|
<bitmap android:gravity="center" android:src="@drawable/splash"/>
|
||||||
android:gravity="center"
|
</item>
|
||||||
android:src="@mipmap/launch_image" />
|
|
||||||
</item> -->
|
|
||||||
</layer-list>
|
</layer-list>
|
||||||
|
|||||||
BIN
android/app/src/main/res/drawable-xhdpi/splash.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
android/app/src/main/res/drawable-xxhdpi/splash.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
android/app/src/main/res/drawable-xxxhdpi/splash.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
android/app/src/main/res/drawable/background.png
Normal file
|
After Width: | Height: | Size: 69 B |
@@ -1,12 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!-- Modify this file to customize your launch splash screen -->
|
|
||||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item android:drawable="@android:color/white" />
|
<item>
|
||||||
|
<bitmap android:gravity="fill" android:src="@drawable/background"/>
|
||||||
<!-- You can insert your own image assets here -->
|
</item>
|
||||||
<!-- <item>
|
<item>
|
||||||
<bitmap
|
<bitmap android:gravity="center" android:src="@drawable/splash"/>
|
||||||
android:gravity="center"
|
</item>
|
||||||
android:src="@mipmap/launch_image" />
|
|
||||||
</item> -->
|
|
||||||
</layer-list>
|
</layer-list>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 9.6 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
19
android/app/src/main/res/values-night-v31/styles.xml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
||||||
|
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||||
|
<item name="android:forceDarkAllowed">false</item>
|
||||||
|
<item name="android:windowFullscreen">false</item>
|
||||||
|
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
|
||||||
|
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
||||||
|
</style>
|
||||||
|
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||||
|
This theme determines the color of the Android Window while your
|
||||||
|
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||||
|
running.
|
||||||
|
|
||||||
|
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||||
|
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||||
|
<item name="android:windowBackground">?android:colorBackground</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
@@ -5,6 +5,10 @@
|
|||||||
<!-- Show a splash screen on the activity. Automatically removed when
|
<!-- Show a splash screen on the activity. Automatically removed when
|
||||||
the Flutter engine draws its first frame -->
|
the Flutter engine draws its first frame -->
|
||||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
|
<item name="android:forceDarkAllowed">false</item>
|
||||||
|
<item name="android:windowFullscreen">false</item>
|
||||||
|
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
|
||||||
|
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
||||||
</style>
|
</style>
|
||||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||||
This theme determines the color of the Android Window while your
|
This theme determines the color of the Android Window while your
|
||||||
|
|||||||
19
android/app/src/main/res/values-v31/styles.xml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
||||||
|
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||||
|
<item name="android:forceDarkAllowed">false</item>
|
||||||
|
<item name="android:windowFullscreen">false</item>
|
||||||
|
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
|
||||||
|
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
||||||
|
</style>
|
||||||
|
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||||
|
This theme determines the color of the Android Window while your
|
||||||
|
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||||
|
running.
|
||||||
|
|
||||||
|
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||||
|
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||||
|
<item name="android:windowBackground">?android:colorBackground</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
@@ -5,6 +5,10 @@
|
|||||||
<!-- Show a splash screen on the activity. Automatically removed when
|
<!-- Show a splash screen on the activity. Automatically removed when
|
||||||
the Flutter engine draws its first frame -->
|
the Flutter engine draws its first frame -->
|
||||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
|
<item name="android:forceDarkAllowed">false</item>
|
||||||
|
<item name="android:windowFullscreen">false</item>
|
||||||
|
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
|
||||||
|
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
||||||
</style>
|
</style>
|
||||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||||
This theme determines the color of the Android Window while your
|
This theme determines the color of the Android Window while your
|
||||||
|
|||||||
BIN
assets/payment_logo.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
assets/rules_alpha.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/rules_apple_pay.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
assets/rules_belcard.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
assets/rules_mastercard.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
assets/rules_mastercard_id.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
assets/rules_samsung_pay.png
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
BIN
assets/rules_visa.png
Normal file
|
After Width: | Height: | Size: 6.0 KiB |
BIN
assets/rules_visa_secure.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
assets/tech_works.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
@@ -2,6 +2,8 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>MinimumOSVersion</key>
|
||||||
|
<string>13.0</string>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>en</string>
|
<string>en</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ post_install do |installer|
|
|||||||
# ML Kit исключает arm64 для симулятора → сборка только x86_64.
|
# ML Kit исключает arm64 для симулятора → сборка только x86_64.
|
||||||
# На M1 это ещё могло работать через Rosetta; на M4 симулятор только arm64.
|
# На M1 это ещё могло работать через Rosetta; на M4 симулятор только arm64.
|
||||||
config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'i386'
|
config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'i386'
|
||||||
|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ PODS:
|
|||||||
- device_info_plus (0.0.1):
|
- device_info_plus (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- Flutter (1.0.0)
|
- Flutter (1.0.0)
|
||||||
|
- flutter_native_splash (2.4.3):
|
||||||
|
- Flutter
|
||||||
- flutter_secure_storage (6.0.0):
|
- flutter_secure_storage (6.0.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
- geolocator_apple (1.2.0):
|
- geolocator_apple (1.2.0):
|
||||||
@@ -25,6 +27,7 @@ PODS:
|
|||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||||
- Flutter (from `Flutter`)
|
- Flutter (from `Flutter`)
|
||||||
|
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
|
||||||
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
|
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
|
||||||
- geolocator_apple (from `.symlinks/plugins/geolocator_apple/darwin`)
|
- geolocator_apple (from `.symlinks/plugins/geolocator_apple/darwin`)
|
||||||
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
|
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
|
||||||
@@ -42,6 +45,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/device_info_plus/ios"
|
:path: ".symlinks/plugins/device_info_plus/ios"
|
||||||
Flutter:
|
Flutter:
|
||||||
:path: Flutter
|
:path: Flutter
|
||||||
|
flutter_native_splash:
|
||||||
|
:path: ".symlinks/plugins/flutter_native_splash/ios"
|
||||||
flutter_secure_storage:
|
flutter_secure_storage:
|
||||||
:path: ".symlinks/plugins/flutter_secure_storage/ios"
|
:path: ".symlinks/plugins/flutter_secure_storage/ios"
|
||||||
geolocator_apple:
|
geolocator_apple:
|
||||||
@@ -60,6 +65,7 @@ EXTERNAL SOURCES:
|
|||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe
|
device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe
|
||||||
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
||||||
|
flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf
|
||||||
flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13
|
flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13
|
||||||
geolocator_apple: ab36aa0e8b7d7a2d7639b3b4e48308394e8cef5e
|
geolocator_apple: ab36aa0e8b7d7a2d7639b3b4e48308394e8cef5e
|
||||||
image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326
|
image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326
|
||||||
@@ -69,6 +75,6 @@ SPEC CHECKSUMS:
|
|||||||
yandex_mapkit: b0468cf6fdc2e5ef315e08c081cb48763db1edee
|
yandex_mapkit: b0468cf6fdc2e5ef315e08c081cb48763db1edee
|
||||||
YandexMapsMobile: 27e6e45425bb61f0be2dbc2bf02d4e64926691a9
|
YandexMapsMobile: 27e6e45425bb61f0be2dbc2bf02d4e64926691a9
|
||||||
|
|
||||||
PODFILE CHECKSUM: af32912eda0a14960d3c16b570702dbc954fc964
|
PODFILE CHECKSUM: 5deaa97a3697e922b222a24011322ae172be432f
|
||||||
|
|
||||||
COCOAPODS: 1.16.2
|
COCOAPODS: 1.16.2
|
||||||
|
|||||||
@@ -296,14 +296,10 @@
|
|||||||
inputFileListPaths = (
|
inputFileListPaths = (
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
);
|
);
|
||||||
inputPaths = (
|
|
||||||
);
|
|
||||||
name = "[CP] Copy Pods Resources";
|
name = "[CP] Copy Pods Resources";
|
||||||
outputFileListPaths = (
|
outputFileListPaths = (
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
);
|
);
|
||||||
outputPaths = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
||||||
@@ -376,14 +372,10 @@
|
|||||||
inputFileListPaths = (
|
inputFileListPaths = (
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
);
|
);
|
||||||
inputPaths = (
|
|
||||||
);
|
|
||||||
name = "[CP] Embed Pods Frameworks";
|
name = "[CP] Embed Pods Frameworks";
|
||||||
outputFileListPaths = (
|
outputFileListPaths = (
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
);
|
);
|
||||||
outputPaths = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||||
@@ -486,7 +478,7 @@
|
|||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SUPPORTED_PLATFORMS = iphoneos;
|
SUPPORTED_PLATFORMS = iphoneos;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1";
|
||||||
VALIDATE_PRODUCT = YES;
|
VALIDATE_PRODUCT = YES;
|
||||||
};
|
};
|
||||||
name = Profile;
|
name = Profile;
|
||||||
@@ -500,7 +492,7 @@
|
|||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
DEVELOPMENT_TEAM = F42W9GA5WC;
|
DEVELOPMENT_TEAM = J463CU8H5F;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
@@ -619,7 +611,7 @@
|
|||||||
MTL_ENABLE_DEBUG_INFO = YES;
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1";
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
};
|
};
|
||||||
@@ -672,7 +664,7 @@
|
|||||||
SUPPORTED_PLATFORMS = iphoneos;
|
SUPPORTED_PLATFORMS = iphoneos;
|
||||||
SWIFT_COMPILATION_MODE = wholemodule;
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1";
|
||||||
VALIDATE_PRODUCT = YES;
|
VALIDATE_PRODUCT = YES;
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
@@ -686,7 +678,7 @@
|
|||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
DEVELOPMENT_TEAM = F42W9GA5WC;
|
DEVELOPMENT_TEAM = J463CU8H5F;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
@@ -712,7 +704,7 @@
|
|||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
DEVELOPMENT_TEAM = F42W9GA5WC;
|
DEVELOPMENT_TEAM = J463CU8H5F;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 351 KiB After Width: | Height: | Size: 322 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 986 B |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
21
ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "background.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png
vendored
Normal file
|
After Width: | Height: | Size: 69 B |
@@ -1,23 +1,23 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"idiom" : "universal",
|
|
||||||
"filename" : "LaunchImage.png",
|
"filename" : "LaunchImage.png",
|
||||||
|
"idiom" : "universal",
|
||||||
"scale" : "1x"
|
"scale" : "1x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idiom" : "universal",
|
|
||||||
"filename" : "LaunchImage@2x.png",
|
"filename" : "LaunchImage@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idiom" : "universal",
|
|
||||||
"filename" : "LaunchImage@3x.png",
|
"filename" : "LaunchImage@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
"scale" : "3x"
|
"scale" : "3x"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
"version" : 1,
|
"author" : "xcode",
|
||||||
"author" : "xcode"
|
"version" : 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 68 B After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 68 B After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 68 B After Width: | Height: | Size: 11 KiB |
@@ -16,13 +16,19 @@
|
|||||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
|
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" image="LaunchBackground" translatesAutoresizingMaskIntoConstraints="NO" id="tWc-Dq-wcI"/>
|
||||||
</imageView>
|
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4"></imageView>
|
||||||
</subviews>
|
</subviews>
|
||||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
|
<constraint firstItem="YRO-k0-Ey4" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" id="3T2-ad-Qdv"/>
|
||||||
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
|
<constraint firstItem="tWc-Dq-wcI" firstAttribute="bottom" secondItem="Ze5-6b-2t3" secondAttribute="bottom" id="RPx-PI-7Xg"/>
|
||||||
|
<constraint firstItem="tWc-Dq-wcI" firstAttribute="top" secondItem="Ze5-6b-2t3" secondAttribute="top" id="SdS-ul-q2q"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="tWc-Dq-wcI" secondAttribute="trailing" id="Swv-Gf-Rwn"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="YRO-k0-Ey4" secondAttribute="trailing" id="TQA-XW-tRk"/>
|
||||||
|
<constraint firstItem="YRO-k0-Ey4" firstAttribute="bottom" secondItem="Ze5-6b-2t3" secondAttribute="bottom" id="duK-uY-Gun"/>
|
||||||
|
<constraint firstItem="tWc-Dq-wcI" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" id="kV7-tw-vXt"/>
|
||||||
|
<constraint firstItem="YRO-k0-Ey4" firstAttribute="top" secondItem="Ze5-6b-2t3" secondAttribute="top" id="xPn-NY-SIU"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
</viewController>
|
</viewController>
|
||||||
@@ -32,6 +38,7 @@
|
|||||||
</scene>
|
</scene>
|
||||||
</scenes>
|
</scenes>
|
||||||
<resources>
|
<resources>
|
||||||
<image name="LaunchImage" width="168" height="185"/>
|
<image name="LaunchImage" width="130" height="130"/>
|
||||||
|
<image name="LaunchBackground" width="1" height="1"/>
|
||||||
</resources>
|
</resources>
|
||||||
</document>
|
</document>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
@@ -76,5 +76,7 @@
|
|||||||
<string>Приложению нужен доступ к камере для сканирования QR-кодов самокатов</string>
|
<string>Приложению нужен доступ к камере для сканирования QR-кодов самокатов</string>
|
||||||
<key>NSPhotoLibraryUsageDescription</key>
|
<key>NSPhotoLibraryUsageDescription</key>
|
||||||
<string>Приложению нужен доступ к галерее для выбора фото профиля и подтверждения поездки</string>
|
<string>Приложению нужен доступ к галерее для выбора фото профиля и подтверждения поездки</string>
|
||||||
</dict>
|
<key>UIStatusBarHidden</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import 'package:flutter_client_sse/constants/sse_request_type_enum.dart';
|
|||||||
import 'package:flutter_client_sse/flutter_client_sse.dart';
|
import 'package:flutter_client_sse/flutter_client_sse.dart';
|
||||||
|
|
||||||
import '../../domain/entities/client_subscription.dart';
|
import '../../domain/entities/client_subscription.dart';
|
||||||
|
import '../../domain/entities/fine.dart';
|
||||||
import '../../domain/entities/point.dart';
|
import '../../domain/entities/point.dart';
|
||||||
import '../../domain/entities/user_profile.dart';
|
import '../../domain/entities/user_profile.dart';
|
||||||
import '../../domain/entities/payment_card.dart';
|
import '../../domain/entities/payment_card.dart';
|
||||||
@@ -934,6 +935,44 @@ class ApiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// GET /scooterorder/{id}/fine
|
||||||
|
Future<Fine?> getFineDetails(int orderId) async {
|
||||||
|
try {
|
||||||
|
final response = await _dio.get(
|
||||||
|
"$baseUrl/scooterorder/$orderId/fine",
|
||||||
|
options: await _getAuthOptions(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200 && response.data != null) {
|
||||||
|
return Fine.fromJson(response.data);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} on DioException catch (e) {
|
||||||
|
_handleDioError(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// PUT /scooterorder/{id}/fine
|
||||||
|
Future<void> payFine({
|
||||||
|
required int orderId,
|
||||||
|
int? cardId,
|
||||||
|
required bool isBalance,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
await _dio.put(
|
||||||
|
"$baseUrl/scooterorder/$orderId/fine",
|
||||||
|
data: {
|
||||||
|
if (cardId != null) 'cardId': cardId,
|
||||||
|
'isBalance': isBalance,
|
||||||
|
},
|
||||||
|
options: await _getAuthOptions(),
|
||||||
|
);
|
||||||
|
} on DioException catch (e) {
|
||||||
|
_handleDioError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<Map<String, dynamic>> getNews() async {
|
Future<Map<String, dynamic>> getNews() async {
|
||||||
try {
|
try {
|
||||||
final response = await _dio.get(
|
final response = await _dio.get(
|
||||||
@@ -1148,5 +1187,6 @@ class ApiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ Добавь исключение (можно в отдельный файл)
|
|
||||||
|
|
||||||
class PromoCodeNotFoundException implements Exception {}
|
class PromoCodeNotFoundException implements Exception {}
|
||||||
|
|||||||
@@ -1,21 +1,22 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:be_happy/data/exceptions/auth_block_exception.dart';
|
|
||||||
import 'package:be_happy/data/exceptions/route_history_not_found_exception.dart';
|
|
||||||
import 'package:be_happy/data/exceptions/scooter_not_found_exception.dart';
|
|
||||||
import 'package:be_happy/data/exceptions/unauthorized_exception.dart';
|
|
||||||
import 'package:be_happy/data/exceptions/wrong_zone_exception.dart';
|
|
||||||
import 'package:be_happy/domain/entities/point.dart';
|
|
||||||
import 'package:be_happy/domain/repositories/scooter_repository.dart';
|
|
||||||
|
|
||||||
import '../../core/failures.dart';
|
import '../../core/failures.dart';
|
||||||
import '../../core/result.dart';
|
import '../../core/result.dart';
|
||||||
import '../../domain/entities/active_scooter_order.dart';
|
import '../../domain/entities/active_scooter_order.dart';
|
||||||
import '../../domain/entities/client_subscription.dart';
|
import '../../domain/entities/client_subscription.dart';
|
||||||
|
import '../../domain/entities/fine.dart';
|
||||||
|
import '../../domain/entities/point.dart';
|
||||||
import '../../domain/entities/scooter.dart';
|
import '../../domain/entities/scooter.dart';
|
||||||
import '../../domain/entities/tariff.dart';
|
import '../../domain/entities/tariff.dart';
|
||||||
import '../../domain/entities/subscription.dart';
|
import '../../domain/entities/subscription.dart';
|
||||||
import '../../domain/entities/scooter_order.dart';
|
import '../../domain/entities/scooter_order.dart';
|
||||||
|
import '../../domain/repositories/scooter_repository.dart';
|
||||||
|
import '../exceptions/auth_block_exception.dart';
|
||||||
import '../exceptions/auth_exception.dart';
|
import '../exceptions/auth_exception.dart';
|
||||||
|
import '../exceptions/route_history_not_found_exception.dart';
|
||||||
|
import '../exceptions/scooter_not_found_exception.dart';
|
||||||
|
import '../exceptions/unauthorized_exception.dart';
|
||||||
|
import '../exceptions/wrong_zone_exception.dart';
|
||||||
import '../network/api_service.dart';
|
import '../network/api_service.dart';
|
||||||
|
|
||||||
class ScooterRepositoryImpl extends ScooterRepository {
|
class ScooterRepositoryImpl extends ScooterRepository {
|
||||||
@@ -425,4 +426,52 @@ class ScooterRepositoryImpl extends ScooterRepository {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Result<Fine>> getFineDetails(int orderId) async {
|
||||||
|
late final Result<Fine> result;
|
||||||
|
try {
|
||||||
|
final fine = await _apiService.getFineDetails(orderId);
|
||||||
|
if (fine != null) {
|
||||||
|
result = Success(fine);
|
||||||
|
} else {
|
||||||
|
result = Failure(UnknownFailure("Не удалось получить данные о штрафе"));
|
||||||
|
}
|
||||||
|
} on AuthException catch (e) {
|
||||||
|
result = Failure(AuthFailure(e.attemptsLeft));
|
||||||
|
} on UnauthorizedException catch (e) {
|
||||||
|
result = Failure(AuthFailure(0, message: e.toString()));
|
||||||
|
} on AuthBlockException catch (e) {
|
||||||
|
result = Failure(AuthBlockFailure(e.toString()));
|
||||||
|
} catch (e) {
|
||||||
|
result = Failure(UnknownFailure("Неизвестная ошибка"));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Result<void>> payFine({
|
||||||
|
required int orderId,
|
||||||
|
int? cardId,
|
||||||
|
required bool isBalance,
|
||||||
|
}) async {
|
||||||
|
late final Result<void> result;
|
||||||
|
try {
|
||||||
|
await _apiService.payFine(
|
||||||
|
orderId: orderId,
|
||||||
|
cardId: cardId,
|
||||||
|
isBalance: isBalance,
|
||||||
|
);
|
||||||
|
result = Success(null);
|
||||||
|
} on AuthException catch (e) {
|
||||||
|
result = Failure(AuthFailure(e.attemptsLeft));
|
||||||
|
} on UnauthorizedException catch (e) {
|
||||||
|
result = Failure(AuthFailure(0, message: e.toString()));
|
||||||
|
} on AuthBlockException catch (e) {
|
||||||
|
result = Failure(AuthBlockFailure(e.toString()));
|
||||||
|
} catch (e) {
|
||||||
|
result = Failure(UnknownFailure("Неизвестная ошибка"));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,7 +116,13 @@ final getIt = GetIt.instance;
|
|||||||
Future<void> setupDependencies() async {
|
Future<void> setupDependencies() async {
|
||||||
final sharedPreferences = await SharedPreferences.getInstance();
|
final sharedPreferences = await SharedPreferences.getInstance();
|
||||||
final dio = Dio();
|
final dio = Dio();
|
||||||
dio.interceptors.add(LogInterceptor(/*requestHeader: false, responseHeader:false, */responseBody: true, requestBody: true));
|
dio.interceptors.add(
|
||||||
|
LogInterceptor(
|
||||||
|
/*requestHeader: false, responseHeader:false, */
|
||||||
|
responseBody: true,
|
||||||
|
requestBody: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
dio.interceptors.add(AuthInterceptor());
|
dio.interceptors.add(AuthInterceptor());
|
||||||
// HTTP
|
// HTTP
|
||||||
getIt.registerSingleton<http.Client>(http.Client());
|
getIt.registerSingleton<http.Client>(http.Client());
|
||||||
@@ -299,15 +305,18 @@ Future<void> setupDependencies() async {
|
|||||||
() => NotificationsBloc(getIt<GetNotificationsUsecase>()),
|
() => NotificationsBloc(getIt<GetNotificationsUsecase>()),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
// Blocs
|
// Blocs
|
||||||
getIt.registerLazySingleton<SplashBloc>(() => SplashBloc(getIt()));
|
getIt.registerLazySingleton<SplashBloc>(() => SplashBloc(getIt()));
|
||||||
|
|
||||||
getIt.registerFactory<PhoneAuthBloc>(() => PhoneAuthBloc(getIt()));
|
getIt.registerFactory<PhoneAuthBloc>(() => PhoneAuthBloc(getIt()));
|
||||||
|
|
||||||
getIt.registerFactory<VerifyCodeBloc>(() => VerifyCodeBloc(getIt(), getIt(), getIt()));
|
getIt.registerFactory<VerifyCodeBloc>(
|
||||||
|
() => VerifyCodeBloc(getIt(), getIt(), getIt()),
|
||||||
|
);
|
||||||
|
|
||||||
getIt.registerLazySingleton<ProfileBloc>(() => ProfileBloc(getIt(), getIt(), getIt()));
|
getIt.registerLazySingleton<ProfileBloc>(
|
||||||
|
() => ProfileBloc(getIt(), getIt(), getIt()),
|
||||||
|
);
|
||||||
|
|
||||||
getIt.registerFactory<EditProfileBloc>(
|
getIt.registerFactory<EditProfileBloc>(
|
||||||
() => EditProfileBloc(getIt(), getIt()),
|
() => EditProfileBloc(getIt(), getIt()),
|
||||||
@@ -335,7 +344,6 @@ Future<void> setupDependencies() async {
|
|||||||
|
|
||||||
getIt.registerFactory<AddCardBloc>(() => AddCardBloc(getIt()));
|
getIt.registerFactory<AddCardBloc>(() => AddCardBloc(getIt()));
|
||||||
|
|
||||||
|
|
||||||
getIt.registerFactory<PaymentMethodSheetBloc>(
|
getIt.registerFactory<PaymentMethodSheetBloc>(
|
||||||
() => PaymentMethodSheetBloc(getIt(), getIt(), getIt(), getIt()),
|
() => PaymentMethodSheetBloc(getIt(), getIt(), getIt(), getIt()),
|
||||||
);
|
);
|
||||||
@@ -352,7 +360,6 @@ Future<void> setupDependencies() async {
|
|||||||
|
|
||||||
getIt.registerFactory<NewsBloc>(() => NewsBloc(getIt<NewsRepository>()));
|
getIt.registerFactory<NewsBloc>(() => NewsBloc(getIt<NewsRepository>()));
|
||||||
|
|
||||||
|
|
||||||
getIt.registerFactory<SendPhotoBloc>(() => SendPhotoBloc(getIt(), getIt()));
|
getIt.registerFactory<SendPhotoBloc>(() => SendPhotoBloc(getIt(), getIt()));
|
||||||
|
|
||||||
getIt.registerFactory<SubscriptionListBloc>(
|
getIt.registerFactory<SubscriptionListBloc>(
|
||||||
@@ -385,21 +392,24 @@ Future<void> setupDependencies() async {
|
|||||||
);
|
);
|
||||||
|
|
||||||
getIt.registerFactory<ScooterCodeBloc>(
|
getIt.registerFactory<ScooterCodeBloc>(
|
||||||
() => ScooterCodeBloc(getScooterByTitleUsecase: getIt<GetScooterByTitleUsecase>()),
|
() => ScooterCodeBloc(
|
||||||
|
getScooterByTitleUsecase: getIt<GetScooterByTitleUsecase>(),
|
||||||
|
getAddressUsecase: getIt<GetAddressByPointUsecase>(),
|
||||||
|
getPedestrianRoutesUsecase: getIt<GetPedestrianRoutesUsecase>()
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
// Repository
|
// Repository
|
||||||
getIt.registerSingleton<PromoCodeRepository>(
|
getIt.registerSingleton<PromoCodeRepository>(
|
||||||
PromoCodeRepositoryImpl(getIt<ApiService>()),
|
PromoCodeRepositoryImpl(getIt<ApiService>()),
|
||||||
);
|
);
|
||||||
|
|
||||||
// UseCase
|
// UseCase
|
||||||
getIt.registerSingleton<ApplyPromoCodeUsecase>(
|
getIt.registerSingleton<ApplyPromoCodeUsecase>(
|
||||||
ApplyPromoCodeUsecase(getIt<PromoCodeRepository>()),
|
ApplyPromoCodeUsecase(getIt<PromoCodeRepository>()),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Bloc (factory, т.к. экран создаёт новый экземпляр)
|
// Bloc (factory, т.к. экран создаёт новый экземпляр)
|
||||||
getIt.registerFactory<PromoCodeBloc>(
|
getIt.registerFactory<PromoCodeBloc>(
|
||||||
() => PromoCodeBloc(getIt<ApplyPromoCodeUsecase>()),
|
() => PromoCodeBloc(getIt<ApplyPromoCodeUsecase>()),
|
||||||
);
|
);
|
||||||
|
|||||||
76
lib/domain/entities/fine.dart
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
|
||||||
|
class Fine extends Equatable {
|
||||||
|
final int id;
|
||||||
|
final String scooterNumber;
|
||||||
|
final String date;
|
||||||
|
final String description;
|
||||||
|
final double amount;
|
||||||
|
final String amountPrint;
|
||||||
|
final List<String> photoUrls;
|
||||||
|
final bool isPaid;
|
||||||
|
|
||||||
|
const Fine({
|
||||||
|
required this.id,
|
||||||
|
required this.scooterNumber,
|
||||||
|
required this.date,
|
||||||
|
required this.description,
|
||||||
|
required this.amount,
|
||||||
|
required this.amountPrint,
|
||||||
|
required this.photoUrls,
|
||||||
|
required this.isPaid,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory Fine.fromJson(Map<String, dynamic> json, {String? scooterNumber}) {
|
||||||
|
final List<dynamic> filesJson = json['files'] ?? [];
|
||||||
|
final List<String> photos = filesJson
|
||||||
|
.map((file) => file['path'] as String? ?? file['filename'] as String? ?? '')
|
||||||
|
.where((path) => path.isNotEmpty)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
// Форматирование даты из createdAt (2026-08-07T09:15:26.565Z -> 07-08-2026)
|
||||||
|
String formattedDate = '';
|
||||||
|
if (json['createdAt'] != null) {
|
||||||
|
final parsedDate = DateTime.tryParse(json['createdAt'].toString());
|
||||||
|
if (parsedDate != null) {
|
||||||
|
final day = parsedDate.day.toString().padLeft(2, '0');
|
||||||
|
final month = parsedDate.month.toString().padLeft(2, '0');
|
||||||
|
final year = parsedDate.year;
|
||||||
|
formattedDate = '$day-$month-$year';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Fine(
|
||||||
|
id: json['id'] as int? ?? 0,
|
||||||
|
scooterNumber: scooterNumber ?? '',
|
||||||
|
date: formattedDate,
|
||||||
|
description: json['description'] as String? ?? '',
|
||||||
|
amount: (json['value'] as num?)?.toDouble() ?? 0.0,
|
||||||
|
amountPrint: json['valuePrint'] as String? ?? '0,00 BYN',
|
||||||
|
photoUrls: photos,
|
||||||
|
isPaid: json['isPaid'] as bool? ?? false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'id': id,
|
||||||
|
'description': description,
|
||||||
|
'value': amount,
|
||||||
|
'valuePrint': amountPrint,
|
||||||
|
'isPaid': isPaid,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [
|
||||||
|
id,
|
||||||
|
scooterNumber,
|
||||||
|
date,
|
||||||
|
description,
|
||||||
|
amount,
|
||||||
|
amountPrint,
|
||||||
|
photoUrls,
|
||||||
|
isPaid,
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -27,11 +27,12 @@ class ScooterOrder {
|
|||||||
final DateTime? expiresAt;
|
final DateTime? expiresAt;
|
||||||
final DateTime? cancelAt;
|
final DateTime? cancelAt;
|
||||||
final String? cancelDescription;
|
final String? cancelDescription;
|
||||||
final double mileage; //эту
|
final double mileage;
|
||||||
final double avgSpeed; //эту и снизу еще 4 есть
|
final double avgSpeed;
|
||||||
final String payTotalPricePrint;
|
final String payTotalPricePrint;
|
||||||
final String unpayTotalPricePrint;
|
final String unpayTotalPricePrint;
|
||||||
final String totalOrderPricePrint;
|
final String totalOrderPricePrint;
|
||||||
|
final bool hasFine;
|
||||||
|
|
||||||
ScooterOrder({
|
ScooterOrder({
|
||||||
required this.id,
|
required this.id,
|
||||||
@@ -65,6 +66,7 @@ class ScooterOrder {
|
|||||||
required this.payTotalPricePrint, // оплачено
|
required this.payTotalPricePrint, // оплачено
|
||||||
required this.unpayTotalPricePrint, //неоплачено
|
required this.unpayTotalPricePrint, //неоплачено
|
||||||
required this.totalOrderPricePrint, //общая
|
required this.totalOrderPricePrint, //общая
|
||||||
|
required this.hasFine,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory ScooterOrder.fromJson(Map<String, dynamic> json) {
|
factory ScooterOrder.fromJson(Map<String, dynamic> json) {
|
||||||
@@ -114,6 +116,7 @@ class ScooterOrder {
|
|||||||
payTotalPricePrint: paymentInfo?['payTotalPricePrint'] ?? '',
|
payTotalPricePrint: paymentInfo?['payTotalPricePrint'] ?? '',
|
||||||
unpayTotalPricePrint: paymentInfo?['unpayTotalPricePrint'] ?? '',
|
unpayTotalPricePrint: paymentInfo?['unpayTotalPricePrint'] ?? '',
|
||||||
totalOrderPricePrint: paymentInfo?['totalOrderPricePrint'] ?? '',
|
totalOrderPricePrint: paymentInfo?['totalOrderPricePrint'] ?? '',
|
||||||
|
hasFine: paymentInfo?['hasFine'] ?? false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import 'package:be_happy/domain/entities/active_scooter_order.dart';
|
|||||||
|
|
||||||
import '../../core/result.dart';
|
import '../../core/result.dart';
|
||||||
import '../entities/client_subscription.dart';
|
import '../entities/client_subscription.dart';
|
||||||
|
import '../entities/fine.dart';
|
||||||
import '../entities/point.dart';
|
import '../entities/point.dart';
|
||||||
import '../entities/scooter.dart';
|
import '../entities/scooter.dart';
|
||||||
import '../entities/subscription.dart';
|
import '../entities/subscription.dart';
|
||||||
@@ -53,4 +54,12 @@ abstract class ScooterRepository {
|
|||||||
|
|
||||||
Future<Result<List<Point>>> getScooterOrderRouteHistory(int id);
|
Future<Result<List<Point>>> getScooterOrderRouteHistory(int id);
|
||||||
|
|
||||||
|
Future<Result<Fine>> getFineDetails(int orderId);
|
||||||
|
|
||||||
|
Future<Result<void>> payFine({
|
||||||
|
required int orderId,
|
||||||
|
int? cardId,
|
||||||
|
required bool isBalance,
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
14
lib/domain/usecase/get_fine_details_usecase.dart
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import 'package:be_happy/core/result.dart';
|
||||||
|
import '../entities/fine.dart';
|
||||||
|
import '../repositories/scooter_repository.dart';
|
||||||
|
|
||||||
|
|
||||||
|
class GetFineDetailsUsecase {
|
||||||
|
final ScooterRepository repository;
|
||||||
|
|
||||||
|
GetFineDetailsUsecase(this.repository);
|
||||||
|
|
||||||
|
Future<Result<Fine>> call(int orderId) {
|
||||||
|
return repository.getFineDetails(orderId);
|
||||||
|
}
|
||||||
|
}
|
||||||
21
lib/domain/usecase/pay_fine_usecase.dart
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import '../../core/result.dart';
|
||||||
|
import '../entities/fine.dart';
|
||||||
|
import '../repositories/scooter_repository.dart';
|
||||||
|
|
||||||
|
class PayFineUsecase {
|
||||||
|
final ScooterRepository repository;
|
||||||
|
|
||||||
|
PayFineUsecase(this.repository);
|
||||||
|
|
||||||
|
Future<Result<void>> call({
|
||||||
|
required int orderId,
|
||||||
|
int? cardId,
|
||||||
|
required bool isBalance,
|
||||||
|
}) {
|
||||||
|
return repository.payFine(
|
||||||
|
orderId: orderId,
|
||||||
|
cardId: cardId,
|
||||||
|
isBalance: isBalance,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
import 'package:be_happy/domain/entities/map_settings.dart';
|
import 'package:be_happy/domain/entities/map_settings.dart';
|
||||||
|
|
||||||
import '../repositories/app_settings_repository.dart';
|
import '../repositories/app_settings_repository.dart';
|
||||||
|
|||||||
50
lib/presentation/components/full_screen_image_viewer.dart
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class FullScreenImageViewer extends StatefulWidget {
|
||||||
|
final List<String> photoUrls;
|
||||||
|
final int initialIndex;
|
||||||
|
|
||||||
|
const FullScreenImageViewer({
|
||||||
|
super.key,
|
||||||
|
required this.photoUrls,
|
||||||
|
required this.initialIndex,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<FullScreenImageViewer> createState() => _FullScreenImageViewerState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FullScreenImageViewerState extends State<FullScreenImageViewer> {
|
||||||
|
late PageController _pageController;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_pageController = PageController(initialPage: widget.initialIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: Colors.black,
|
||||||
|
appBar: AppBar(
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
iconTheme: const IconThemeData(color: Colors.white),
|
||||||
|
),
|
||||||
|
body: PageView.builder(
|
||||||
|
controller: _pageController,
|
||||||
|
itemCount: widget.photoUrls.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final isNetwork = widget.photoUrls[index].startsWith('http');
|
||||||
|
return InteractiveViewer(
|
||||||
|
child: Center(
|
||||||
|
child: isNetwork
|
||||||
|
? Image.network(widget.photoUrls[index])
|
||||||
|
: Image.asset(widget.photoUrls[index]),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -101,7 +101,6 @@ class _ScooterBottomSheetState extends State<ScooterBottomSheet> {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
// PageView с динамическим списком
|
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 220,
|
height: 220,
|
||||||
child: PageView.builder(
|
child: PageView.builder(
|
||||||
@@ -115,8 +114,6 @@ class _ScooterBottomSheetState extends State<ScooterBottomSheet> {
|
|||||||
|
|
||||||
return AnimatedContainer(
|
return AnimatedContainer(
|
||||||
duration: const Duration(milliseconds: 250),
|
duration: const Duration(milliseconds: 250),
|
||||||
// 2. Добавляем левый отступ ТОЛЬКО для первого элемента,
|
|
||||||
// чтобы он совпадал с заголовком
|
|
||||||
margin: EdgeInsets.only(
|
margin: EdgeInsets.only(
|
||||||
left: index == 0 ? 20 : 0,
|
left: index == 0 ? 20 : 0,
|
||||||
right: 16,
|
right: 16,
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ class _ScooterBottomSheetState extends State<ScooterBottomSheet> {
|
|||||||
if (state.status == ScooterDetailModalStatus.success) {
|
if (state.status == ScooterDetailModalStatus.success) {
|
||||||
return Dismissible(
|
return Dismissible(
|
||||||
key: const Key('scooter-modal'),
|
key: const Key('scooter-modal'),
|
||||||
direction: DismissDirection.down, // Закрытие только вниз
|
direction: DismissDirection.down,
|
||||||
onDismissed: (_) => context.pop(),
|
onDismissed: (_) => context.pop(),
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.bottomCenter,
|
alignment: Alignment.bottomCenter,
|
||||||
@@ -79,7 +79,6 @@ class _ScooterBottomSheetState extends State<ScooterBottomSheet> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
// Header с адресом (без изменений)
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -123,13 +122,11 @@ class _ScooterBottomSheetState extends State<ScooterBottomSheet> {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
// PageView с динамическим списком
|
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 220,
|
height: 220,
|
||||||
child: PageView.builder(
|
child: PageView.builder(
|
||||||
controller: _pageController,
|
controller: _pageController,
|
||||||
padEnds: false,
|
padEnds: false,
|
||||||
// Оставляем false, чтобы первый элемент прилипал к левому краю
|
|
||||||
itemCount: state.scooters!.length,
|
itemCount: state.scooters!.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final scooter = state.scooters![index];
|
final scooter = state.scooters![index];
|
||||||
|
|||||||
29
lib/presentation/event/fine_payment_event.dart
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import '../../domain/entities/payment_card.dart';
|
||||||
|
|
||||||
|
abstract class FinePaymentEvent extends Equatable {
|
||||||
|
const FinePaymentEvent();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class FetchFineDetails extends FinePaymentEvent {
|
||||||
|
final int fineId;
|
||||||
|
const FetchFineDetails(this.fineId);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [fineId];
|
||||||
|
}
|
||||||
|
|
||||||
|
class FinePaymentCardChanged extends FinePaymentEvent {
|
||||||
|
final PaymentCard card;
|
||||||
|
const FinePaymentCardChanged(this.card);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [card];
|
||||||
|
}
|
||||||
|
|
||||||
|
class SelectFineBalancePressed extends FinePaymentEvent {}
|
||||||
|
|
||||||
|
class PayFineSubmitted extends FinePaymentEvent {}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:be_happy/presentation/screens/card_binding_result_screen.dart';
|
import 'package:be_happy/presentation/screens/card_binding_result_screen.dart';
|
||||||
|
import 'package:be_happy/presentation/screens/lease_agreement_screen.dart';
|
||||||
|
import 'package:be_happy/presentation/screens/payment_rules_screen.dart';
|
||||||
import 'package:bot_toast/bot_toast.dart';
|
import 'package:bot_toast/bot_toast.dart';
|
||||||
import 'package:be_happy/core/app_colors.dart';
|
import 'package:be_happy/core/app_colors.dart';
|
||||||
import 'package:be_happy/di/service_locator.dart';
|
import 'package:be_happy/di/service_locator.dart';
|
||||||
|
|||||||
@@ -25,14 +25,14 @@ class DocumentsScreen extends StatelessWidget {
|
|||||||
LinkRow(
|
LinkRow(
|
||||||
icon: 'assets/icons/doc.png',
|
icon: 'assets/icons/doc.png',
|
||||||
title: 'Договор аренды',
|
title: 'Договор аренды',
|
||||||
onTap: () => openLink('https://...'),
|
onTap: () => context.push('/home/documents/lease-agreement'),
|
||||||
),
|
),
|
||||||
const Divider(height: 1, color: Colors.white24),
|
const Divider(height: 1, color: Colors.white24),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
LinkRow(
|
LinkRow(
|
||||||
icon: 'assets/icons/doc.png',
|
icon: 'assets/icons/doc.png',
|
||||||
title: 'Политика конфиденциальности',
|
title: 'Политика конфиденциальности',
|
||||||
onTap: () => context.push('/privacy-policy')
|
onTap: () => context.push('/privacy-policy'),
|
||||||
),
|
),
|
||||||
const Divider(height: 1, color: Colors.white24),
|
const Divider(height: 1, color: Colors.white24),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
@@ -46,7 +46,7 @@ class DocumentsScreen extends StatelessWidget {
|
|||||||
LinkRow(
|
LinkRow(
|
||||||
icon: 'assets/icons/doc.png',
|
icon: 'assets/icons/doc.png',
|
||||||
title: 'Правила оплаты картой',
|
title: 'Правила оплаты картой',
|
||||||
onTap: () => openLink('https://...'),
|
onTap: () => context.push('/home/documents/payment-rules'),
|
||||||
),
|
),
|
||||||
const Divider(height: 1, color: Colors.white24),
|
const Divider(height: 1, color: Colors.white24),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
|
|||||||
421
lib/presentation/screens/fine_payment_screen.dart
Normal file
@@ -0,0 +1,421 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import '../../core/app_colors.dart';
|
||||||
|
import '../../di/service_locator.dart';
|
||||||
|
import '../../domain/entities/payment_card.dart';
|
||||||
|
import '../../domain/usecase/add_payment_card_usecase.dart';
|
||||||
|
import '../../domain/usecase/get_payment_cards_usecase.dart';
|
||||||
|
import '../../domain/usecase/get_profile_usecase.dart';
|
||||||
|
import '../../domain/usecase/set_main_payment_card_usecase.dart';
|
||||||
|
import '../components/custom_app_bar.dart';
|
||||||
|
import '../components/full_screen_image_viewer.dart';
|
||||||
|
import '../components/gradient_button.dart';
|
||||||
|
import '../components/payment_option.dart';
|
||||||
|
import '../components/sheet/payment_method_sheet.dart';
|
||||||
|
import '../event/fine_payment_event.dart';
|
||||||
|
import '../event/payment_method_sheet_event.dart';
|
||||||
|
import '../state/fine_payment_state.dart';
|
||||||
|
import '../viewmodel/fine_payment_bloc.dart';
|
||||||
|
import '../viewmodel/payment_method_sheet_bloc.dart';
|
||||||
|
|
||||||
|
class FinePaymentScreen extends StatelessWidget {
|
||||||
|
final int fineId;
|
||||||
|
|
||||||
|
const FinePaymentScreen({
|
||||||
|
super.key,
|
||||||
|
required this.fineId,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => FinePaymentBloc(
|
||||||
|
getFineDetailsUsecase: getIt(),
|
||||||
|
payFineUsecase: getIt(),
|
||||||
|
)..add(FetchFineDetails(fineId)),
|
||||||
|
child: const _FinePaymentScreenContent(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FinePaymentScreenContent extends StatelessWidget {
|
||||||
|
const _FinePaymentScreenContent();
|
||||||
|
|
||||||
|
void _openFullScreenImage(BuildContext context, List<String> photos, int initialIndex) {
|
||||||
|
Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (_) => FullScreenImageViewer(
|
||||||
|
photoUrls: photos,
|
||||||
|
initialIndex: initialIndex,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: Container(
|
||||||
|
decoration: const BoxDecoration(gradient: AppColors.phoneScreenBg),
|
||||||
|
child: SafeArea(
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
Positioned(
|
||||||
|
bottom: 60,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
child: Image.asset(
|
||||||
|
'assets/wave.png',
|
||||||
|
fit: BoxFit.fitWidth,
|
||||||
|
color: Colors.white.withOpacity(0.1),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
const Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 20),
|
||||||
|
child: CustomAppBar(title: 'Оплата штрафа'),
|
||||||
|
),
|
||||||
|
|
||||||
|
Expanded(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
child: BlocConsumer<FinePaymentBloc, FinePaymentState>(
|
||||||
|
listenWhen: (previous, current) => previous.status != current.status,
|
||||||
|
listener: (context, state) {
|
||||||
|
if (state.status == FinePaymentStatus.success && state.paymentCompleted) {
|
||||||
|
context.go('/home');
|
||||||
|
} else if (state.status == FinePaymentStatus.failure) {
|
||||||
|
ScaffoldMessenger.of(context).hideCurrentSnackBar();
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(
|
||||||
|
state.errorMessage ?? 'Произошла ошибка при оплате штрафа',
|
||||||
|
style: const TextStyle(color: Colors.white),
|
||||||
|
),
|
||||||
|
backgroundColor: Colors.redAccent.withOpacity(0.9),
|
||||||
|
behavior: SnackBarBehavior.floating,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
margin: const EdgeInsets.all(20),
|
||||||
|
duration: const Duration(seconds: 3),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
builder: (context, state) {
|
||||||
|
final fine = state.fine;
|
||||||
|
|
||||||
|
if (state.status == FinePaymentStatus.loading && fine == null) {
|
||||||
|
return const SizedBox(
|
||||||
|
height: 400,
|
||||||
|
child: Center(
|
||||||
|
child: CircularProgressIndicator(color: Colors.white),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fine == null) return const SizedBox.shrink();
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
|
if (fine.photoUrls.isNotEmpty)
|
||||||
|
SizedBox(
|
||||||
|
height: 110,
|
||||||
|
child: ListView.separated(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
itemCount: fine.photoUrls.length,
|
||||||
|
separatorBuilder: (_, __) => const SizedBox(width: 12),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final photo = fine.photoUrls[index];
|
||||||
|
final isNetwork = photo.startsWith('http');
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () => _openFullScreenImage(
|
||||||
|
context,
|
||||||
|
fine.photoUrls,
|
||||||
|
index,
|
||||||
|
),
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
child: isNetwork
|
||||||
|
? Image.network(
|
||||||
|
photo,
|
||||||
|
width: 110,
|
||||||
|
height: 110,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
)
|
||||||
|
: Image.asset(
|
||||||
|
photo,
|
||||||
|
width: 110,
|
||||||
|
height: 110,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
_buildInfoLabel('Самокат:'),
|
||||||
|
_buildValueRow(
|
||||||
|
'qr_icon.png',
|
||||||
|
fine.scooterNumber,
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
|
_buildInfoLabel('Дата:'),
|
||||||
|
Text(
|
||||||
|
fine.date,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
|
_buildInfoLabel('Описание:'),
|
||||||
|
Text(
|
||||||
|
fine.description,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 28),
|
||||||
|
|
||||||
|
Container(
|
||||||
|
width: double.infinity,
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white.withOpacity(0.07),
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
border: Border.all(
|
||||||
|
color: Colors.white.withOpacity(0.2),
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Сумма штрафа:',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white.withOpacity(0.8),
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Image.asset(
|
||||||
|
'assets/icons/money_icon.png',
|
||||||
|
width: 36,
|
||||||
|
height: 36,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Text(
|
||||||
|
fine.amountPrint,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 28,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 28),
|
||||||
|
|
||||||
|
(state.useBalance || state.selectedCard != null)
|
||||||
|
? PaymentOption(
|
||||||
|
title: state.useBalance ? 'Баланс' : "",
|
||||||
|
subtitle: state.useBalance
|
||||||
|
? '${state.userBalance.toStringAsFixed(2)} BYN'
|
||||||
|
: '${state.selectedCard!.cardLastNumber}',
|
||||||
|
isSelected: true,
|
||||||
|
onTap: () async {
|
||||||
|
final result = await showModalBottomSheet<dynamic>(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
builder: (innerContext) => BlocProvider(
|
||||||
|
create: (context) => PaymentMethodSheetBloc(
|
||||||
|
getIt<GetPaymentCardsUsecase>(),
|
||||||
|
getIt<SetMainPaymentCardUsecase>(),
|
||||||
|
getIt<GetProfileUseCase>(),
|
||||||
|
getIt<AddPaymentCardUsecase>(),
|
||||||
|
)..add(PaymentMethodSheetStarted()),
|
||||||
|
child: PaymentMethodSheet(
|
||||||
|
initialSelectedCard: state.useBalance ? null : state.selectedCard,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result != null && context.mounted) {
|
||||||
|
if (result is PaymentCard) {
|
||||||
|
context.read<FinePaymentBloc>().add(FinePaymentCardChanged(result));
|
||||||
|
} else if (result == 'balance') {
|
||||||
|
context.read<FinePaymentBloc>().add(SelectFineBalancePressed());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: Container(
|
||||||
|
height: 56,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(24),
|
||||||
|
border: Border.all(
|
||||||
|
color: Colors.white.withOpacity(0.4),
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () async {
|
||||||
|
final result = await showModalBottomSheet<dynamic>(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
builder: (innerContext) => BlocProvider(
|
||||||
|
create: (context) => PaymentMethodSheetBloc(
|
||||||
|
getIt<GetPaymentCardsUsecase>(),
|
||||||
|
getIt<SetMainPaymentCardUsecase>(),
|
||||||
|
getIt<GetProfileUseCase>(),
|
||||||
|
getIt<AddPaymentCardUsecase>(),
|
||||||
|
)..add(PaymentMethodSheetStarted()),
|
||||||
|
child: const PaymentMethodSheet(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result != null && context.mounted) {
|
||||||
|
if (result is PaymentCard) {
|
||||||
|
context.read<FinePaymentBloc>().add(FinePaymentCardChanged(result));
|
||||||
|
} else if (result == 'balance') {
|
||||||
|
context.read<FinePaymentBloc>().add(SelectFineBalancePressed());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
borderRadius: BorderRadius.circular(24),
|
||||||
|
child: Center(
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'Способ оплаты',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Icon(
|
||||||
|
Icons.arrow_forward_ios,
|
||||||
|
size: 12,
|
||||||
|
color: Colors.white.withOpacity(0.6),
|
||||||
|
),
|
||||||
|
Icon(
|
||||||
|
Icons.arrow_forward_ios,
|
||||||
|
size: 12,
|
||||||
|
color: Colors.white.withOpacity(0.4),
|
||||||
|
),
|
||||||
|
Icon(
|
||||||
|
Icons.arrow_forward_ios,
|
||||||
|
size: 12,
|
||||||
|
color: Colors.white.withOpacity(0.2),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(24.0),
|
||||||
|
child: BlocBuilder<FinePaymentBloc, FinePaymentState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
final isButtonDisabled = state.status == FinePaymentStatus.loading ||
|
||||||
|
(!state.useBalance && state.selectedCard == null);
|
||||||
|
|
||||||
|
return GradientButton(
|
||||||
|
text: state.status == FinePaymentStatus.loading
|
||||||
|
? 'Обработка...'
|
||||||
|
: 'Оплатить',
|
||||||
|
showArrows: true,
|
||||||
|
height: 56,
|
||||||
|
width: double.infinity,
|
||||||
|
onTap: isButtonDisabled
|
||||||
|
? null
|
||||||
|
: () {
|
||||||
|
context.read<FinePaymentBloc>().add(PayFineSubmitted());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildInfoLabel(String text) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 6),
|
||||||
|
child: Text(
|
||||||
|
text,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white.withOpacity(0.7),
|
||||||
|
fontSize: 15,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildValueRow(String iconName, String value) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Image.asset('assets/icons/$iconName', width: 24, height: 24),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Text(
|
||||||
|
value,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
383
lib/presentation/screens/lease_agreement_screen.dart
Normal file
@@ -0,0 +1,383 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import '../../core/app_colors.dart';
|
||||||
|
import '../components/custom_app_bar.dart';
|
||||||
|
|
||||||
|
class LeaseAgreementScreen extends StatelessWidget {
|
||||||
|
const LeaseAgreementScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: Container(
|
||||||
|
width: double.infinity,
|
||||||
|
decoration: const BoxDecoration(gradient: AppColors.phoneScreenBg),
|
||||||
|
child: SafeArea(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
const Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 20),
|
||||||
|
child: CustomAppBar(title: ''),
|
||||||
|
),
|
||||||
|
|
||||||
|
Expanded(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 24),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'Договор (публичная оферта) на аренду имущества',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
|
_buildParagraph(
|
||||||
|
'ООО “БИХЕППИБЕЛ” Республика Беларусь, г. Витебск, г. Полоцк, г. Новополоцк, г. Борисов, г. Могилев, г. Молодечно, является «Арендодатель». Арендодатель (юридическое лицо) указывается при выборе объекта аренды в акте приема-передачи публичной оферты аренды имущества, с одной стороны и полностью дееспособное физическое лицо (гражданин РБ, иностранный гражданин, лицо без гражданства), акцептующее настоящий договор аренды, именуемое в дальнейшем «Арендатор», с другой стороны, совместно именуемые «Стороны» заключили настоящий Договор публичный оферты (далее - Договор) о нижеследующем: Стороны заключают настоящий Договор, который создает для Арендатора юридические обязательства. Арендодатель при заключение руководствуется законодательством об аренде имущества, Конвенция Совета Европы о защите физических лиц при автоматизированной обработке персональных данных от 28 января 1981 года ETS N 108. Если вы не согласны с каким-либо из условий настоящего Договора, пожалуйста, не переходите к сервису по аренде объекта в приложение BeHappy, не используйте любые услуги, предлагаемые через приложения, и не создавайте учетную запись для таких услуг. Используя электронные Платформы, вы соглашаетесь с условиями настоящего Договора. Вы подтверждаете, что внимательно прочитали и поняли условия настоящего Договора.',
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
_buildSectionHeader('1. Основные термины и определения, используемые в Договоре'),
|
||||||
|
_buildParagraph(
|
||||||
|
'В настоящем Договоре используются следующие термины, имеющие определенное ниже значение. В случае, если в Договоре используются термины и понятия, не определенные в настоящем разделе, они имеют значения, которые обычно придаются им в соответствующей отрасли/сфере.\n\n'
|
||||||
|
'1.1. Акцепт – ответ физического лица (гражданина РБ, иностранного гражданина, лица без гражданства), достигшего 18 лет и не состоящего под опекой или попечительством, о принятии настоящей Оферты, который совершается в порядке, предусмотренным настоящим Договором. Ответ должен быть полным и безоговорочным;\n\n'
|
||||||
|
'1.2. Аренда – использование имущества (объекта) в соответствии с его целевым назначением, с момента начала аренды до момента его завершения, по правилам, определенным настоящим Договором;\n\n'
|
||||||
|
'1.3. Арендатор - физическое лицо (гражданин РБ, иностранный гражданин, лицо без гражданства), достигшего 18 лет и не состоящего под опекой или попечительством;\n\n'
|
||||||
|
'1.4. Объект (имущество), технические характеристики объекта и собственник объекта указывается в акте-приема передачи в момент выбора объекта на аренду в приложение BeHappy;\n\n'
|
||||||
|
'1.5. Стоимость аренды объекта – расценка имущества указаны в личном кабинете Арендатора, отображаемые в Приложении BeHappy. Иные Тарифы (в т.ч. используемые в рамках отдельных акций) размещаются на Сайте и/или в Приложении " BeHappy " и являются неотъемлемой частью к настоящему Договору;\n\n'
|
||||||
|
'1.6. Безакцептное списание – списание денежных средств с банковской карты, указанной Арендатором во исполнение условий настоящего Договора. Списание денежных средств производится без получения дополнительного согласия со стороны Арендатора, по правилам и в порядке, предусмотренными законодательством Республики Беларусь и настоящим Договором;\n\n'
|
||||||
|
'1.7. Несезон – это то время, когда погодные или иные условия не позволяют предоставить качественный и безопасный сервис и Объекта изымаются из города без возможности аренды;\n\n'
|
||||||
|
'1.8. Служба поддержки – структурное подразделение Арендодателя, осуществляющее консультационную поддержку Пользователей в круглосуточном режиме семь дней в неделю, без выходных и праздничных дней, в дни работы сервиса (кроме несезона, см. п.1.7), оказывающая помощь Пользователям и их информирование в процессе предоставления Арендодателем Сервиса BeHappy. Поддержка и информирование осуществляются при обращении Пользователя в Службу поддержки через приложение в устройстве по мессенджерам (WhatsApp, Viber, Telegram);\n\n'
|
||||||
|
'1.9. Лицевой счет Арендатора - счётчик, на котором фиксируются платежи Арендатора и суммы денежных средств, внесенных в качестве оплаты за аренду имущества;\n\n'
|
||||||
|
'1.10. Зона начала/завершения аренды - территория города, обозначенная в приложении BeHappy - «Разрешено кататься», кроме территории, обозначенной «Парковка запрещена»;\n\n'
|
||||||
|
'1.11. Зона, разрешенная для поездок - территория города, обозначенная в приложении BeHappy - «Разрешено кататься»;\n\n'
|
||||||
|
'1.12. Парковка — место, отмеченное на карте в мобильном приложении Арендодателя, где Арендатор может завершить сессию аренды. Кроме территории, обозначенной «Парковка запрещена»;\n\n'
|
||||||
|
'1.13. Бронирование – закрепление выбранного Арендатором Объекта за данным Пользователем на период бронирования;\n\n'
|
||||||
|
'1.14. Групповая поездка — функция в Приложении, позволяющая одному Арендатору начать аренду сразу на нескольких Объектах.',
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
_buildSectionHeader('2. Предмет Договора и общие положения'),
|
||||||
|
_buildParagraph(
|
||||||
|
'2.1. Арендодатель передает во временное владение и пользование Арендатору Объект, а Арендатор принимает Объект для использования в потребительских целях (для личных нужд), не связанных с осуществлением предпринимательской деятельности;\n\n'
|
||||||
|
'2.2. Арендатор гарантирует, что не будет использовать Приложение BeHappy с нарушением требований действующего законодательства, а также условий Договора Аренды, либо в целях причинения вреда Арендодателю или третьим лицам;\n\n'
|
||||||
|
'2.3. На момент заключения настоящего договора сдаваемый в аренду Объект принадлежит Арендодателю на праве собственности. Переход права собственности на указанный Объект к другому лицу не является основанием для изменения или расторжения настоящего договора.',
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
_buildSectionHeader('3. Условия и порядок заключения Договора'),
|
||||||
|
_buildParagraph(
|
||||||
|
'3.1. На момент заключения договора Арендатор должен достигнуть 18-летнего возраста, не состоять под опекой или попечительством;\n\n'
|
||||||
|
'3.2. Акцепт считается совершенным Арендатором при условии выполнения им следующей совокупности действий:\n'
|
||||||
|
'3.2.1. Арендатор самостоятельно совершает загрузку (скачивание) на Устройство Приложения с использованием магазинов приложений AppStore (itunes.apple.com) и / или Google Play (play.google.com) в сети Интернет;\n'
|
||||||
|
'3.2.2. Заполняет анкету в полном объеме, предоставляя требуемую в этой анкете информацию: номер своего мобильного телефона, адрес электронной почты, имя фамилию и отчество, а также дату рождения, в соответствии с данными паспорта;\n'
|
||||||
|
'3.2.3. Ознакомление Арендатора в полном объеме с условиями настоящего Договора и политики конфиденциальности, путем нажатия кнопки «продолжить» при регистрации в Мобильном приложении, которую Арендодатель и Арендатор признают аналогом собственноручной подписи Арендатора, равным по юридической силе собственноручной подписи Арендатора на бумажном носителе и принятие условия настоящего Договора и Политики конфиденциальности путем проставления галочки в чек-боксе «Я принимаю условия использования сервиса» и «Я даю согласие на обработку моих персональных данных» и нажатия кнопки «продолжить»;\n\n'
|
||||||
|
'3.3. Арендатор не может завершить Регистрацию, не приняв условия настоящего Договора путем проставления галочки в чек-боксе «Я принимаю условия лицензионного договора», «Я принимаю условия договора оферты аренды» и «Я даю согласие на обработку моих персональных данных»;\n\n'
|
||||||
|
'3.4. Договор заключается Арендатором к установленным Договором условиям в целом, без каких-либо условий и оговорок;\n\n'
|
||||||
|
'3.5. Стороны договорились о том, что в случае возникновения у Арендодателя сомнений в достоверности указанных в Анкете персональных данных, он вправе через Мобильное приложение или иным способом направить запрос о предоставлении дополнительной информации о личности Арендатора, в том числе, фотографий страниц его паспорта, или иного документа, удостоверяющего личность. Стороны договорились, что при не предоставлении дополнительной информации, позволяющей устранить сомнения в достоверности указанных в Анкете персональных данных, акцепт может считаться не совершенным, а Договор может считаться не заключенным;\n\n'
|
||||||
|
'3.6. Арендодатель не несет ответственности или каких-либо обязательств в случае, если оферта не акцептована и Договор не заключен по причине невозможности обработки предоставленных Арендатором сведений и информации по техническим причинам;\n\n'
|
||||||
|
'3.7. Посредством акцепта настоящей оферты Арендатор подтверждает, что любые действия, осуществляемые в Мобильном приложении под его учетной записью, совершены лично Арендатором. Все риски, связанные с несанкционированным использованием учетной записи или мобильного устройства Арендатора, лежат на Арендаторе;\n\n'
|
||||||
|
'3.8. Посредством акцепта настоящей оферты Арендатор подтверждает с записью разговоров/сообщений/уведомлений при обращении в Службу Поддержки BeHappy;\n\n'
|
||||||
|
'3.9. Арендатор обязуется сохранять в тайне данные анкеты, а также иные данные, посредством которых может быть получен доступ к Приложению от имени Пользователя. В случае утраты номера телефона, или при наличии оснований полагать, что этими данными (а также иными данными) завладело третье лицо, Арендатор обязан с использованием контактных данных, указанных им при Регистрации, подать Службе Поддержки запрос (путем отправки на электронный адрес указанный в разделе мобильного приложения BeHappy ПОДДЕРЖКА. Запрос о блокировании доступа в личный кабинет рассматривается Арендодателем в течение 1 (одного) часа в рабочее время с момента направления запроса. Все риски, вытекающие из невыполнения данного обязательства Пользователем, лежат на Арендаторе;\n\n'
|
||||||
|
'3.10. По окончании Регистрации все вызовы в Службу поддержки осуществляются Арендатором с номера мобильного телефона, указанного при заполнении анкеты, либо иного номера телефона при условии предоставления Арендатором дополнительных данных по запросу сотрудника Службы поддержки;',
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
_buildSectionHeader('4. Заявления Арендатора'),
|
||||||
|
_buildParagraph(
|
||||||
|
'Принимая условия настоящего Договора, Арендатор заявляет, что он:\n\n'
|
||||||
|
'4.1. владеет русским языком (на уровне, достаточном для прочтения и осознания смысла и значения настоящего Договора);\n\n'
|
||||||
|
'4.2. прочитал условия Договора, осознал значение и смысл указанного документа, согласен с его содержанием и принимает его без каких-либо оговорок, условий и изъятий и обязуется выполнять установленные им требования, нести ответственность за неисполнение/ненадлежащее исполнение изложенных в нем требований и условий, а также понимает все последствия своих действий по Регистрации, пользованию Сервисами Приложения, Сервисом BeHappy;\n\n'
|
||||||
|
'4.3. Арендатор дает свое согласие на обработку Арендодателем предоставленных им (а также полученных от любых третьих лиц) своих персональных данных: ФИО, дата, месяц и год рождения, место рождения, серия и номер удостоверяющего личность документа (паспорта или иного заменяющего его документа), сведения об адресе регистрации по месту жительства, данные водительского удостоверения, контактные данные, в том числе (но не ограничиваясь) номер телефона, адрес электронной почты, данные о наличии (отсутствии) медицинских противопоказаний к использованию транспортного средства, биометрические данные (фотографии), данные о месте работы, должности, также иные персональные данные, связанные с заключением и исполнением Договора, и подтверждает, что давая такое согласие, действует по своей воле и в своих интересах;\n\n'
|
||||||
|
'4.4. прочитал Политику конфиденциальности, осознал значение и смысл указанного документа, согласен с его содержанием;\n\n'
|
||||||
|
'4.5. предоставил Арендодателю при Регистрации полные, действительные и достоверные данные (в том числе, персональные данные);\n\n'
|
||||||
|
'4.6. соответствует всем требованиям, предъявляемым к нему настоящим Договором, в частности, обладает правом управления транспортными средствами в соответствии с требованиями действующего законодательства, того государства, на чей территории подписывается Договор аренды, у Арендатора отсутствуют предусмотренные действующим законодательством, того государства (страны) противопоказания для управления транспортными средствами, его возраст составляет не менее 18 (восемнадцати) лет, он соответствует требованиям, предъявляемым действующим законодательством , того государства (страны), которое дает право управлять транспортными средствами. Указанные требования к Арендатору не являются исчерпывающими. Сервис BeHappy вправе отказать в Регистрации, а также не заключать Договор присоединения с Арендатором, не соответствующим указанным требованиям, равно как в случае, если у Сервиса BeHappy есть основания полагать, что Арендатор может допустить нарушение настоящего Договора;\n\n'
|
||||||
|
'4.7. согласен с размещением в Объекта, позволяющего отследить и зафиксировать точные координаты Объекта, а также нарушения обязательств по Договору со стороны Арендатора;\n\n'
|
||||||
|
'4.8. безусловно и безоговорочно согласен с тем, что размер причиненных Арендатором повреждений или иного рода ухудшений Объекту, а также иных убытков Арендодателю, возникших в результате ненадлежащего использования (эксплуатации) Арендатором Объекта и/или нарушения Арендатором иных условий Аренды по настоящему Договору, определяются (оцениваются) Арендодателем самостоятельно и единолично, исходя из имеющихся у Арендодателя данных и сведений, полученных, в том числе (но не ограничиваясь), при использовании данных технической поддержки Приложения " BeHappy " (включая, но не ограничиваясь, сведения и данные, в том числе фотографии, предоставленные Арендатором с использованием Приложения "BeHappy"), а Арендатор безусловно и безоговорочно соглашается возмещать их Арендодателю в полном объеме по первому требованию Арендодателя, заявленному (направленному) как с использованием Приложения " BeHappy ", так и предъявленному Арендатору в ином порядке и иным доступным способом;\n\n'
|
||||||
|
'4.9. признает юридическую силу за документами, направленными Арендодателем посредством Сайта или Приложения, как за собственноручно подписанными документами, за действиями, совершенными посредством Сайта или Приложения;\n\n'
|
||||||
|
'4.10. признает, что средства идентификации, используемые Арендодателем, являются достаточными для установления лица, от которого исходят соответствующие документы и действия;\n\n'
|
||||||
|
'4.11. Ознакомление с условиями настоящего Договора должно быть произведено Пользователем до момента проставления галочки в чек-боксе «Я принимаю условия использования сервиса» и «Я даю согласие на обработку моих персональных данных» и нажатия кнопки «продолжить». Арендатор, своевременно не ознакомившийся с условиями настоящего Договора, принимает на себя все риски связанных с этим неблагоприятных последствий.',
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
_buildSectionHeader('5. Содержание Договора'),
|
||||||
|
_buildParagraph(
|
||||||
|
'5.1. Арендодатель обязан:\n'
|
||||||
|
'5.1.1. предоставить Арендатору через Мобильное приложение и путем размещения в мобильном приложение BeHappy полной информации об объекте Арендодателя, его технических характеристиках, размере арендных платежей и прочих условиях Договора;\n'
|
||||||
|
'5.1.2. выставить в доступ Объекты в технически исправном состоянии;\n'
|
||||||
|
'5.1.3. нести расходы на содержание Объекта, возникающие в связи с его правильной эксплуатацией (за исключением расходов, которые прямо возложены на Арендатора Договором);\n'
|
||||||
|
'5.1.4. обеспечивать соблюдение требований законодательства при получении и обработке персональных данных Арендатора;\n'
|
||||||
|
'5.1.5. надлежащим образом исполнять обязательства, вытекающие из настоящего Договора.\n\n'
|
||||||
|
'5.2. Арендодатель вправе:\n'
|
||||||
|
'5.2.1. требовать от Арендатора исполнения обязательств по Договору;\n'
|
||||||
|
'5.2.2. осуществлять проверку сведений, в том числе персональных данных, предоставляемых Арендатором в момент регистрации в Мобильном приложении;\n'
|
||||||
|
'5.2.3. обрабатывать персональные данные Арендатора, предоставленные в момент регистрации в Мобильном приложении, а также полученные при заключении и исполнении настоящего Договора;\n'
|
||||||
|
'5.2.4. осуществлять контроль за обеспечением сохранности Объекта, а также за его техническим состоянием, контролировать соответствие эксплуатации Объекта целям, установленным настоящим Договором;\n'
|
||||||
|
'5.2.5. отказать в заключении Договора, в случае предоставления Арендатором в момент регистрации в Мобильном приложении недостоверных или неполных персональных данных;\n'
|
||||||
|
'5.2.6. отказать в заключении Договора, приостановить либо прекратить его действие, при наличии у Арендатора задолженности, вытекающей из настоящего и иных Договоров, заключенных между Арендатором и Арендодателем, до момента погашения задолженности в полном объеме, а также в случае нарушения Арендатором иных условий данного Договора;\n'
|
||||||
|
'5.2.7. в одностороннем порядке вносить изменения в настоящий Договор (в т.ч. в стоимость и условия аренды);\n'
|
||||||
|
'5.2.8. по истечении 24 часов после возникновения задолженности по оплате арендных платежей без предупреждения изъять Объект у Арендатора, а также прибегнуть к иным предусмотренным законодательством действующим законодательством, того государства (страны) противопоказания Республики Беларусь способам защиты своих прав;\n'
|
||||||
|
'5.2.9. в случае удаления Объекта Арендатором от Зоны, разрешенной для поездок, более чем на 150 метров, за отказ вернуться в разрешенную зону, за невозможность связаться по указанному номеру телефона, Арендодатель предпринимает меры для возврата Объекта в разрешенную зону: отправляет уведомление на телефон о факте нарушения и с просьбой вернуться в зону для катания, снижает скорость до 5 км/ч, блокирует колесо Объекта. Если после всех мер Арендодателя, Объект не вернулся в разрешенную зону, Арендодатель заявляет в правоохранительные органы о факте кражи, а также прибегнуть к иным предусмотренным законодательством Республики Беларусь способам защиты своих прав;\n'
|
||||||
|
'5.2.10. в случае наличия у Арендодателя достаточных оснований полагать о возможности совершения Арендатором мошеннических действий, а также иных проявлениях недобросовестного поведения, в одностороннем порядке прекратить исполнение по данному Договору, уведомив Арендатора через Мобильное приложение, а также прибегнуть к иным предусмотренным законодательством РБ способам защиты своих прав;\n'
|
||||||
|
'5.2.11. перевести Объект в режим экономии энергии (снижение скорости) в случае снижения заряда аккумулятора ниже 20%;\n'
|
||||||
|
'5.2.12. осуществлять списание с банковской карты Арендатора денежных средств в оплату денежных обязательств Арендатора перед Арендодателем в соответствии с Договором и Приложениями к нему, в том числе, в случаях, предусмотренных Договором, в безакцептном порядке (без получения дополнительного согласия Арендатора);\n'
|
||||||
|
'5.2.13. уступить свои права по взысканию задолженностей, образовавшихся за Аренду по настоящему Договору, любым третьим лицам, в соответствии с действующим законодательством без согласия Арендатора, но с уведомлением Арендатора;\n'
|
||||||
|
'5.2.14. осуществлять исходящие вызовы Арендатору и отправлять голосовые и иные сообщения уведомительного характера, содержащие информацию о состоянии задолженности, иную информацию, касающуюся предоставления Аренды, информацию об изменениях условий Договора (в т.ч. Тарифов), о новых услугах, новости, а также сообщения рекламного характера на номер мобильного телефона Арендатора, указанный при Регистрации (или на иные номера, сообщенные Арендатора Арендодателю на условиях настоящего Договора), а также направлять всю вышеуказанную информацию на адрес электронной почты, указанный при Регистрации (или на иной электронный адрес, сообщенный Арендатором Арендодателю на условиях настоящего Договора);\n\n'
|
||||||
|
'5.3. Арендатор вправе:\n'
|
||||||
|
'5.3.1. Арендатор вправе использовать Объект в сроки, в порядке и на условиях, предусмотренных настоящим Договором.\n\n'
|
||||||
|
'5.4. Арендатор обязан:\n'
|
||||||
|
'5.4.1. использовать сервисы иных сайтов приложения BeHappy, Сервисы Сайта и Приложения только в личных некоммерческих целях, не связанных с осуществлением предпринимательской деятельности и только в соответствии с условиями настоящего Договора;\n'
|
||||||
|
'5.4.2. Арендатор понимает и соглашается, что для надлежащего использования Сервисов Сайта и Приложения, он сам должен обеспечить надлежащую скорость интернет-подключения устройств. Арендатор перед использованием Сервисов Сайта и Приложения должен самостоятельно убедиться в том, что его устройство обладает необходимыми для этого характеристиками. Все вопросы приобретения прав доступа в Интернет, покупки и наладки для этого соответствующих устройств и программных продуктов решаются Пользователем самостоятельно и за свой счет и не подпадают под действие Договора;\n'
|
||||||
|
'5.4.3. до начала использования Объекта произвести его осмотр на предмет наличия повреждений;\n'
|
||||||
|
'5.4.4. при обнаружении повреждений или иного рода ухудшений Объекта до начала его использования, сообщить об этом Арендодателю путем отправки ему фотографии при помощи Мобильного приложения. В случае если Арендатор до начала использования не уведомил Арендодателя о существующих повреждениях или иного рода ухудшениях Объекта, Арендатор признает себя виновным в причинении повреждений во время Аренды им Объекта, если такие были обнаружены другим Арендатором или Арендодателем в дальнейшем;\n'
|
||||||
|
'5.4.5. при отсутствии повреждений на Объекта, принять Объект во временное владение и пользование в порядке, предусмотренном разделом 10 настоящего Договора;\n'
|
||||||
|
'5.4.6. использовать Объект в строгом соответствии с его целевым назначением без коммерческого использования и без использования, подразумевающего мошеннические действия;\n'
|
||||||
|
'5.4.7. своевременно, в соответствии с условиями настоящего Договора, вносить любые платежи, предусмотренные Договором и Приложениями к нему, обеспечивать остаток денежных средств на банковской карте в количестве, достаточном для осуществления всех платежей, предусмотренных настоящим Договором и Приложениями к нему;\n'
|
||||||
|
'5.4.8. осуществлять управление Объектом лично. Арендатор не вправе передавать Объект в пользование третьим лицам в субаренду;\n'
|
||||||
|
'5.4.9. Арендатор не вправе передавать Объект в пользование третьим лицам, не достигшим возраста 18 лет;\n'
|
||||||
|
'5.4.10. обеспечивать сохранность Объекта с момента приема до момента прекращения Аренды;\n'
|
||||||
|
'5.4.11. бережно использовать имущество Арендодателя. Принимать меры по предотвращению ущерба имуществу Арендодателя. В связи с этим, Арендатору запрещается ряд действий;\n'
|
||||||
|
'5.4.12. Арендатору запрещается использовать Объект на неровных поверхностях, вне тротуаров и дорожек/дорог с твердым покрытием, на песке, переезжать через любые ямы, выбоины, бордюры, горки, бугры, выпуклости и иные неровности, отличающиеся от основного полотна более чем на 3 см, равно как и имеющие острые края, либо иные особенности дорожного полотна, создающие дополнительную нагрузку для колес Объекта, а также лужи, глубиной более 2 см вне зависимости от возможности внешнего определения глубины;\n'
|
||||||
|
'5.4.13. Арендатору запрещается использовать Объект для езды по бездорожью, пересеченной местности;\n'
|
||||||
|
'5.4.14. Арендатору запрещается выполнять акробатические трюки, совершать прыжки, наезды на препятствия, бордюры, ступени, а также использовать Объект в соревнованиях;\n'
|
||||||
|
'5.4.15. Арендатору запрещается использовать Объект для транспортировки, буксирования грузов, передвигаться на Объекте более чем одному человеку, а также перевозить детей и животных, перемещаться на Объекте при превышении суммарного веса (с одеждой, сумкой, рюкзаком и т.д.) свыше 100 кг;\n'
|
||||||
|
'5.4.16. Арендатору запрещается оставлять Объект вовремя и после окончания аренды не пристегнутым, при условии оснащения объекта электромеханическим замком. Арендатору запрещается парковка Объекта в горизонтальном состоянии. Арендатору запрещается эксплуатировать Объект, если блокировочный трос не находится в замке, а также Арендатору запрещается оставлять Объект после окончания аренды не пристегнутым к стойке или другому парковочному объекту, указанному на карте в зоне Парковки. Так же запрещается, парковка Объекта в труднодоступных местах, пристегивание его к неразрешенным конструкциям или иным образом блокирование Объекта;\n'
|
||||||
|
'5.4.17. Арендатору запрещается складывать Объект (в том числе с использованием штатного механизма);\n'
|
||||||
|
'5.4.18. Арендатору запрещается снимать наклейки, идентификационные номера, штрихкоды, равно как и наносить надписи, наклеивать наклейки, наносить пометки, повреждать лакокрасочное покрытие Объекта, пачкать его (за исключением нормального загрязнения платформы уличной обувью);\n'
|
||||||
|
'5.4.19. Арендатору запрещается допускать полный разряд аккумулятора Объекта;\n'
|
||||||
|
'5.4.20. Арендатору запрещается использовать технологии или предпринимать действия, которые могут нанести вред сервисам иных сайтов мобильного приложения BeHappy, Объекту, иному имуществу Арендодателя;\n'
|
||||||
|
'5.4.21. Арендатору запрещается вносить любые изменения в Объект, изменять какие-либо характеристики, производить улучшения или ухудшения Объекта;\n'
|
||||||
|
'5.4.22. незамедлительно известить Арендодателя через Мобильное приложение о любых повреждениях Объекта, полученных во время Аренды, отключении любой из систем, иных неисправностях, дорожно-транспортных происшествиях с участием Объекта; нарушениях функционирования Мобильного приложения. При таких обстоятельствах Арендатор должен прекратить использование Объекта, о чем требуется проинформировать Арендодателя через Мобильное приложение BeHappy;\n'
|
||||||
|
'5.4.23. принимать надлежащие меры для защиты своей учетной записи, включая свои имя и пароль, в Мобильном приложении от несанкционированного использования другими лицами, и незамедлительно сообщить Арендодателю соответствующую информацию, в случае обнаружения такого использования;\n'
|
||||||
|
'5.4.24. Арендатор не вправе передавать данные своей учетной записи третьим лицам;\n'
|
||||||
|
'5.4.25. в случае возникновения у Арендодателя сомнений относительно правомочности использования зарегистрированной учетной записи, Арендодатель вправе направить запрос о предоставлении дополнительной информации о личности Арендатора, в том числе, фотографий страниц его паспорта, или иного документа, удостоверяющего личность. Стороны договорились, что при непредставлении ответа на запрос, Арендодатель вправе приостановить, а по истечении трех суток прекратить действие настоящего Договора;\n'
|
||||||
|
'5.4.26. своевременно, в соответствии с условиями настоящего Договора вносить арендную плату, оплачивать иные платежи, предусмотренные Договором, обеспечивать на банковской карте, указанной Арендатором во исполнение условий настоящего Договора, денежные средства в количестве, достаточном для оплаты Аренды Объекта;\n'
|
||||||
|
'5.4.27. своевременно, в соответствии с условиями настоящего Договора оплатить в полной мере аренду Объекта, даже если Объект из-за снижения заряда перешёл в экономный режим, либо отключился из-за того, что был разряжен во время предыдущего периода аренды этим Арендодателем;\n'
|
||||||
|
'5.4.28. в случае списания денежных средств со счета Арендатора в размере, не соответствующим тарифам, немедленно уведомить Арендодателя в разделе мобильного приложения BeHappy ПОДДЕРЖКА;\n'
|
||||||
|
'5.4.29. по завершении использования и прекращении аренды Арендатор обязан припарковать Объект в зоне начала/завершения аренды на велопарковке или иной парковке, разрешенной в Мобильном Приложении и не противоречащей действующим законам Республики Беларусь. В случае её отсутствия Объект следует припарковать на обочине дороги или на тротуаре, но таким образом, чтобы Объект не препятствовал движению пешеходов или транспортных средств и не находился на проезжей части. Арендатору запрещено допускать парковки Объекта на проезжей части дороги и на расстоянии ближе 1 метра от неё, на мостах и путепроводах, на железнодорожных переездах, в тоннелях, на эстокадах, мостах, путепроводах, вблизи опасных поворотов, на велосипедных дорожках, на пешеходных переходах (наземных, подземных и надземных) и на расстоянии ближе 5 метров от них, на тротуарах шириной менее 1,5 метров, на остановочных пунктах общественного транспорта и на расстоянии ближе 5 метров от них, ближе 15 метров от мест остановки маршрутных транспортных средств или стоянки легковых такси, на газонах и других участках с зелёными насаждениями (клумбы, цветники и иные подобные элементы благоустройства), на территориях, прилегающих к памятникам и мемориалам, ближе 10 метров от служебных дверей и рамп магазинов, через которые производятся погрузочно-разгрузочные работы, ворот, в том числе ворот гаражей, и на других участках, где может быть создано препятствие для движения пешеходов и транспорта, под навесами из железобетонных перекрытий;\n'
|
||||||
|
'5.4.30. при завершении использования Объекта и прекращении Аренды Арендатор обязан закрепить Объект тросом и замком к конструкции велопарковки (где она предусмотрена), в случае ее отсутствия к иной замкнутой металлической конструкции, в разрешенном, обозначенном на карте в Мобильном приложении месте. Трос должен быть закреплен через замкнутую часть конструкции, замок плотно закрыт. Парковка и закрытие замка подтверждается путем отправки фотографий Объекта через мобильное приложение. В случае отсутствия в конструкции Объекта механизма фиксации (замка), Арендатор обязан припарковать объект строго в разрешённом месте в соответствии с нанесённой парковочной разметкой, а в случае её отсутствия, припарковать объект так, чтобы он не создавал помех другим участникам движения и не нарушал правила благоустройства города;\n'
|
||||||
|
'5.4.31. объект должен быть возвращен Арендодателю в технически исправном состоянии, без каких-либо повреждений, в порядке, предусмотренном настоящим Договором;\n'
|
||||||
|
'5.4.32. во время использования Объекта Арендатор обязан соблюдать ПДД и нести ответственность за их нарушение. В этой связи, Арендатор при использовании Объекта, в частности, обязан:\n'
|
||||||
|
'5.4.32.1. безоговорочно уступать движение пешеходам;\n'
|
||||||
|
'5.4.32.2. осуществлять движение по велосипедной, велопешеходной дорожкам или полосе для велосипедистов не создавая препятствий;\n'
|
||||||
|
'5.4.32.3. в случае, если отсутствуют велосипедная и велопешеходная дорожки, полоса для велосипедистов либо отсутствует возможность двигаться по ним или по правому краю проезжей части, допускается движение по обочине не создавая препятствий;\n'
|
||||||
|
'5.4.32.4. в случае, если отсутствуют велосипедная и велопешеходная дорожки, полоса для велосипедистов либо отсутствует возможность двигаться по ним, а также по правому краю проезжей части или обочине, допускается движение по тротуару или пешеходной дорожке не создавая препятствий;\n'
|
||||||
|
'5.4.32.5. если движение по тротуару, пешеходной дорожке, обочине или в пределах пешеходных зон подвергает опасности или создает помехи для движения иных лиц, рекомендуется спешиться и уступить дорогу пешеходам;\n'
|
||||||
|
'5.4.32.6. при движении в темное время суток или в условиях недостаточной видимости необходимо включить фару и фонарь самоката, а также необходимо обозначить себя световозвращающими элементами и обеспечивать видимость этих предметов другим участникам дорожного движения;\n'
|
||||||
|
'5.4.32.7. пересекать проезжаю часть со скоростью идущего шагом пешехода по велосипедному переезду или пешеходному переходу, не создавая препятствий;\n'
|
||||||
|
'5.4.32.8. при использовании Объекта рекомендуется надевать защитный шлем и другую защиту.\n'
|
||||||
|
'5.4.33. Для предотвращения вероятности привлечения к ответственности за нарушение ПДД, Арендатору запрещается:\n'
|
||||||
|
'5.4.33.1. управлять Объектом в состоянии алкогольного, наркотического, токсического или иного опьянения, а также после приема медицинских препаратов вызывающих аналогичные свойства организма, равно как и отказываться от проведения процедуры медицинского освидетельствования по запросу уполномоченного должностного лица;\n'
|
||||||
|
'5.4.33.2. управлять Объектом, не держась за руль хотя бы одной рукой;\n'
|
||||||
|
'5.4.33.3. пересекать проезжую часть по надземным, наземным, подземным и иным переходам, не спускаясь с Объектом;\n'
|
||||||
|
'5.4.33.4. спускаться на Объекте в метро, катание непосредственно по зоне метро не допускается, заезжать на нем внутрь зданий, территорий ограниченного доступа и т.д.\n'
|
||||||
|
'5.4.34. не допускать причинение вреда жизни, здоровью и имуществу третьих лиц, в том числе других участников дорожного движения. В случае причинения вреда с использованием Объекта, Арендатор привлекается к ответственности в порядке, предусмотренном действующим законодательством Республики Беларусь;\n'
|
||||||
|
'5.4.35. своевременно, в соответствии с условиями настоящего договора, оплатить в полной мере аренду Объекта, даже если из-за снижения заряда аккумулятора во время аренды Арендатором Объекта перешел в экономный режим и/или колесо Объекта заблокировано;\n'
|
||||||
|
'5.4.36. парковать Объект на тактильных дорожках, предназначенных для перемещения людей с инвалидностью по зрению;\n'
|
||||||
|
'5.4.37. перевозить на Объекте других лиц.',
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
_buildSectionHeader('6. Гарантии и ответственность Арендодателя'),
|
||||||
|
_buildParagraph(
|
||||||
|
'6.1. Предоставляет Арендатору Сервисы иных Сайтов Приложения BeHappy, а также любую информацию, содержащуюся на Сайте и/или в Приложении «как есть» («as is») без гарантий любого рода. Это означает, среди прочего, что Арендодатель:\n'
|
||||||
|
'6.1.1. не несет ответственности за скорость и бесперебойную работу Сайта, Приложения, их совместимость с программным обеспечением и операционными системами устройств Пользователя, а также скорость интернет-подключения устройств;\n'
|
||||||
|
'6.1.2. не несет ответственности за отсутствие ошибок и/или вирусов при работе Сайта и Приложения, прерывание каналов связи и выход из строя серверной инфраструктуры на уровне магистральных каналов связи, центров обмена данными, вычислительных центров, а также линий связи регионального и местного значения;\n'
|
||||||
|
'6.1.3. не несет ответственности за временную неработоспособность платежных систем, обеспечивающую прием и перевод платы Арендатором, вызванную не зависящими от Арендодателя причинами, а также обстоятельствами непреодолимой силы;\n'
|
||||||
|
'6.1.4. не несет ответственности за нарушение условий Договора, если такое нарушение вызвано действием обстоятельств непреодолимой силы (форс-мажор), включая среди прочих: действия и решения органов государственной власти и/или местного самоуправления, пожар, наводнение, землетрясение, другие стихийные действия, отсутствие электроэнергии и/или сбои работы компьютерной сети, забастовки, гражданские волнения, беспорядки, любые иные обстоятельства, которые могут повлиять на выполнение Сервисом BeHappy условий настоящего Договора;\n'
|
||||||
|
'6.1.5. не несет ответственности за неисполнение либо ненадлежащее исполнение своих обязательств, если это вызвано действиями/бездействием Арендатора, находящимися вне контроля Арендодателя, в том числе в результате совершения ошибочных действий/бездействия Арендатора;\n\n'
|
||||||
|
'6.2. Сайт и Приложение могут содержать ссылки на сайты, приложения и иные ресурсы третьих лиц. Указанные сайты, приложения и иные ресурсы третьих лиц и их материалы не проверяются Арендодателем на соответствие требованиям законодательства и ответственность за их содержание Арендодатель не несет;\n\n'
|
||||||
|
'6.3. Действия любых третьих лиц, а также действия платежных систем, операторов связи не являются действиями Арендодателя, и ответственности за них Арендодатель не несет;\n\n'
|
||||||
|
'6.4. Арендодатель гарантирует добросовестную и законную обработку персональных данных Пользователя в соответствии с предусмотренными в настоящем Договоре целями;\n\n'
|
||||||
|
'6.5. Арендодатель гарантирует своевременное обновление данных Арендатора в случае предоставления им обновленных данных;\n\n'
|
||||||
|
'6.6. Арендодатель несёт ответственность только за прямой действительный ущерб, причиненный вследствие виновного невыполнения обязательств Арендодателя, в пределах суммы всех платежей за пользование имуществом (не включая возмещение расходов, штрафы и убытки), уплаченных Арендатору Арендодателем за весь срок, предшествующий предъявлению соответствующего требования, но не более 12 (двенадцати) месяцев.',
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
_buildSectionHeader('7. Гарантии и ответственность Арендатора'),
|
||||||
|
_buildParagraph(
|
||||||
|
'7.1. Арендатор лично (самостоятельно) отвечает за свою жизнь и здоровье во время аренды Объекта;\n\n'
|
||||||
|
'7.2. Риск случайной гибели или случайного повреждения Объекта с момента нажатия в Мобильном приложении кнопки «Начало поездки» и до момента завершения аренды лично несет Арендатор;\n\n'
|
||||||
|
'7.3. Арендатор несет лично (самостоятельно) полную ответственность за вред, причиненный третьим лицам, и их имуществу в результате использования Объекта;\n\n'
|
||||||
|
'7.4. Невозврат Объекта в соответствии с правилами настоящего Договора в течение разрешенного периода пользования, будет считаться утратой Объекта до момента его обнаружения. В случае утраты Объекта Арендатор обязуется возместить ущерб в размере восстановительной стоимости Объекта в сумме 3500 (трех тысяч пятьсот) рублей Республики Беларусь (далее РБ). В случае обнаружения Объекта после разрешенного периода пользования, Арендатор обязуется оплатить стоимость пользования Объекта до момента фактического возврата Объекта Арендодателю по предусмотренным тарифам, но не более восстановительной стоимости;\n\n'
|
||||||
|
'7.5. Удаление от разрешенной зоны катания более, чем на 800 метров во время аренды Объекта будет считаться попыткой кражи. Арендодатель будет обязан заявить в правоохранительные органы о факте кражи, а также прибегнуть к иным предусмотренным законодательством Республики Беларусь способам защиты своих прав;\n\n'
|
||||||
|
'7.6. В случае хищения Объекта до его возврата, Арендатор незамедлительно обязуется сообщить об этом в Службу поддержки сразу же, как только установит факт хищения. При этом Арендатор должен обратиться в правоохранительные органы с заявлением о хищении имущества и получить соответствующую справку из правоохранительных органов о возбуждении уголовного дела либо об отказе в возбуждении уголовного дела. Копию справки пользователь обязан предоставить в Службу поддержки указанные в разделе мобильного приложения BeHappy ПОДДЕРЖКА.\n\n'
|
||||||
|
'7.7. В случае обнаружения Объекта Арендодатель возвращает Арендатору списанные при утрате Объекта со счета банковской карты Арендатора денежные средства за вычетом стоимости повреждений обнаруженного Объекта, а также стоимости пользования Объекта. При этом Арендатор обязан предоставить справку из правоохранительных органов;\n\n'
|
||||||
|
'7.8. В случае дорожно-транспортного или иного происшествия с участием Арендатора на Объекте, Арендатор обязан сообщить об этом незамедлительно в Службу поддержки, контакты указаны в мобильном приложение BeHappy в разделе ПОДДЕРЖКА. В любом случае Арендатор остается ответственным за Объектом до момента возврата Объекта, либо передачи его представителю Арендодателя;\n\n'
|
||||||
|
'7.9. В случае причинения вреда (повреждения) Объекту, Арендатор обязан возместить Арендодателю причиненный ущерб в размере, определяемом Арендодателем исходя из прейскуранта перечня запчастей, указанного на сайте. В случае несогласия Арендатора с произведенным расчетом, он вправе обратиться к Арендодателю для разрешения споров в порядке, предусмотренном настоящим Договором;\n\n'
|
||||||
|
'7.10. Арендатор гарантирует, что он физически способен и умеет пользоваться объектом и обладает достаточным опытом для езды в городских условиях по проезжей части, по тротуарам и велодорожкам. Арендатор подтверждает, что введенные им при регистрации Пользователя данные соответствуют действительности, а также что Арендатору принадлежит банковская карта, указанная при регистрации Арендатора и денежных средств, находящихся на счете указанной банковской карты достаточно для оплаты услуг пользования Объекта и Арендатор обязуется пополнять счет указанной карты в размере, достаточном для оплаты услуг пользования Объектом. Арендатор настоящим уполномочивает банк-эмитент, выпустивший банковскую карту, указанную при регистрации Пользователя, а также процессинговый центр банка, с которым Арендодателем заключен договор эквайринга и /или платежного агента Арендодателя на списание денежных средств в размере причиненного вреда;\n\n'
|
||||||
|
'7.11. В случае невозврата или утраты Объекта Арендодателя в соответствии с правилами настоящего Договора, Арендатор несет ответственность за Объект в сумме 3500 (трех тысяч пятьсот) рублей Республики Беларусь;\n\n'
|
||||||
|
'7.12. Арендатор дает свое согласие на получение от Арендодателя рекламных сообщений, содержащих рекламу товаров и услуг, реализуемых Арендодателем и/или его партнерами и/или иными третьими лицами, на адрес электронной почты и номер мобильного телефона (включая привязанные к нему аккаунты в мессенджерах WhatsApp, Viber, Telegram и пр.), указанные Пользователем в процессе Регистрации, а также иные адреса электронной почты и номера мобильных телефонов (включая привязанные к ним аккаунты в мессенджерах WhatsApp, Viber, Telegram и пр.), сообщенные Пользователем Арендодателю в соответствии с настоящим Договором. В случае, если Арендатор обращается к Арендодателю с требованием прекратить распространение в его адрес рекламных сообщений, Арендодатель обязуется немедленно прекратить такое распространение в отношении Арендатора, обратившегося с соответствующим требованием;\n\n'
|
||||||
|
'7.13. В случае нарушения Арендатором условий Договора, Арендатор блокируется.',
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
_buildSectionHeader('8. Размер, порядок и форма оплаты'),
|
||||||
|
_buildParagraph(
|
||||||
|
'8.1. Осуществление арендных платежей, штрафов и иных платежей, предусмотренных настоящим Договором, производится посредством безакцептного списания денежных средств с банковской карты Арендатора в соответствие с тарифами Арендодателя;\n\n'
|
||||||
|
'8.2. При акцепте настоящей Оферты Арендатор выражает свое безоговорочное согласие на отправление Арендодателем поручений, запросов, требований банку Арендатора, а также на безакцептное списание денежных средств, составляющих арендные платежи, штрафы и иные платежи, предусмотренные настоящим Договором;\n\n'
|
||||||
|
'8.3. В момент регистрации у Арендатора в целях подтверждения верности введения данных банковской карты списывается сумма в размере до 1 (одного) рубля. Поступление вышеуказанных денежных средств на счет Арендодателя является одним из условий акцепта настоящего Договора. Данная денежная сумма не засчитывается в качестве арендного платежа и возвращается в течение суток (обычно сразу, но зависит от оператора) на банковскую карту Арендатора;\n\n'
|
||||||
|
'8.4. За пользование Объектом Арендатор уплачивает Арендодателю арендную плату согласно тарифов, обозначенных в Мобильном приложении;\n\n'
|
||||||
|
'8.5. В стоимость аренды Объекта, включены все налоги, в соответствии с законодательством страны, на чей территории арендуется Объект;\n\n'
|
||||||
|
'8.6. Арендодатель вправе производить списание с банковской карты Арендатора причитающихся ему платежей в любой момент до наступления момента завершения аренды (оплата аренды производится согласно условиям выбранного тарифа аренды Объекта и могут осуществляться как разовым списанием, так и списанием несколькими транзакциями, согласно условий тарифа);\n\n'
|
||||||
|
'8.7. В целях гарантии оплаты поездки Арендодатель имеет право заблокировать на карте Арендатора в момент начала аренды сумму в размере до 20 (двадцати) рублей. Данная денежная сумма может быть зачтена в качестве арендного платежа, если стоимость аренды более заблокированной суммы. По завершении аренды, если стоимость аренды менее заблокированной суммы, Арендодатель снимает блокировку и денежные средства остаются на карте Арендатора, а Арендодатель производит списание стоимости поездки. Поступление вышеуказанных денежных средств на счет Арендодателя является одним из условий акцепта настоящего Договора;\n\n'
|
||||||
|
'8.8. Списание денежных средств за пользование Объектом производится сразу после прекращения аренды. В случае, когда длительность аренды превышает 30 минут, то возможны промежуточные списания денежных средств. Также в начале, в зависимости от выбранного тарифного плана, аренды возможно резервирование денежный средств на банковской карте Арендатора.\n\n'
|
||||||
|
'8.9. В случае недостатка денежных средств или невозможности их списания со счета Арендатора, а также в случае достижения задолженности, Арендодатель вправе в принудительном порядке завершить аренду Объекта и установить блокировку учетной записи, не допускающей возобновление аренды, до момента полной оплаты Арендатором образовавшейся задолженности по Договору. Арендатор обязуется погасить возникшую задолженность в течение 48 часов с момента возникновения задолженности за аренду и отображавшиеся в личном кабинете Арендатора,обеспечив достаточный для проведения списания остаток средств на банковской карте, при этом до момента погашения возникшей задолженности Арендодатель вправе присвоить Пользователю статус должника и заблокировать ему доступ к Сервису BeHappy полностью или в части;\n\n'
|
||||||
|
'8.10. Списание штрафов и иных платежей, предусмотренных Договором, за исключением арендных платежей, производится в течение 30 (тридцати) календарных дней с даты направления Арендатору требования об оплате с использованием Мобильного приложения. В случае недостаточности денежных средств на счете Арендатора, любой возникший в рамках данного Договора платеж признается задолженностью;\n\n'
|
||||||
|
'8.11. В случае неисполнения Пользователем обязательств по оплате, предусмотренных настоящим Договором (в том числе, настоящим разделом Договора), Арендодатель вправе направить соответствующие сведения (включая персональные данные Пользователя) в уполномоченные органы для привлечения Пользователя к административной ответственности, а также в организации, осуществляющие взыскание денежных средств;\n\n'
|
||||||
|
'8.12. В случае ошибочного списания денежных средств с банковской карты Пользователя Арендатор вправе обратиться к Арендодателю по реквизитам указанные в разделе мобильного приложения BeHappy ПОДДЕРЖКА. Арендодатель вправе потребовать от Пользователя предоставить копию паспорта и документов, подтверждающих ошибочное списание (выписки со счета и пр.). Арендодатель не позднее 10 (десяти) рабочих дней рассматривает данное обращение и принимает по нему меры, за исключением случаев, когда денежные средства Пользователя были списаны в соответствии с Договором и/или Приложениями. Возврат денежных средств осуществляется на банковский счет Пользователя, с которого было произведено списание, в течение 14 (четырнадцати) рабочих дней, если более длительный срок не установлен внутренними банковскими процедурами. Порядок возврата денежных средств регулируется правилами международных платежных систем. Возврат наличными денежными средствами не допускается.\n\n'
|
||||||
|
'8.13. При отсутствии вины Арендатора в нарушении условий настоящего Договора Арендодатель в течение 7 календарных дней с даты направления Арендатором Претензии с использованием Мобильного приложения производит возврат ошибочно списанных денежных средств;\n\n'
|
||||||
|
'8.14. В случае нарушения Арендатором положений, предусмотренных п.п. 5.4.12, 5.4.13, 5.4.17, 5.4.29 настоящего Договора, Арендатор уплачивает штраф в размере до 400 (четырехсот) рублей Республики Беларусь;\n\n'
|
||||||
|
'8.15. В случае нарушения Арендатором положений, предусмотренных п.п. 5.4.4, 5.4.23, 5.4.24, настоящего Договора, Арендатор оплачивает штраф в размере до 300 (трехсот) рублей Республики Беларусь;\n\n'
|
||||||
|
'8.16. В случае нарушения Арендатором положений, предусмотренных п.п.5.4.32 (5.4.32.1 - 5.4.32.8), 5.4.33 (5.4.33.1 – 5.4.33.4), 5.4.37 настоящего Договора, Арендатор оплачивает штраф в размере до 500 (пятьсот) рублей Республики Беларусь;\n\n'
|
||||||
|
'8.17. В случае нарушения Арендатором положений, предусмотренных п.п.5.4.6, 5.4.8, 5.4.9 настоящего Договора, Арендатор оплачивает штраф в размере до 300 (трехсот) рублей;\n\n'
|
||||||
|
'8.18. В случае нарушения Арендатором положений, предусмотренных п.п. 5.4.31 настоящего Договора, Арендатор оплачивает штраф в размере до 2 000 (двух тысяч) рублей;\n\n'
|
||||||
|
'8.19. За нарушение ПДД на Арендатора возлагается ответственность, предусмотренная Кодексом об административных правонарушениях РБ. Если оплата штрафа за Арендатора была произведена Арендодателем, Арендатор обязан возместить Арендодателю понесенные им в этой связи расходы;\n\n'
|
||||||
|
'8.20. В случае причинения Арендатором во время использования Объектом вреда жизни, здоровью или имуществу третьих лиц, в том числе других участников дорожного движения, Арендатор обязуется в полном объеме возместить ущерб, причиненный его действиями как третьим лицам, так и Арендодателю;\n\n'
|
||||||
|
'8.21. В случае нарушения Арендатором положений, предусмотренных п.п. 5.4.30, 5.4.18, 5.4.19 настоящего Договора, Арендатор уплачивает штраф в размере до 100 (ста) рублей;\n\n'
|
||||||
|
'8.22. В случае утраты (кражи) объекта Арендатором применяется пункт 7.4 настоящего договора;\n\n'
|
||||||
|
'8.23. В случае повторного нарушения Арендатором положений, предусмотренных п.п. 5.4.29, которые не привели к утере или порче Объекта, Арендатор уплачивает штраф в размере до 300 (трехсот) рублей;\n\n'
|
||||||
|
'8.24. В случае нарушения Арендатором положений, предусмотренных п.п.8.9 настоящего Договора, Арендатор уплачивает арендодателю пеню в размере 2% за каждый день просрочки оплаты задолженности;\n\n'
|
||||||
|
'8.25. В случае нарушения Арендатором положений, предусмотренных п.п.5.4.14 настоящего Договора, Арендатор оплачивает штраф в размере до 30 (тридцати) рублей;\n\n'
|
||||||
|
'8.26. В случае нарушения Арендатором положений, предусмотренных п.п.5.4.15 настоящего Договора, Арендатор оплачивает штраф в размере до 100 (ста) рублей;',
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
_buildSectionHeader('9. Срок Аренды'),
|
||||||
|
_buildParagraph(
|
||||||
|
'9.1. Срок Аренды Объекта исчисляется минутами и часами и/или километрами и метрами. Это зависит от выбранного тарифа;\n\n'
|
||||||
|
'9.2. Период Аренды Объекта исчисляется с момента нажатия в Мобильном приложении кнопки «Начать поездку» до фактического прекращения Аренды Объекта Арендатором путем нажатия соответствующей кнопки в Мобильном приложении, и, при запросе Арендодателя, направления в качестве подтверждения фотографии общего вида, припаркованного Объекта через Мобильное приложение.',
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
_buildSectionHeader('10. Прием-передача от Арендодателя к Арендатору осуществляется следующим образом:'),
|
||||||
|
_buildParagraph(
|
||||||
|
'10.1. Арендатор при помощи Мобильного приложения выбирает на карте конкретный Объект. Арендатор при помощи камеры телефона сканирует QR штрихкод, размещенный на Объекте либо вводит номер самоката. При этом в Мобильном приложении отражаются следующие сведения о выбранном Объекте: местоположение, тариф, уровень заряда. Во время выбора Объекта Арендатор должен проверить достаточность денежных средств на банковской карте, привязанной к его аккаунту в Мобильном приложении;\n\n'
|
||||||
|
'10.2. Арендатор при помощи Мобильного приложения может забронировать конкретный Объект по тарифу и на срок, обозначенные в мобильном приложении. Бронирование может быть отменено Арендатором через мобильное приложение;\n\n'
|
||||||
|
'10.3. Арендодатель вправе отказать Арендатору в бронировании Объекта в случае отмены хотя бы одного бронирования Объекта;\n\n'
|
||||||
|
'10.4. Допускается бронирование нескольких Объектов одним Арендатором;\n\n'
|
||||||
|
'10.5. Допускается аренда более чем одного Объекта одним Арендатором с одного аккаунта — Групповая поездка. В этом случае Арендатор может передать Объект для управления только третьим лица достигшим возраста 18 лет. В случае групповой поездки ответственность за всё взятое в аренду имущество Арендодателя и за соблюдение условий аренду Объекта в рамках этого Договора несёт Арендатор, а не третье лицо, которому был передан Объект. Начиная аренду сразу нескольких Объектов Арендатор обязуется оплатить все начатые аренды от его имени и в случае нарушений условий договора, оплатить наложенные штрафные санкций на все поездки, начатые от его аккаунта;\n'
|
||||||
|
'В случае начала групповой поездки — каждая аренда начинается Арендатором отдельно, как и завершается. Максимальное число арендованных Объектов для Групповой поездки в одном аккаунте — 4 (четыре);\n\n'
|
||||||
|
'10.6. В момент нажатия кнопки «Начало поездки» в Мобильном приложении Арендатор подтверждает фактический прием выбранного Объекта и применяемого к его аренде тарифа и с этого момента начинает исчисляться период Аренды конкретного Объекта.',
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
_buildSectionHeader('11. Прием-передача Объекта от Арендатора к Арендодателю осуществляется следующим образом:'),
|
||||||
|
_buildParagraph(
|
||||||
|
'11.1. После выбора Объекта Арендатор подписывает акт приема-передачи Объекта в аренду, где указывается наименование объекта, технические характеристики Объекта. Реквизиты Арендодателя: наименование ,регистрационный номер регистрации юридического лица, юридический адрес, банковские реквизиты, контакты.\n'
|
||||||
|
'Арендатор паркует Объект в разрешенном месте в Зоне начала/завершения аренды, нажимает в Мобильном приложении кнопку «Завершить аренду», и по запросу Арендодателя отправляет через Мобильное приложение фотографию общего вида, припаркованного Объекта, а также пристегивает замком к парковке (при наличии таких мест и оборудовании Объектов замками);\n\n'
|
||||||
|
'11.2. После получения Арендодателем фотографий общего вида, припаркованного Объекта, заканчивается срок его Аренды. При соблюдении Арендатором предусмотренного Договором порядка приема-передачи Объекта, ему в Мобильном приложении приходит сообщение от Арендодателя с указанием суммы арендной платы, подлежащей к оплате.',
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
_buildSectionHeader('12. Срок действия Договора'),
|
||||||
|
_buildParagraph(
|
||||||
|
'12.1. Договор вступает в силу с момента акцепта Оферты в порядке, предусмотренным настоящим Договором;\n\n'
|
||||||
|
'12.2. Договор заключен сроком на 12 месяцев с даты акцепта. Если ни одна из Сторон за 10 календарных дней до окончания срока действия настоящего Договора письменно не сообщит другой о его прекращении, Договор считается продленным на следующие 12 месяцев на прежних условиях, при этом количество продлений не ограничено.',
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
_buildSectionHeader('13. Основания и порядок расторжения Договора'),
|
||||||
|
_buildParagraph(
|
||||||
|
'13.1. Настоящий Договор может быть расторгнут до истечения срока, предусмотренного настоящим Договором, по соглашению Сторон или по инициативе одной из Сторон выраженной в понятном обеим Сторонам виде;\n\n'
|
||||||
|
'13.2. Настоящий Договор может быть расторгнут досрочно в одностороннем внесудебном порядке по инициативе Арендодателя по следующим основаниям:\n'
|
||||||
|
'13.2.1. При неоднократном (более двух раз) нарушении Арендатором ПДД РБ при использовании Объекта;\n'
|
||||||
|
'13.2.2. При неоднократном (более двух раз) нарушении Арендатором правил парковки Объекта;\n'
|
||||||
|
'13.2.3. При установлении факта причинения Арендатором ущерба Объекта;\n'
|
||||||
|
'13.2.4. При установлении факта применения Арендатором технических средств, блокирующих GPS-сигнал, а также иных способов отключения защитных систем или систем контроля, установленных на Объекта;\n\n'
|
||||||
|
'13.3. При досрочном расторжении Договора по инициативе Арендодателя Договор считается расторгнутым с момента уведомления Арендатора через Мобильное приложение, сообщением на телефонный номер Арендатора или электронную почту, указанную при регистрации;\n\n'
|
||||||
|
'13.4. Настоящий Договор может быть расторгнут досрочно в одностороннем внесудебном порядке по инициативе Арендатора, в случае направления им запроса на удаление аккаунта, при условии отсутствия задолженности по арендным платежам, штрафам и иным платежам, предусмотренным настоящим Договором;\n\n'
|
||||||
|
'13.5. Запрос на удаление аккаунта направляется Арендатором Арендодателю на реквизиты указанные в разделе мобильного приложения BeHappy ПОДДЕРЖКА.\n\n'
|
||||||
|
'13.6. В случае расторжения Договора по инициативе Арендатора, при отсутствии задолженности, Договор считается расторгнутым по истечении 10 календарных дней с даты направления Арендатором запроса на удаление аккаунта. При наличии у Арендатора задолженности, Договор считается расторгнутым по истечении 10 календарных дней с даты полного погашения Арендатором задолженности по арендным платежам, штрафам и иным платежам, предусмотренным настоящим Договором. В день расторжения Договора Арендодатель производит удаление аккаунта Арендатора;\n\n'
|
||||||
|
'13.7. По всем другим основаниям, которые не перечислены в п.13.2 настоящего Договора, при отсутствии согласия Сторон, Договор расторгается в судебном порядке;\n\n'
|
||||||
|
'13.8. Окончание срока действия Договора не освобождает Арендатора от обязанности оплатить арендные платежи, штрафы и иные платежи по Договору, а также не освобождает от ответственности за нарушение условий Договора.',
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
_buildSectionHeader('14. Заключительные положения'),
|
||||||
|
_buildParagraph(
|
||||||
|
'14.1. Любая переписка, телефонные и иные переговоры, имевшие место до заключения Договора, теряют юридическую силу с момента заключения Договора;\n\n'
|
||||||
|
'14.2. При нахождении Арендатора за пределами территории Республики Беларусь или при указании Арендатора номера мобильного телефона зарубежного оператора мобильной связи при Регистрации, а также для целей получения сообщений/уведомлений, предусмотренных настоящим Договором, Арендатор принимает на себя все риски несвоевременного получения или неполучения Арендатор сообщений от Арендодателя;\n\n'
|
||||||
|
'14.3. Сообщения, направленные на электронную почту, считаются полученными адресатом в момент их отправки;\n\n'
|
||||||
|
'14.4. Арендатор обязуется уведомлять Арендодателя об изменении места жительства (регистрации), места пребывания, номера мобильного телефона, адреса электронной почты, иных данных, предоставленных Пользователем Арендодателю, о перемене имени, фамилии, изменении реквизитов документа, удостоверяющего личность, изменении реквизитов или лишении водительского удостоверения, изменении данных документа, подтверждающего право иностранного гражданина/лица без гражданства на пребывание (проживание) на территории Республики Беларусь, изменении гражданства, иных персональных данных, а также об утрате вышеуказанных данных, обратившись в Службу поддержки в раздел поддержка мобильного приложения BeHappy ПОДДЕРЖКА. Арендодатель не несет ответственность за какие-либо последствия, связанные с изменением указанных в настоящем пункте Договора данных Арендатора, если Арендатор не оповестил об указанных в настоящем пункте Договора обстоятельствах Арендодателя, и/или предоставил Арендодателю неверные данные;\n\n'
|
||||||
|
'14.5. Настоящий Договор составлен на русском языке.',
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
_buildSectionHeader('15. Разрешение споров и подсудность'),
|
||||||
|
_buildParagraph(
|
||||||
|
'15.1. Все споры и разногласия по настоящему Договору разрешаются путем переговоров;\n\n'
|
||||||
|
'15.2. В случае, если Арендодатель и Арендатор не пришли к решению посредством переговоров в течение 10 (десяти) рабочих дней, споры и разногласия передаются на разрешение суда по месту нахождения Арендодателя.',
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
_buildSectionHeader('16. Адрес и иные реквизиты Арендодателя:'),
|
||||||
|
_buildParagraph(
|
||||||
|
'ООО “БИХЕППИБЕЛ” (Оказывает услуги в городах Молодечно, Витебск, Полоцк, Новополоцк, Могилев, Борисов является организатором эквайринговой площадки)\n'
|
||||||
|
'УНП 392026683\n'
|
||||||
|
'Свидетельство о государственной регистрации № 0239895 от 22.01.2026 выдано администрацией Октябрьского района г.Витебска\n'
|
||||||
|
'ОКПО 511318892000\n'
|
||||||
|
'Юридический адрес: Республика Беларусь, 210017, г. Витебск, ул. Гагарина 105 кБ, офис 11А.\n'
|
||||||
|
'Банковские реквизиты в белорусских рублях:\n'
|
||||||
|
'Расчетный счет: BY86ALFA30122G27690010270000\n'
|
||||||
|
'ЗАО «Альфа-Банк», 220013, Республика Беларусь,220013, г. Минск, ул. Сурганова, 43-47, 220013\n'
|
||||||
|
'БИК ALFABY2X\n'
|
||||||
|
'Почтовый адрес: behappybelarus@gmail.com',
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 32),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildParagraph(String text) {
|
||||||
|
return Text(
|
||||||
|
text,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Color(0xFFD1D1D6),
|
||||||
|
fontSize: 14,
|
||||||
|
height: 1.5,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSectionHeader(String text) {
|
||||||
|
return Text(
|
||||||
|
text,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
height: 1.4,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -224,6 +224,20 @@ class OrderHistoryDetailScreen extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
||||||
|
if (order.hasFine) ...[
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
GradientButton(
|
||||||
|
text: 'Штраф',
|
||||||
|
showArrows: true,
|
||||||
|
height: 56,
|
||||||
|
width: double.infinity,
|
||||||
|
fontSize: 16,
|
||||||
|
onTap: () {
|
||||||
|
context.go('/home/checkout/${order.id}');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
// 🔹 ДЕТАЛИ ПОЕЗДКИ
|
// 🔹 ДЕТАЛИ ПОЕЗДКИ
|
||||||
|
|||||||
122
lib/presentation/screens/payment_rules_screen.dart
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import '../../core/app_colors.dart';
|
||||||
|
import '../components/custom_app_bar.dart';
|
||||||
|
|
||||||
|
class PaymentRulesScreen extends StatelessWidget {
|
||||||
|
const PaymentRulesScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: Container(
|
||||||
|
width: double.infinity,
|
||||||
|
decoration: const BoxDecoration(gradient: AppColors.phoneScreenBg),
|
||||||
|
child: SafeArea(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
const Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 20),
|
||||||
|
child: CustomAppBar(title: ''),
|
||||||
|
),
|
||||||
|
|
||||||
|
Expanded(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 24),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'Правила оплаты и безопасность платежей, конфиденциальность информации',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
|
_buildParagraph(
|
||||||
|
'Оплата банковскими картами осуществляется через ЗАО «Альфа-Банк».',
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
|
||||||
|
_buildParagraph(
|
||||||
|
'К оплате принимаются карты международных платежных систем VISA, MasterCard, платежной системы БЕЛКАРТ.',
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
|
Container(
|
||||||
|
width: double.infinity,
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 20),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white.withOpacity(0.05),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
border: Border.all(
|
||||||
|
color: Colors.white.withOpacity(0.1),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Image.asset(
|
||||||
|
'assets/payment_logo.png',
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
errorBuilder: (context, error, stackTrace) {
|
||||||
|
return const Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(Icons.credit_card, color: Colors.white70, size: 28),
|
||||||
|
SizedBox(width: 10),
|
||||||
|
Text(
|
||||||
|
'VISA • MasterCard • БЕЛКАРТ',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white70,
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
letterSpacing: 0.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
|
_buildParagraph(
|
||||||
|
'Безопасность совершения платежа обеспечивается современными методами проверки, шифрования и передачи данных по закрытым каналам связи. Ввод данных карточки осуществляется на защищенной авторизационной странице банка. Для оплаты необходимо ввести реквизиты карточки: номер, имя держателя, срок действия и трехзначный код безопасности. Трёхзначный код безопасности (CVV2 для VISA, CVC2 для MasterCard) — это три цифры, находящиеся на обратной стороне карточки. Если карточка поддерживает технологию 3DSecure или Интернет-пароль для держателей карточек БЕЛКАРТ, Вы будете перенаправлены на страницу банка, выпустившего карточку, для ввода кода безопасности.',
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
|
_buildParagraph(
|
||||||
|
'Предоставляемая Вами персональная информация (например: имя, адрес, телефон, e-mail, номер банковской карты и прочее) является конфиденциальной и не подлежит разглашению. Данные карточки передаются только в зашифрованном виде и не сохраняются на данном интернет-ресурсе.',
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 32),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildParagraph(String text) {
|
||||||
|
return Text(
|
||||||
|
text,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Color(0xFFD1D1D6),
|
||||||
|
fontSize: 14,
|
||||||
|
height: 1.5,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,17 +25,15 @@ class _PhoneScreenState extends State<PhoneScreen> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
// Вызываем диалог сразу после отрисовки первого кадра экрана
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
_showTestModeDialog();
|
_showTestModeDialog();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Метод для отображения диалогового окна
|
|
||||||
void _showTestModeDialog() {
|
void _showTestModeDialog() {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
barrierDismissible: false, // Запрещает закрывать окно кликом по фону
|
barrierDismissible: false,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text(
|
title: const Text(
|
||||||
@@ -46,7 +44,7 @@ class _PhoneScreenState extends State<PhoneScreen> {
|
|||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pop(); // Закрывает диалоговое окно
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
child: const Text(
|
child: const Text(
|
||||||
'Продолжить',
|
'Продолжить',
|
||||||
@@ -67,7 +65,12 @@ class _PhoneScreenState extends State<PhoneScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocConsumer<PhoneAuthBloc, PhoneAuthState>(
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus();
|
||||||
|
},
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
child: BlocConsumer<PhoneAuthBloc, PhoneAuthState>(
|
||||||
listener: (context, state) {
|
listener: (context, state) {
|
||||||
if (state.isSuccess) {
|
if (state.isSuccess) {
|
||||||
context.go("/verify?phone=+375${state.phone}");
|
context.go("/verify?phone=+375${state.phone}");
|
||||||
@@ -252,6 +255,7 @@ class _PhoneScreenState extends State<PhoneScreen> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,18 @@
|
|||||||
import 'dart:ui' as ui;
|
import 'dart:ui' as ui;
|
||||||
import 'package:be_happy/core/result.dart';
|
import 'package:be_happy/core/result.dart';
|
||||||
import 'package:be_happy/domain/entities/scooter.dart';
|
import 'package:be_happy/domain/entities/scooter.dart';
|
||||||
|
import 'package:be_happy/domain/usecase/get_address_by_point_usecase.dart';
|
||||||
|
import 'package:be_happy/domain/usecase/get_pedestrian_routes_usecase.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:geolocator/geolocator.dart';
|
||||||
import 'package:mobile_scanner/mobile_scanner.dart';
|
import 'package:mobile_scanner/mobile_scanner.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:be_happy/di/service_locator.dart';
|
import 'package:be_happy/di/service_locator.dart';
|
||||||
import 'package:be_happy/domain/usecase/get_scooter_by_title_usecase.dart';
|
import 'package:be_happy/domain/usecase/get_scooter_by_title_usecase.dart';
|
||||||
import '../components/custom_app_bar.dart';
|
import '../components/custom_app_bar.dart';
|
||||||
import '../components/gradient_button.dart';
|
import '../components/gradient_button.dart';
|
||||||
|
import 'package:yandex_mapkit/yandex_mapkit.dart';
|
||||||
|
|
||||||
|
|
||||||
class QrScanScreen extends StatefulWidget {
|
class QrScanScreen extends StatefulWidget {
|
||||||
const QrScanScreen({super.key});
|
const QrScanScreen({super.key});
|
||||||
@@ -49,9 +54,9 @@ class _QrScanScreenState extends State<QrScanScreen> {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
final getScooterByTitleUsecase = getIt<GetScooterByTitleUsecase>();
|
final getScooterByTitleUsecase = getIt<GetScooterByTitleUsecase>();
|
||||||
print("UseCase успешно получен из DI");
|
final getAddressByPointUsecase = getIt<GetAddressByPointUsecase>();
|
||||||
|
final getPedestrianRoutesUsecase = getIt<GetPedestrianRoutesUsecase>();
|
||||||
final result = await getScooterByTitleUsecase(title);
|
final result = await getScooterByTitleUsecase(title);
|
||||||
print("UseCase успешно выполнен");
|
|
||||||
|
|
||||||
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
@@ -60,9 +65,32 @@ class _QrScanScreenState extends State<QrScanScreen> {
|
|||||||
switch (result) {
|
switch (result) {
|
||||||
case Success<Scooter?>():
|
case Success<Scooter?>():
|
||||||
final scooter = result.data;
|
final scooter = result.data;
|
||||||
|
|
||||||
|
final String currentAddress = await getAddressByPointUsecase(
|
||||||
|
scooter!.longitude,
|
||||||
|
scooter.latitude,
|
||||||
|
);
|
||||||
|
|
||||||
|
final position = await Geolocator.getCurrentPosition(
|
||||||
|
desiredAccuracy: LocationAccuracy.high,
|
||||||
|
);
|
||||||
|
|
||||||
|
final routes = await getPedestrianRoutesUsecase(
|
||||||
|
Point(latitude: position.latitude, longitude: position.longitude),
|
||||||
|
Point(latitude: scooter.longitude, longitude: scooter.latitude),
|
||||||
|
);
|
||||||
|
|
||||||
|
final distance =
|
||||||
|
routes?.firstOrNull?.metadata.weight.walkingDistance.value;
|
||||||
|
print("Distance is: $distance");
|
||||||
|
final time = routes?.firstOrNull?.metadata.weight.time.value;
|
||||||
|
|
||||||
|
scooter.timeToTravel = time;
|
||||||
|
scooter.distance = distance;
|
||||||
|
|
||||||
if (scooter != null) {
|
if (scooter != null) {
|
||||||
context.pop();
|
context.pop();
|
||||||
context.push('/home/scooter/${scooter.id}');
|
context.push('/home/scooter/${scooter.id}', extra: [currentAddress, distance]);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
_showErrorAndRestart('Самокат не найден');
|
_showErrorAndRestart('Самокат не найден');
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class _ScooterCodeInputScreenState extends State<ScooterCodeInputScreen> {
|
|||||||
return BlocListener<ScooterCodeBloc, ScooterCodeState>(
|
return BlocListener<ScooterCodeBloc, ScooterCodeState>(
|
||||||
listener: (context, state) {
|
listener: (context, state) {
|
||||||
if (state is ScooterCodeSuccess) {
|
if (state is ScooterCodeSuccess) {
|
||||||
context.go("/home/scooter/${state.scooter.id}");
|
context.go("/home/scooter/${state.scooter.id}", extra: [state.address, state.distance]);
|
||||||
}
|
}
|
||||||
if (state is ScooterCodeFailure) {
|
if (state is ScooterCodeFailure) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
|||||||
105
lib/presentation/screens/tech_works_screen.dart
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
class TechWorksScreen extends StatelessWidget {
|
||||||
|
const TechWorksScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: Container(
|
||||||
|
width: double.infinity,
|
||||||
|
height: double.infinity,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
begin: Alignment.topCenter,
|
||||||
|
end: Alignment.bottomCenter,
|
||||||
|
colors: [
|
||||||
|
Color(0xFF223252),
|
||||||
|
Color(0xFF0D1220),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: SafeArea(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20.0),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const Spacer(flex: 3),
|
||||||
|
|
||||||
|
Image.asset(
|
||||||
|
'assets/tech_works.png',
|
||||||
|
width: 260,
|
||||||
|
height: 260,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
),
|
||||||
|
|
||||||
|
const Spacer(flex: 2),
|
||||||
|
|
||||||
|
Container(
|
||||||
|
width: double.infinity,
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 24.0,
|
||||||
|
vertical: 28.0,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: const Color(0xFF101528),
|
||||||
|
borderRadius: BorderRadius.circular(28.0),
|
||||||
|
border: Border.all(
|
||||||
|
color: Colors.white.withOpacity(0.05),
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'Технические работы',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 22,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
'В приложении ведутся технические работы. Попробуйте зайти позже.',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white.withOpacity(0.75),
|
||||||
|
fontSize: 15,
|
||||||
|
height: 1.35,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
SystemNavigator.pop();
|
||||||
|
},
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
minimumSize: const Size(100, 44),
|
||||||
|
foregroundColor: const Color(0xFF20E298),
|
||||||
|
),
|
||||||
|
child: const Text(
|
||||||
|
'ОК',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Color(0xFF20E298),
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
56
lib/presentation/state/fine_payment_state.dart
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import '../../domain/entities/fine.dart';
|
||||||
|
import '../../domain/entities/payment_card.dart';
|
||||||
|
|
||||||
|
enum FinePaymentStatus { initial, loading, success, failure }
|
||||||
|
|
||||||
|
class FinePaymentState extends Equatable {
|
||||||
|
final FinePaymentStatus status;
|
||||||
|
final Fine? fine;
|
||||||
|
final PaymentCard? selectedCard;
|
||||||
|
final bool useBalance;
|
||||||
|
final double userBalance;
|
||||||
|
final String? errorMessage;
|
||||||
|
final bool paymentCompleted;
|
||||||
|
|
||||||
|
const FinePaymentState({
|
||||||
|
this.status = FinePaymentStatus.initial,
|
||||||
|
this.fine,
|
||||||
|
this.selectedCard,
|
||||||
|
this.useBalance = false,
|
||||||
|
this.userBalance = 0.0,
|
||||||
|
this.errorMessage,
|
||||||
|
this.paymentCompleted = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
FinePaymentState copyWith({
|
||||||
|
FinePaymentStatus? status,
|
||||||
|
Fine? fine,
|
||||||
|
PaymentCard? selectedCard,
|
||||||
|
bool? useBalance,
|
||||||
|
double? userBalance,
|
||||||
|
String? errorMessage,
|
||||||
|
bool? paymentCompleted,
|
||||||
|
}) {
|
||||||
|
return FinePaymentState(
|
||||||
|
status: status ?? this.status,
|
||||||
|
fine: fine ?? this.fine,
|
||||||
|
selectedCard: selectedCard ?? this.selectedCard,
|
||||||
|
useBalance: useBalance ?? this.useBalance,
|
||||||
|
userBalance: userBalance ?? this.userBalance,
|
||||||
|
errorMessage: errorMessage,
|
||||||
|
paymentCompleted: paymentCompleted ?? this.paymentCompleted,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [
|
||||||
|
status,
|
||||||
|
fine,
|
||||||
|
selectedCard,
|
||||||
|
useBalance,
|
||||||
|
userBalance,
|
||||||
|
errorMessage,
|
||||||
|
paymentCompleted,
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import '../../domain/entities/scooter.dart';
|
||||||
|
|
||||||
abstract class ScooterCodeState {
|
abstract class ScooterCodeState {
|
||||||
final String code;
|
final String code;
|
||||||
final String? error;
|
final String? error;
|
||||||
@@ -14,8 +16,10 @@ class ScooterCodeLoading extends ScooterCodeState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ScooterCodeSuccess extends ScooterCodeState {
|
class ScooterCodeSuccess extends ScooterCodeState {
|
||||||
final dynamic scooter; // Замените dynamic на вашу модель Scooter
|
final Scooter scooter;
|
||||||
const ScooterCodeSuccess(this.scooter, {super.code});
|
final String address;
|
||||||
|
final double distance;
|
||||||
|
const ScooterCodeSuccess(this.scooter, {super.code, required this.address, required this.distance});
|
||||||
}
|
}
|
||||||
|
|
||||||
class ScooterCodeFailure extends ScooterCodeState {
|
class ScooterCodeFailure extends ScooterCodeState {
|
||||||
|
|||||||
81
lib/presentation/viewmodel/fine_payment_bloc.dart
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import '../../domain/usecase/get_fine_details_usecase.dart';
|
||||||
|
import '../../domain/usecase/pay_fine_usecase.dart';
|
||||||
|
import '../event/fine_payment_event.dart';
|
||||||
|
import '../state/fine_payment_state.dart';
|
||||||
|
|
||||||
|
class FinePaymentBloc extends Bloc<FinePaymentEvent, FinePaymentState> {
|
||||||
|
final GetFineDetailsUsecase getFineDetailsUsecase;
|
||||||
|
final PayFineUsecase payFineUsecase;
|
||||||
|
|
||||||
|
FinePaymentBloc({
|
||||||
|
required this.getFineDetailsUsecase,
|
||||||
|
required this.payFineUsecase,
|
||||||
|
}) : super(const FinePaymentState()) {
|
||||||
|
on<FetchFineDetails>(_onFetchFineDetails);
|
||||||
|
on<FinePaymentCardChanged>(_onCardChanged);
|
||||||
|
on<SelectFineBalancePressed>(_onSelectBalance);
|
||||||
|
on<PayFineSubmitted>(_onPayFineSubmitted);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onFetchFineDetails(
|
||||||
|
FetchFineDetails event,
|
||||||
|
Emitter<FinePaymentState> emit,
|
||||||
|
) async {
|
||||||
|
emit(state.copyWith(status: FinePaymentStatus.loading));
|
||||||
|
try {
|
||||||
|
final fine = await getFineDetailsUsecase(event.fineId);
|
||||||
|
// emit(state.copyWith(status: FinePaymentStatus.initial, fine: fine));
|
||||||
|
} catch (e) {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
status: FinePaymentStatus.failure,
|
||||||
|
errorMessage: 'Не удалось загрузить данные о штрафе',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onCardChanged(
|
||||||
|
FinePaymentCardChanged event,
|
||||||
|
Emitter<FinePaymentState> emit,
|
||||||
|
) {
|
||||||
|
emit(state.copyWith(selectedCard: event.card, useBalance: false));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onSelectBalance(
|
||||||
|
SelectFineBalancePressed event,
|
||||||
|
Emitter<FinePaymentState> emit,
|
||||||
|
) {
|
||||||
|
emit(state.copyWith(useBalance: true, selectedCard: null));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onPayFineSubmitted(
|
||||||
|
PayFineSubmitted event,
|
||||||
|
Emitter<FinePaymentState> emit,
|
||||||
|
) async {
|
||||||
|
if (state.fine == null) return;
|
||||||
|
|
||||||
|
emit(state.copyWith(status: FinePaymentStatus.loading));
|
||||||
|
try {
|
||||||
|
await payFineUsecase(
|
||||||
|
orderId: state.fine!.id,
|
||||||
|
cardId: state.useBalance ? null : state.selectedCard?.id,
|
||||||
|
isBalance: state.useBalance,
|
||||||
|
);
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
status: FinePaymentStatus.success,
|
||||||
|
paymentCompleted: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
status: FinePaymentStatus.failure,
|
||||||
|
errorMessage: e.toString(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,25 @@
|
|||||||
import 'package:be_happy/domain/entities/scooter.dart';
|
import 'package:be_happy/domain/entities/scooter.dart';
|
||||||
import 'package:be_happy/domain/usecase/get_scooter_by_title_usecase.dart';
|
import 'package:be_happy/domain/usecase/get_scooter_by_title_usecase.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:geolocator/geolocator.dart';
|
||||||
|
import 'package:yandex_mapkit/yandex_mapkit.dart';
|
||||||
|
|
||||||
|
import '../../core/result.dart';
|
||||||
|
import '../../domain/usecase/get_address_by_point_usecase.dart';
|
||||||
|
import '../../domain/usecase/get_pedestrian_routes_usecase.dart';
|
||||||
import '../event/scooter_code_event.dart';
|
import '../event/scooter_code_event.dart';
|
||||||
import '../state/scooter_code_state.dart';
|
import '../state/scooter_code_state.dart';
|
||||||
|
|
||||||
class ScooterCodeBloc extends Bloc<ScooterCodeEvent, ScooterCodeState> {
|
class ScooterCodeBloc extends Bloc<ScooterCodeEvent, ScooterCodeState> {
|
||||||
final GetScooterByTitleUsecase getScooterByTitleUsecase;
|
final GetScooterByTitleUsecase getScooterByTitleUsecase;
|
||||||
|
final GetAddressByPointUsecase getAddressUsecase;
|
||||||
|
final GetPedestrianRoutesUsecase getPedestrianRoutesUsecase;
|
||||||
|
|
||||||
ScooterCodeBloc({required this.getScooterByTitleUsecase}) : super(const ScooterCodeInitial()) {
|
ScooterCodeBloc({
|
||||||
|
required this.getScooterByTitleUsecase,
|
||||||
|
required this.getAddressUsecase,
|
||||||
|
required this.getPedestrianRoutesUsecase,
|
||||||
|
}) : super(const ScooterCodeInitial()) {
|
||||||
on<ScooterCodeChanged>((event, emit) {
|
on<ScooterCodeChanged>((event, emit) {
|
||||||
emit(ScooterCodeInitial(code: event.code));
|
emit(ScooterCodeInitial(code: event.code));
|
||||||
});
|
});
|
||||||
@@ -18,10 +29,50 @@ class ScooterCodeBloc extends Bloc<ScooterCodeEvent, ScooterCodeState> {
|
|||||||
|
|
||||||
final result = await getScooterByTitleUsecase(event.code);
|
final result = await getScooterByTitleUsecase(event.code);
|
||||||
|
|
||||||
/*result.fold(
|
switch (result) {
|
||||||
(error) => emit(ScooterCodeFailure(error.message ?? "Ошибка", code: event.code)),
|
case Success(:final data):
|
||||||
(scooter) => emit(ScooterCodeSuccess(scooter, code: event.code)),
|
if (data != null) {
|
||||||
);*/
|
final String currentAddress = await getAddressUsecase(
|
||||||
|
data.longitude,
|
||||||
|
data.latitude,
|
||||||
|
);
|
||||||
|
|
||||||
|
final position = await Geolocator.getCurrentPosition(
|
||||||
|
desiredAccuracy: LocationAccuracy.high,
|
||||||
|
);
|
||||||
|
|
||||||
|
final routes = await getPedestrianRoutesUsecase(
|
||||||
|
Point(latitude: position.latitude, longitude: position.longitude),
|
||||||
|
Point(latitude: data.longitude, longitude: data.latitude),
|
||||||
|
);
|
||||||
|
|
||||||
|
final distance =
|
||||||
|
routes?.firstOrNull?.metadata.weight.walkingDistance.value;
|
||||||
|
print("Distance is: $distance");
|
||||||
|
final time = routes?.firstOrNull?.metadata.weight.time.value;
|
||||||
|
|
||||||
|
data.distance = distance;
|
||||||
|
data.timeToTravel = time;
|
||||||
|
|
||||||
|
emit(
|
||||||
|
ScooterCodeSuccess(
|
||||||
|
data,
|
||||||
|
code: event.code,
|
||||||
|
address: currentAddress,
|
||||||
|
distance: distance ?? 0.0,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
emit(ScooterCodeFailure("Самокат не найден", code: event.code));
|
||||||
|
}
|
||||||
|
case Failure(:final failure):
|
||||||
|
emit(
|
||||||
|
ScooterCodeFailure(
|
||||||
|
failure.message ?? "Неизвестная ошибка",
|
||||||
|
code: event.code,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
24
pubspec.lock
@@ -9,6 +9,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.1"
|
version: "0.4.1"
|
||||||
|
ansicolor:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: ansicolor
|
||||||
|
sha256: "50e982d500bc863e1d703448afdbf9e5a72eb48840a4f766fa361ffd6877055f"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.3"
|
||||||
archive:
|
archive:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -286,6 +294,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.0"
|
version: "5.0.0"
|
||||||
|
flutter_native_splash:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: flutter_native_splash
|
||||||
|
sha256: "4fb9f4113350d3a80841ce05ebf1976a36de622af7d19aca0ca9a9911c7ff002"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.7"
|
||||||
flutter_plugin_android_lifecycle:
|
flutter_plugin_android_lifecycle:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -901,6 +917,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.4.0"
|
||||||
|
universal_io:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: universal_io
|
||||||
|
sha256: f63cbc48103236abf48e345e07a03ce5757ea86285ed313a6a032596ed9301e2
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.1"
|
||||||
url_launcher:
|
url_launcher:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ name: be_happy
|
|||||||
description: ""
|
description: ""
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
|
|
||||||
version: 1.0.0+17
|
version: 1.0.0+19
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.8.1
|
sdk: ^3.8.1
|
||||||
@@ -42,11 +42,17 @@ dev_dependencies:
|
|||||||
flutter_lints: ^5.0.0
|
flutter_lints: ^5.0.0
|
||||||
flutter_launcher_icons: ^0.14.4
|
flutter_launcher_icons: ^0.14.4
|
||||||
change_app_package_name: ^1.4.0
|
change_app_package_name: ^1.4.0
|
||||||
|
flutter_native_splash: ^2.4.7
|
||||||
|
|
||||||
|
flutter_native_splash:
|
||||||
|
color: "#000000"
|
||||||
|
image: assets/main_logo.png
|
||||||
|
|
||||||
flutter_launcher_icons:
|
flutter_launcher_icons:
|
||||||
android: true
|
android: true
|
||||||
ios: true
|
ios: true
|
||||||
image_path: "assets/main-logo.png"
|
image_path: "assets/main-logo.png"
|
||||||
|
remove_alpha_channel: true
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
|
|||||||