├── stream_cart_mobile ├── lib │ ├── core │ │ ├── network │ │ │ ├── network_info.dart │ │ │ └── network_config.dart │ │ ├── config │ │ │ └── livekit_config.dart │ │ ├── utils │ │ │ └── currency_formatter.dart │ │ ├── error │ │ │ ├── exceptions.dart │ │ │ └── failures.dart │ │ └── enums │ │ │ ├── user_role.dart │ │ │ └── address_type.dart │ ├── presentation │ │ ├── pages │ │ │ ├── address │ │ │ │ └── address_detail_page.dart │ │ │ └── order │ │ │ │ └── order_detail_page.dart │ │ ├── widgets │ │ │ ├── chat │ │ │ │ └── pagination_widget.dart │ │ │ ├── common │ │ │ │ └── loading_widget.dart │ │ │ ├── review │ │ │ │ ├── review_empty.dart │ │ │ │ ├── rating_stars.dart │ │ │ │ ├── review_summary_section.dart │ │ │ │ ├── merchant_reply.dart │ │ │ │ ├── review_error.dart │ │ │ │ └── infinite_scroll_listener.dart │ │ │ └── livestream │ │ │ │ ├── status_pill.dart │ │ │ │ └── error_retry.dart │ │ ├── blocs │ │ │ ├── auth │ │ │ │ └── change_password │ │ │ │ │ ├── change_password_event.dart │ │ │ │ │ └── change_password_state.dart │ │ │ ├── payment │ │ │ │ ├── payment_event.dart │ │ │ │ └── payment_state.dart │ │ │ ├── profile │ │ │ │ ├── profile_event.dart │ │ │ │ └── profile_state.dart │ │ │ ├── category_detail │ │ │ │ ├── category_detail_event.dart │ │ │ │ └── category_detail_state.dart │ │ │ ├── cart_live │ │ │ │ ├── preview_order_live_event.dart │ │ │ │ └── preview_order_live_state.dart │ │ │ ├── home │ │ │ │ └── home_event.dart │ │ │ ├── chatbot │ │ │ │ └── chat_bot_event.dart │ │ │ ├── search │ │ │ │ └── search_event.dart │ │ │ └── product_detail │ │ │ │ └── product_detail_event.dart │ │ └── theme │ │ │ └── app_colors.dart │ ├── domain │ │ ├── repositories │ │ │ ├── payment │ │ │ │ └── payment_repository.dart │ │ │ ├── cart_live │ │ │ │ └── preview_order_live_repository.dart │ │ │ ├── chatbot │ │ │ │ └── chat_bot_repository.dart │ │ │ ├── profile_repository.dart │ │ │ ├── livestream │ │ │ │ ├── livestream_product_repository.dart │ │ │ │ ├── livestream_repository.dart │ │ │ │ └── livestream_message_repository.dart │ │ │ ├── product │ │ │ │ ├── attribute_value_repository.dart │ │ │ │ ├── product_attribute_repository.dart │ │ │ │ └── product_variants_repository.dart │ │ │ ├── deliveries │ │ │ │ └── deliveries_repository.dart │ │ │ ├── flash_sale_repository.dart │ │ │ ├── search_repository.dart │ │ │ ├── order │ │ │ │ ├── order_item_repository.dart │ │ │ │ └── order_repository.dart │ │ │ ├── notification_repository.dart │ │ │ ├── shop_repository.dart │ │ │ ├── home_repository.dart │ │ │ ├── review │ │ │ │ └── review_repository.dart │ │ │ ├── cart_repository.dart │ │ │ └── shop_voucher │ │ │ │ └── shop_voucher_repository.dart │ │ ├── entities │ │ │ ├── auth │ │ │ │ ├── change_password_request_entity.dart │ │ │ │ ├── login_request_entity.dart │ │ │ │ ├── change_password_response_entity.dart │ │ │ │ ├── login_response_entity.dart │ │ │ │ ├── register_request_entity.dart │ │ │ │ └── otp_entities.dart │ │ │ ├── products │ │ │ │ ├── product_detail_attribute_entity.dart │ │ │ │ ├── product_image_entity.dart │ │ │ │ └── product_attribute_entity.dart │ │ │ ├── deliveries │ │ │ │ ├── from_shop_entity.dart │ │ │ │ ├── preview_deliveries_response_entity.dart │ │ │ │ ├── preview_deliveries_entity.dart │ │ │ │ └── shipping_item_entity.dart │ │ │ ├── order │ │ │ │ └── add_order_item_request_entity.dart │ │ │ ├── cart_live │ │ │ │ ├── price_data_live_entity.dart │ │ │ │ └── cart_item_by_shop_live_entity.dart │ │ │ └── payment │ │ │ │ └── payment_entity.dart │ │ └── usecases │ │ │ ├── cart │ │ │ ├── clear_cart_usecase.dart │ │ │ ├── remove_cart_item_usecase.dart │ │ │ ├── get_cart_items_usecase.dart │ │ │ ├── get_cart_summary_usecase.dart │ │ │ ├── remove_multiple_cart_items_usecase.dart │ │ │ ├── get_cart_preview_usecase.dart │ │ │ ├── update_cart_item_usecase.dart │ │ │ ├── get_all_cart_items_usecase.dart │ │ │ ├── remove_from_cart_usecase.dart │ │ │ └── add_to_cart_usecase.dart │ │ │ ├── cart_live │ │ │ ├── clear_livestream_cart_usecase.dart │ │ │ ├── remove_livestream_cart_item_usecase.dart │ │ │ ├── update_livestream_cart_item_quantity_usecase.dart │ │ │ ├── add_to_livestream_cart_usecase.dart │ │ │ ├── get_livestream_cart_usecase.dart │ │ │ └── get_preview_order_live_usecase.dart │ │ │ ├── address │ │ │ ├── delete_address_usecase.dart │ │ │ ├── get_addresses_usecase.dart │ │ │ ├── get_provinces_usecase.dart │ │ │ ├── get_wards_usecase.dart │ │ │ ├── get_address_by_id_usecase.dart │ │ │ ├── get_districts_usecase.dart │ │ │ ├── get_addresses_by_shop_usecase.dart │ │ │ ├── get_default_shipping_address_usecase.dart │ │ │ ├── set_default_shipping_address_usecase.dart │ │ │ ├── assign_address_to_shop_usecase.dart │ │ │ └── get_addresses_by_type_usecase.dart │ │ │ ├── shop │ │ │ └── get_product_count_by_shop_usecase.dart │ │ │ ├── notification │ │ │ ├── get_unread_notification_count_usecase.dart │ │ │ ├── mark_notification_as_read_usecase.dart │ │ │ └── get_notifications_usecase.dart │ │ │ ├── chat │ │ │ ├── get_unread_count_usecase.dart │ │ │ ├── connect_signalr_usecase.dart │ │ │ ├── disconnect_signalr_usecase.dart │ │ │ ├── mark_chat_room_as_read_usecase.dart │ │ │ ├── load_chat_room_detail_usecase.dart │ │ │ ├── send_typing_indicator_usecase.dart │ │ │ ├── join_chat_room_usecase.dart │ │ │ ├── leave_chat_room_usecase.dart │ │ │ ├── update_message_usecase.dart │ │ │ ├── load_chat_rooms_usecase.dart │ │ │ ├── load_shop_chat_rooms_usecase.dart │ │ │ ├── load_chat_room_messages_usecase.dart │ │ │ ├── create_chat_room_usecase.dart │ │ │ ├── send_message_usecase.dart │ │ │ └── search_chat_room_messages_usecase.dart │ │ │ ├── account │ │ │ ├── get_user_profile_usecase.dart │ │ │ └── update_user_profile.dart │ │ │ ├── category │ │ │ ├── get_categories_usecase.dart │ │ │ └── get_category_detail_usecase.dart │ │ │ ├── chatbot │ │ │ ├── get_chatbot_history_usecase.dart │ │ │ └── send_chatbot_message_usecase.dart │ │ │ ├── flash-sale │ │ │ ├── get_flash_sales.dart │ │ │ └── get_flash_sale_products.dart │ │ │ ├── livestream │ │ │ ├── get_livestream_usecase.dart │ │ │ ├── join_livestream_usecase.dart │ │ │ ├── get_livestreams_by_shop_usecase.dart │ │ │ ├── get_active_livestreams_usecase.dart │ │ │ ├── join_chat_livestream_usecase.dart │ │ │ ├── get_livestream_messages_usecase.dart │ │ │ ├── get_products_by_livestream_usecase.dart │ │ │ ├── get_pinned_products_by_livestream_usecase.dart │ │ │ └── send_message_livestream_usecase.dart │ │ │ ├── product │ │ │ ├── get_product_detail_usecase.dart │ │ │ ├── get_products_by_category_usecase.dart │ │ │ ├── get_product_primary_images_usecase.dart │ │ │ ├── get_products_usecase.dart │ │ │ └── get_product_images_usecase.dart │ │ │ ├── auth │ │ │ ├── login_usecase.dart │ │ │ ├── register_usecase.dart │ │ │ ├── change_password_usecase.dart │ │ │ └── otp_usecases.dart │ │ │ ├── product_variants │ │ │ ├── get_product_variant_by_id.dart │ │ │ ├── get_product_variants_by_product_id.dart │ │ │ ├── get_available_variants.dart │ │ │ ├── check_variant_availability.dart │ │ │ └── get_cheapest_variant.dart │ │ │ ├── review │ │ │ ├── delete_review_usecase.dart │ │ │ ├── create_review_usecase.dart │ │ │ ├── get_review_by_id_usecase.dart │ │ │ ├── get_reviews_by_user_usecase.dart │ │ │ ├── get_reviews_by_order_usecase.dart │ │ │ ├── get_reviews_by_livestream_usecase.dart │ │ │ └── update_review_usecase.dart │ │ │ ├── order │ │ │ ├── delete_order_item_usecase.dart │ │ │ ├── cancel_order_usecase.dart │ │ │ ├── get_order_by_id_usecase.dart │ │ │ ├── get_order_by_code_usecase.dart │ │ │ ├── get_order_item_by_id_usecase.dart │ │ │ ├── get_preview_order_usecase.dart │ │ │ ├── get_order_items_by_order_usecase.dart │ │ │ ├── update_order_status_usecase.dart │ │ │ ├── create_multiple_orders_usecase.dart │ │ │ ├── add_order_item_usecase.dart │ │ │ └── get_orders_by_account_usecase.dart │ │ │ ├── search │ │ │ └── search_products_usecase.dart │ │ │ ├── deliveries │ │ │ └── preview_order_delivery_usecase.dart │ │ │ ├── payment │ │ │ └── generate_payment_qr_usecase.dart │ │ │ └── shop_voucher │ │ │ ├── apply_shop_voucher_usecase.dart │ │ │ ├── get_available_shop_vouchers_usecase.dart │ │ │ └── get_shop_vouchers_usecase.dart │ └── data │ │ ├── datasources │ │ └── flash-sale │ │ │ └── flash_sale_remote_data_source.dart │ │ └── models │ │ ├── chat │ │ └── unread_count_model.g.dart │ │ ├── delivery │ │ ├── from_shop_model.g.dart │ │ ├── preview_deliveries_response_model.g.dart │ │ ├── shipping_item_model.g.dart │ │ ├── preview_deliveries_model.g.dart │ │ └── service_response_model.g.dart │ │ ├── auth │ │ └── login_request_model.dart │ │ ├── cart_live │ │ ├── price_data_live_model.g.dart │ │ ├── cart_item_by_shop_live_model.g.dart │ │ └── preview_order_live_model.g.dart │ │ ├── order │ │ └── add_order_item_request_model.g.dart │ │ └── payment │ │ └── payment_model.g.dart ├── test │ ├── test_category_import.dart │ ├── test_import.dart │ └── widget_test.dart ├── linux │ ├── .gitignore │ └── runner │ │ ├── main.cc │ │ ├── my_application.h │ │ └── CMakeLists.txt ├── ios │ ├── Flutter │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── AppFrameworkInfo.plist │ ├── Runner │ │ ├── Runner-Bridging-Header.h │ │ ├── Assets.xcassets │ │ │ ├── LaunchImage.imageset │ │ │ │ ├── LaunchImage.png │ │ │ │ ├── LaunchImage@2x.png │ │ │ │ ├── LaunchImage@3x.png │ │ │ │ ├── README.md │ │ │ │ └── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ ├── Icon-App-20x20@1x.png │ │ │ │ ├── Icon-App-20x20@2x.png │ │ │ │ ├── Icon-App-20x20@3x.png │ │ │ │ ├── Icon-App-29x29@1x.png │ │ │ │ ├── Icon-App-29x29@2x.png │ │ │ │ ├── Icon-App-29x29@3x.png │ │ │ │ ├── Icon-App-40x40@1x.png │ │ │ │ ├── Icon-App-40x40@2x.png │ │ │ │ ├── Icon-App-40x40@3x.png │ │ │ │ ├── Icon-App-50x50@1x.png │ │ │ │ ├── Icon-App-50x50@2x.png │ │ │ │ ├── Icon-App-57x57@1x.png │ │ │ │ ├── Icon-App-57x57@2x.png │ │ │ │ ├── Icon-App-60x60@2x.png │ │ │ │ ├── Icon-App-60x60@3x.png │ │ │ │ ├── Icon-App-72x72@1x.png │ │ │ │ ├── Icon-App-72x72@2x.png │ │ │ │ ├── Icon-App-76x76@1x.png │ │ │ │ ├── Icon-App-76x76@2x.png │ │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── AppDelegate.swift │ ├── Runner.xcodeproj │ │ └── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ ├── RunnerTests │ │ └── RunnerTests.swift │ └── .gitignore ├── macos │ ├── Flutter │ │ ├── Flutter-Debug.xcconfig │ │ └── Flutter-Release.xcconfig │ ├── Runner │ │ ├── Configs │ │ │ ├── Debug.xcconfig │ │ │ ├── Release.xcconfig │ │ │ ├── Warnings.xcconfig │ │ │ └── AppInfo.xcconfig │ │ ├── Assets.xcassets │ │ │ └── AppIcon.appiconset │ │ │ │ ├── app_icon_16.png │ │ │ │ ├── app_icon_32.png │ │ │ │ ├── app_icon_64.png │ │ │ │ ├── app_icon_1024.png │ │ │ │ ├── app_icon_128.png │ │ │ │ ├── app_icon_256.png │ │ │ │ └── app_icon_512.png │ │ ├── Release.entitlements │ │ ├── AppDelegate.swift │ │ ├── DebugProfile.entitlements │ │ ├── MainFlutterWindow.swift │ │ └── Info.plist │ ├── .gitignore │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── Runner.xcodeproj │ │ └── project.xcworkspace │ │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── RunnerTests │ │ └── RunnerTests.swift ├── web │ ├── favicon.png │ ├── icons │ │ ├── Icon-192.png │ │ ├── Icon-512.png │ │ ├── Icon-maskable-192.png │ │ └── Icon-maskable-512.png │ ├── manifest.json │ └── index.html ├── assets │ ├── icons │ │ ├── app_icon.png │ │ └── google_icon.png │ └── images │ │ ├── banner1.jpg │ │ ├── banner2.jpg │ │ └── banner3.jpg ├── windows │ ├── runner │ │ ├── resources │ │ │ └── app_icon.ico │ │ ├── resource.h │ │ ├── runner.exe.manifest │ │ ├── utils.h │ │ └── flutter_window.h │ └── .gitignore ├── .vscode │ └── settings.json ├── android │ ├── app │ │ ├── src │ │ │ ├── main │ │ │ │ ├── res │ │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ │ └── launcher_icon.png │ │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ │ └── launcher_icon.png │ │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ │ └── launcher_icon.png │ │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ │ └── launcher_icon.png │ │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ │ └── launcher_icon.png │ │ │ │ │ ├── drawable │ │ │ │ │ │ └── launch_background.xml │ │ │ │ │ ├── drawable-v21 │ │ │ │ │ │ └── launch_background.xml │ │ │ │ │ ├── values │ │ │ │ │ │ └── styles.xml │ │ │ │ │ └── values-night │ │ │ │ │ │ └── styles.xml │ │ │ │ └── kotlin │ │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── stream_cart_mobile │ │ │ │ │ └── MainActivity.kt │ │ │ ├── debug │ │ │ │ └── AndroidManifest.xml │ │ │ └── profile │ │ │ │ └── AndroidManifest.xml │ │ └── google-services.json │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ ├── .gitignore │ ├── build.gradle.kts │ └── settings.gradle.kts ├── README.md └── .gitignore └── LICENSE /stream_cart_mobile/lib/core/network/network_info.dart: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /stream_cart_mobile/test/test_category_import.dart: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /stream_cart_mobile/linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/presentation/pages/address/address_detail_page.dart: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /stream_cart_mobile/macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "ephemeral/Flutter-Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /stream_cart_mobile/macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "ephemeral/Flutter-Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /stream_cart_mobile/web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/web/favicon.png -------------------------------------------------------------------------------- /stream_cart_mobile/lib/presentation/widgets/chat/pagination_widget.dart: -------------------------------------------------------------------------------- 1 | // Hỗ trợ phân trang trong danh sách tin nhắn chat. (hasReachedEnd) -------------------------------------------------------------------------------- /stream_cart_mobile/macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /stream_cart_mobile/macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /stream_cart_mobile/web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/web/icons/Icon-192.png -------------------------------------------------------------------------------- /stream_cart_mobile/web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/web/icons/Icon-512.png -------------------------------------------------------------------------------- /stream_cart_mobile/assets/icons/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/assets/icons/app_icon.png -------------------------------------------------------------------------------- /stream_cart_mobile/assets/images/banner1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/assets/images/banner1.jpg -------------------------------------------------------------------------------- /stream_cart_mobile/assets/images/banner2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/assets/images/banner2.jpg -------------------------------------------------------------------------------- /stream_cart_mobile/assets/images/banner3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/assets/images/banner3.jpg -------------------------------------------------------------------------------- /stream_cart_mobile/macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /stream_cart_mobile/assets/icons/google_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/assets/icons/google_icon.png -------------------------------------------------------------------------------- /stream_cart_mobile/web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /stream_cart_mobile/web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /stream_cart_mobile/windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /stream_cart_mobile/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "java.configuration.updateBuildConfiguration": "interactive", 3 | "cmake.sourceDirectory": "D:/My Project/stream-cart-mobile/stream_cart_mobile/linux" 4 | } -------------------------------------------------------------------------------- /stream_cart_mobile/android/app/src/main/res/mipmap-hdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/android/app/src/main/res/mipmap-hdpi/launcher_icon.png -------------------------------------------------------------------------------- /stream_cart_mobile/android/app/src/main/res/mipmap-mdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/android/app/src/main/res/mipmap-mdpi/launcher_icon.png -------------------------------------------------------------------------------- /stream_cart_mobile/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png -------------------------------------------------------------------------------- /stream_cart_mobile/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png -------------------------------------------------------------------------------- /stream_cart_mobile/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png -------------------------------------------------------------------------------- /stream_cart_mobile/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /stream_cart_mobile/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /stream_cart_mobile/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /stream_cart_mobile/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /stream_cart_mobile/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /stream_cart_mobile/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /stream_cart_mobile/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /stream_cart_mobile/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dacoband/stream-cart-mobile/HEAD/stream_cart_mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /stream_cart_mobile/linux/runner/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /stream_cart_mobile/android/app/src/main/kotlin/com/example/stream_cart_mobile/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.stream_cart_mobile 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity : FlutterActivity() 6 | -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /stream_cart_mobile/macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /stream_cart_mobile/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip 6 | -------------------------------------------------------------------------------- /stream_cart_mobile/macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /stream_cart_mobile/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /stream_cart_mobile/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | .cxx/ 9 | 10 | # Remember to never publicly share your keystore. 11 | # See https://flutter.dev/to/reference-keystore 12 | key.properties 13 | **/*.keystore 14 | **/*.jks 15 | -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /stream_cart_mobile/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/repositories/payment/payment_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/payment/payment_entity.dart'; 4 | 5 | abstract class PaymentRepository { 6 | Future> generatePaymentQr( 7 | GeneratePaymentQrRequestEntity request, 8 | ); 9 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/presentation/blocs/auth/change_password/change_password_event.dart: -------------------------------------------------------------------------------- 1 | import '../../../../domain/entities/auth/change_password_request_entity.dart'; 2 | 3 | abstract class ChangePasswordEvent {} 4 | 5 | class SubmitChangePasswordEvent extends ChangePasswordEvent { 6 | final ChangePasswordRequestEntity request; 7 | SubmitChangePasswordEvent(this.request); 8 | } 9 | -------------------------------------------------------------------------------- /stream_cart_mobile/ios/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/presentation/theme/app_colors.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class AppColors { 4 | static const Color brandDark = Color(0xFF202328); 5 | static const Color brandAccent = Color(0xFFB0F847); 6 | static const Color brandPrimary = Color(0xFF4CAF50); 7 | 8 | // Neutrals 9 | static const Color bubbleNeutral = Color(0xFFEFF1F3); 10 | } 11 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/repositories/cart_live/preview_order_live_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | 3 | import '../../../core/error/failures.dart'; 4 | import '../../entities/cart_live/preview_order_live_entity.dart'; 5 | 6 | abstract class PreviewOrderLiveRepository { 7 | Future> getPreviewOrderLive(List cartItemIds); 8 | } 9 | -------------------------------------------------------------------------------- /stream_cart_mobile/macos/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/entities/auth/change_password_request_entity.dart: -------------------------------------------------------------------------------- 1 | class ChangePasswordRequestEntity { 2 | final String currentPassword; 3 | final String newPassword; 4 | final String confirmNewPassword; 5 | 6 | ChangePasswordRequestEntity({ 7 | required this.currentPassword, 8 | required this.newPassword, 9 | required this.confirmNewPassword, 10 | }); 11 | } 12 | -------------------------------------------------------------------------------- /stream_cart_mobile/windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /stream_cart_mobile/macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @main 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | 10 | override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { 11 | return true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/entities/auth/login_request_entity.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | class LoginRequestEntity extends Equatable { 4 | final String username; 5 | final String password; 6 | 7 | const LoginRequestEntity({ 8 | required this.username, 9 | required this.password, 10 | }); 11 | 12 | @override 13 | List get props => [username, password]; 14 | } 15 | -------------------------------------------------------------------------------- /stream_cart_mobile/test/test_import.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:stream_cart_mobile/presentation/widgets/home/category_section.dart'; 3 | 4 | void main() { 5 | // Test basic import 6 | print('Testing CategorySection...'); 7 | 8 | // Test constructor 9 | const widget = CategorySection(categories: null); 10 | print('CategorySection created: ${widget.runtimeType}'); 11 | } 12 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/cart/clear_cart_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../repositories/cart_repository.dart'; 3 | import '../../../core/error/failures.dart'; 4 | 5 | class ClearCartUseCase { 6 | final CartRepository repository; 7 | 8 | ClearCartUseCase(this.repository); 9 | 10 | Future> call() async { 11 | return await repository.clearCart(); 12 | } 13 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/data/datasources/flash-sale/flash_sale_remote_data_source.dart: -------------------------------------------------------------------------------- 1 | import '../../models/flash-sale/flash_sale_model.dart'; 2 | import '../../models/products/product_model.dart'; 3 | 4 | abstract class FlashSaleRemoteDataSource { 5 | Future> getFlashSales(); 6 | Future getFlashSaleProduct(String productId); 7 | Future> getFlashSaleProducts(List productIds); 8 | } 9 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/repositories/chatbot/chat_bot_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | 3 | import '../../../core/error/failures.dart'; 4 | import '../../entities/chatbot/chat_bot_entity.dart'; 5 | 6 | abstract class ChatBotRepository { 7 | Future> getHistory(); 8 | Future> sendMessage({required String message}); 9 | } 10 | 11 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/cart_live/clear_livestream_cart_usecase.dart: -------------------------------------------------------------------------------- 1 | import '../../../core/services/livestream_cart_service.dart'; 2 | 3 | class ClearLivestreamCartUsecase { 4 | final LivestreamCartService cartService; 5 | ClearLivestreamCartUsecase(this.cartService); 6 | 7 | Future call({ 8 | required String livestreamId, 9 | }) async { 10 | await cartService.clearCart(livestreamId); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /stream_cart_mobile/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /stream_cart_mobile/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/cart_live/remove_livestream_cart_item_usecase.dart: -------------------------------------------------------------------------------- 1 | import '../../../core/services/livestream_cart_service.dart'; 2 | 3 | class RemoveLivestreamCartItemUsecase { 4 | final LivestreamCartService cartService; 5 | RemoveLivestreamCartItemUsecase(this.cartService); 6 | 7 | Future call({ 8 | required String cartItemId, 9 | }) async { 10 | await cartService.removeItem(cartItemId); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /stream_cart_mobile/macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.server 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/entities/auth/change_password_response_entity.dart: -------------------------------------------------------------------------------- 1 | class ChangePasswordResponseEntity { 2 | final bool success; 3 | final String message; 4 | final bool? data; // API returns data: true on success (per swagger screenshot) 5 | final List? errors; 6 | 7 | ChangePasswordResponseEntity({ 8 | required this.success, 9 | required this.message, 10 | this.data, 11 | this.errors, 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/address/delete_address_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../repositories/address_repository.dart'; 3 | import '../../../core/error/failures.dart'; 4 | 5 | class DeleteAddressUseCase { 6 | final AddressRepository repository; 7 | 8 | DeleteAddressUseCase(this.repository); 9 | 10 | Future> call(String id) async { 11 | return await repository.deleteAddress(id); 12 | } 13 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/presentation/widgets/common/loading_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class CustomLoadingWidget extends StatelessWidget { 4 | const CustomLoadingWidget({super.key}); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | return const Center( 9 | child: CircularProgressIndicator( 10 | valueColor: AlwaysStoppedAnimation(Color(0xFF4CAF50)), 11 | ), 12 | ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/cart/remove_cart_item_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../repositories/cart_repository.dart'; 3 | import '../../../core/error/failures.dart'; 4 | 5 | class RemoveCartItemUseCase { 6 | final CartRepository repository; 7 | 8 | RemoveCartItemUseCase(this.repository); 9 | 10 | Future> call(String cartItemId) async { 11 | return await repository.removeCartItem(cartItemId); 12 | } 13 | } -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | 4 | @main 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/repositories/profile_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../core/error/failures.dart'; 3 | import '../entities/account/user_profile_entity.dart'; 4 | import '../../data/models/account/update_profile_model.dart'; 5 | 6 | abstract class ProfileRepository { 7 | Future> getUserProfile(); 8 | Future> updateUserProfile(String userId, UpdateProfileRequestModel request); 9 | } 10 | -------------------------------------------------------------------------------- /stream_cart_mobile/macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/cart/get_cart_items_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../entities/cart/cart_entity.dart'; 3 | import '../../repositories/cart_repository.dart'; 4 | import '../../../core/error/failures.dart'; 5 | 6 | class GetCartItemsUseCase { 7 | final CartRepository repository; 8 | 9 | GetCartItemsUseCase(this.repository); 10 | 11 | Future> call() async { 12 | return await repository.getCartItems(); 13 | } 14 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/shop/get_product_count_by_shop_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../repositories/shop_repository.dart'; 4 | 5 | class GetProductCountByShopUseCase { 6 | final ShopRepository repository; 7 | 8 | GetProductCountByShopUseCase(this.repository); 9 | 10 | Future> call(String shopId) async { 11 | return await repository.getProductCountByShop(shopId); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /stream_cart_mobile/linux/runner/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/notification/get_unread_notification_count_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../repositories/notification_repository.dart'; 4 | 5 | class GetUnreadNotificationCountUseCase { 6 | final NotificationRepository repository; 7 | 8 | GetUnreadNotificationCountUseCase(this.repository); 9 | 10 | Future> call() async { 11 | return await repository.getUnreadCount(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/address/get_addresses_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../entities/address/address_entity.dart'; 3 | import '../../repositories/address_repository.dart'; 4 | import '../../../core/error/failures.dart'; 5 | 6 | class GetAddressesUseCase { 7 | final AddressRepository repository; 8 | 9 | GetAddressesUseCase(this.repository); 10 | 11 | Future>> call() async { 12 | return await repository.getAddresses(); 13 | } 14 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/address/get_provinces_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../entities/address/address_entity.dart'; 3 | import '../../repositories/address_repository.dart'; 4 | import '../../../core/error/failures.dart'; 5 | 6 | class GetProvincesUseCase { 7 | final AddressRepository repository; 8 | 9 | GetProvincesUseCase(this.repository); 10 | 11 | Future>> call() async { 12 | return await repository.getProvinces(); 13 | } 14 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/chat/get_unread_count_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../entities/chat/unread_count_entity.dart'; 3 | import '../../repositories/chat_repository.dart'; 4 | import '../../../core/error/failures.dart'; 5 | 6 | class GetUnreadCountUseCase { 7 | final ChatRepository repository; 8 | 9 | GetUnreadCountUseCase(this.repository); 10 | 11 | Future> call() async { 12 | return await repository.getUnreadCount(); 13 | } 14 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/account/get_user_profile_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../../core/error/failures.dart'; 3 | import '../../entities/account/user_profile_entity.dart'; 4 | import '../../repositories/profile_repository.dart'; 5 | 6 | class GetUserProfileUseCase { 7 | final ProfileRepository repository; 8 | 9 | GetUserProfileUseCase(this.repository); 10 | 11 | Future> call() { 12 | return repository.getUserProfile(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/address/get_wards_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../entities/address/address_entity.dart'; 3 | import '../../repositories/address_repository.dart'; 4 | import '../../../core/error/failures.dart'; 5 | 6 | class GetWardsUseCase { 7 | final AddressRepository repository; 8 | 9 | GetWardsUseCase(this.repository); 10 | 11 | Future>> call(String districtId) async { 12 | return await repository.getWards(districtId); 13 | } 14 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/cart_live/update_livestream_cart_item_quantity_usecase.dart: -------------------------------------------------------------------------------- 1 | import '../../../core/services/livestream_cart_service.dart'; 2 | 3 | class UpdateLivestreamCartItemQuantityUsecase { 4 | final LivestreamCartService cartService; 5 | UpdateLivestreamCartItemQuantityUsecase(this.cartService); 6 | 7 | Future call({ 8 | required String cartItemId, 9 | required int newQuantity, 10 | }) async { 11 | await cartService.updateItemQuantity(cartItemId, newQuantity); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/category/get_categories_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../entities/category/category_entity.dart'; 3 | import '../../repositories/home_repository.dart'; 4 | import '../../../core/error/failures.dart'; 5 | 6 | class GetCategoriesUseCase { 7 | final HomeRepository repository; 8 | 9 | GetCategoriesUseCase(this.repository); 10 | 11 | Future>> call() async { 12 | return await repository.getCategories(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /stream_cart_mobile/windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/core/config/livekit_config.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_dotenv/flutter_dotenv.dart'; 2 | 3 | class LiveKitConfig { 4 | static String get serverUrl { 5 | final envVal = dotenv.maybeGet('LIVEKIT_URL'); 6 | if (envVal != null && envVal.trim().isNotEmpty) return envVal.trim(); 7 | const defineVal = String.fromEnvironment('LIVEKIT_URL', defaultValue: ''); 8 | if (defineVal.isNotEmpty) return defineVal; 9 | throw StateError('LIVEKIT_URL not configured. Set in .env or via --dart-define.'); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/address/get_address_by_id_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../entities/address/address_entity.dart'; 3 | import '../../repositories/address_repository.dart'; 4 | import '../../../core/error/failures.dart'; 5 | 6 | class GetAddressByIdUseCase { 7 | final AddressRepository repository; 8 | 9 | GetAddressByIdUseCase(this.repository); 10 | 11 | Future> call(String id) async { 12 | return await repository.getAddressById(id); 13 | } 14 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/repositories/livestream/livestream_product_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import 'package:stream_cart_mobile/domain/entities/livestream/livestream_product_entity.dart'; 3 | 4 | import '../../../core/error/failures.dart'; 5 | 6 | abstract class LiveStreamProductRepository { 7 | Future>> getProductsByLiveStream(String liveStreamId); 8 | Future>> getPinnedProductsByLiveStream(String liveStreamId, {int? limit}); 9 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/repositories/product/attribute_value_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/products/attribute_value_entity.dart'; 4 | 5 | abstract class AttributeValueRepository { 6 | Future>> getAllAttributeValues(); 7 | Future> getAttributeValueById(String id); 8 | Future>> getAttributeValuesByAttribute(String attributeId); 9 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/cart_live/add_to_livestream_cart_usecase.dart: -------------------------------------------------------------------------------- 1 | import '../../../core/services/livestream_cart_service.dart'; 2 | 3 | class AddToLivestreamCartUsecase { 4 | final LivestreamCartService cartService; 5 | AddToLivestreamCartUsecase(this.cartService); 6 | 7 | Future call({ 8 | required String livestreamId, 9 | required String livestreamProductId, 10 | required int quantity, 11 | }) async { 12 | await cartService.addToCart(livestreamId, livestreamProductId, quantity); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /stream_cart_mobile/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/address/get_districts_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../entities/address/address_entity.dart'; 3 | import '../../repositories/address_repository.dart'; 4 | import '../../../core/error/failures.dart'; 5 | 6 | class GetDistrictsUseCase { 7 | final AddressRepository repository; 8 | 9 | GetDistrictsUseCase(this.repository); 10 | 11 | Future>> call(String provinceId) async { 12 | return await repository.getDistricts(provinceId); 13 | } 14 | } -------------------------------------------------------------------------------- /stream_cart_mobile/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/core/utils/currency_formatter.dart: -------------------------------------------------------------------------------- 1 | import 'package:intl/intl.dart'; 2 | 3 | class CurrencyFormatter { 4 | CurrencyFormatter._(); 5 | 6 | static final NumberFormat _vndFormatter = NumberFormat.currency( 7 | locale: 'vi_VN', 8 | symbol: '', 9 | decimalDigits: 0, 10 | ); 11 | 12 | static String formatVND(num value, {bool withSuffix = true}) { 13 | final formatted = _vndFormatter.format(value).trim(); 14 | if (!withSuffix) { 15 | return formatted; 16 | } 17 | return '$formatted đ'; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/repositories/deliveries/deliveries_repository.dart: -------------------------------------------------------------------------------- 1 | // lib/domain/repositories/deliveries/deliveries_repository.dart 2 | import 'package:dartz/dartz.dart'; 3 | 4 | import '../../../core/error/failures.dart'; 5 | import '../../entities/deliveries/preview_deliveries_entity.dart'; 6 | import '../../entities/deliveries/preview_deliveries_response_entity.dart'; 7 | 8 | abstract class DeliveriesRepository { 9 | Future> previewOrder( 10 | PreviewDeliveriesEntity request, 11 | ); 12 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/repositories/flash_sale_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../core/error/failures.dart'; 3 | import '../entities/flash-sale/flash_sale_entity.dart'; 4 | import '../entities/products/product_entity.dart'; 5 | 6 | abstract class FlashSaleRepository { 7 | Future>> getFlashSales(); 8 | Future> getFlashSaleProduct(String productId); 9 | Future>> getFlashSaleProducts(List productIds); 10 | } 11 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/chatbot/get_chatbot_history_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | 3 | import '../../../core/error/failures.dart'; 4 | import '../../entities/chatbot/chat_bot_entity.dart'; 5 | import '../../repositories/chatbot/chat_bot_repository.dart'; 6 | 7 | class GetChatBotHistoryUseCase { 8 | final ChatBotRepository repository; 9 | 10 | GetChatBotHistoryUseCase(this.repository); 11 | 12 | Future> call() async { 13 | return await repository.getHistory(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/flash-sale/get_flash_sales.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | 3 | import '../../../core/error/failures.dart'; 4 | import '../../entities/flash-sale/flash_sale_entity.dart'; 5 | import '../../repositories/flash_sale_repository.dart'; 6 | 7 | class GetFlashSalesUseCase { 8 | final FlashSaleRepository repository; 9 | 10 | GetFlashSalesUseCase({required this.repository}); 11 | 12 | Future>> call() async { 13 | return await repository.getFlashSales(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/cart/get_cart_summary_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../entities/cart/cart_entity.dart'; 3 | import '../../repositories/cart_repository.dart'; 4 | import '../../../core/error/failures.dart'; 5 | 6 | @Deprecated('Use GetCartItemsUseCase instead') 7 | class GetCartSummaryUseCase { 8 | final CartRepository repository; 9 | 10 | GetCartSummaryUseCase(this.repository); 11 | 12 | Future> call() async { 13 | return await repository.getCartSummary(); 14 | } 15 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/livestream/get_livestream_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | 3 | import '../../../core/error/failures.dart'; 4 | import '../../entities/livestream/livestream_entity.dart'; 5 | import '../../repositories/livestream/livestream_repository.dart'; 6 | 7 | class GetLiveStreamUseCase { 8 | final LiveStreamRepository repository; 9 | GetLiveStreamUseCase(this.repository); 10 | 11 | Future> call(String id) async { 12 | return await repository.getLiveStream(id); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/address/get_addresses_by_shop_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../entities/address/address_entity.dart'; 3 | import '../../repositories/address_repository.dart'; 4 | import '../../../core/error/failures.dart'; 5 | 6 | class GetAddressesByShopUseCase { 7 | final AddressRepository repository; 8 | 9 | GetAddressesByShopUseCase(this.repository); 10 | 11 | Future>> call(String shopId) async { 12 | return await repository.getAddressesByShop(shopId); 13 | } 14 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/address/get_default_shipping_address_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../entities/address/address_entity.dart'; 3 | import '../../repositories/address_repository.dart'; 4 | import '../../../core/error/failures.dart'; 5 | 6 | class GetDefaultShippingAddressUseCase { 7 | final AddressRepository repository; 8 | 9 | GetDefaultShippingAddressUseCase(this.repository); 10 | 11 | Future> call() async { 12 | return await repository.getDefaultShippingAddress(); 13 | } 14 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/category/get_category_detail_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../entities/category/category_entity.dart'; 3 | import '../../repositories/home_repository.dart'; 4 | import '../../../core/error/failures.dart'; 5 | 6 | class GetCategoryDetailUseCase { 7 | final HomeRepository repository; 8 | 9 | GetCategoryDetailUseCase(this.repository); 10 | 11 | Future> call(String categoryId) async { 12 | return await repository.getCategoryDetail(categoryId); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/livestream/join_livestream_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | 3 | import '../../../core/error/failures.dart'; 4 | import '../../entities/livestream/livestream_entity.dart'; 5 | import '../../repositories/livestream/livestream_repository.dart'; 6 | 7 | class JoinLiveStreamUseCase { 8 | final LiveStreamRepository repository; 9 | JoinLiveStreamUseCase(this.repository); 10 | 11 | Future> call(String id) async { 12 | return await repository.joinLiveStream(id); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/product/get_product_detail_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../entities/products/product_detail_entity.dart'; 3 | import '../../repositories/home_repository.dart'; 4 | import '../../../core/error/failures.dart'; 5 | 6 | class GetProductDetailUseCase { 7 | final HomeRepository repository; 8 | 9 | GetProductDetailUseCase(this.repository); 10 | 11 | Future> call(String productId) async { 12 | return await repository.getProductDetail(productId); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/address/set_default_shipping_address_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../entities/address/address_entity.dart'; 3 | import '../../repositories/address_repository.dart'; 4 | import '../../../core/error/failures.dart'; 5 | 6 | class SetDefaultShippingAddressUseCase { 7 | final AddressRepository repository; 8 | 9 | SetDefaultShippingAddressUseCase(this.repository); 10 | 11 | Future> call(String id) async { 12 | return await repository.setDefaultShippingAddress(id); 13 | } 14 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/auth/login_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../entities/auth/login_request_entity.dart'; 3 | import '../../entities/auth/login_response_entity.dart'; 4 | import '../../repositories/auth_repository.dart'; 5 | import '../../../core/error/failures.dart'; 6 | 7 | class LoginUseCase { 8 | final AuthRepository repository; 9 | 10 | LoginUseCase(this.repository); 11 | 12 | Future> call(LoginRequestEntity params) async { 13 | return await repository.login(params); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/product/get_products_by_category_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../entities/products/product_entity.dart'; 3 | import '../../repositories/home_repository.dart'; 4 | import '../../../core/error/failures.dart'; 5 | 6 | class GetProductsByCategoryUseCase { 7 | final HomeRepository repository; 8 | 9 | GetProductsByCategoryUseCase(this.repository); 10 | 11 | Future>> call(String categoryId) async { 12 | return await repository.getProductsByCategory(categoryId); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/address/assign_address_to_shop_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../entities/address/address_entity.dart'; 3 | import '../../repositories/address_repository.dart'; 4 | import '../../../core/error/failures.dart'; 5 | 6 | class AssignAddressToShopUseCase { 7 | final AddressRepository repository; 8 | 9 | AssignAddressToShopUseCase(this.repository); 10 | 11 | Future> call(String addressId, String shopId) async { 12 | return await repository.assignAddressToShop(addressId, shopId); 13 | } 14 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/repositories/livestream/livestream_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/livestream/livestream_entity.dart'; 4 | 5 | abstract class LiveStreamRepository { 6 | Future> getLiveStream(String id); 7 | Future> joinLiveStream(String id); 8 | Future>> getLiveStreamsByShop(String shopId); 9 | Future>> getActiveLiveStreams({bool? promotedOnly}); 10 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/core/error/exceptions.dart: -------------------------------------------------------------------------------- 1 | class ServerException implements Exception { 2 | final String message; 3 | 4 | const ServerException(this.message); 5 | } 6 | 7 | class CacheException implements Exception { 8 | final String message; 9 | 10 | const CacheException(this.message); 11 | } 12 | 13 | class NetworkException implements Exception { 14 | final String message; 15 | 16 | const NetworkException(this.message); 17 | } 18 | 19 | class UnauthorizedException implements Exception { 20 | final String message; 21 | 22 | const UnauthorizedException(this.message); 23 | } 24 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/chat/connect_signalr_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../../core/services/signalr_service.dart'; 4 | 5 | class ConnectSignalRUseCase { 6 | final SignalRService signalRService; 7 | 8 | ConnectSignalRUseCase(this.signalRService); 9 | 10 | Future> call() async { 11 | try { 12 | await signalRService.connect(); 13 | return const Right(null); 14 | } catch (e) { 15 | return Left(NetworkFailure('Failed to connect SignalR: $e')); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/livestream/get_livestreams_by_shop_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | 3 | import '../../../core/error/failures.dart'; 4 | import '../../entities/livestream/livestream_entity.dart'; 5 | import '../../repositories/livestream/livestream_repository.dart'; 6 | 7 | class GetLiveStreamsByShopUseCase { 8 | final LiveStreamRepository repository; 9 | GetLiveStreamsByShopUseCase(this.repository); 10 | 11 | Future>> call(String shopId) async { 12 | return await repository.getLiveStreamsByShop(shopId); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/product_variants/get_product_variant_by_id.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/products/product_variants_entity.dart'; 4 | import '../../repositories/product/product_variants_repository.dart'; 5 | 6 | class GetProductVariantById { 7 | final ProductVariantsRepository repository; 8 | 9 | GetProductVariantById(this.repository); 10 | 11 | Future> call(String variantId) async { 12 | return await repository.getProductVariantById(variantId); 13 | } 14 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/auth/register_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../entities/auth/register_request_entity.dart'; 3 | import '../../entities/auth/register_response_entity.dart'; 4 | import '../../repositories/auth_repository.dart'; 5 | import '../../../core/error/failures.dart'; 6 | 7 | class RegisterUseCase { 8 | final AuthRepository repository; 9 | 10 | RegisterUseCase(this.repository); 11 | 12 | Future> call(RegisterRequestEntity request) async { 13 | return await repository.register(request); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/flash-sale/get_flash_sale_products.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | 3 | import '../../../core/error/failures.dart'; 4 | import '../../entities/products/product_entity.dart'; 5 | import '../../repositories/flash_sale_repository.dart'; 6 | 7 | class GetFlashSaleProductsUseCase { 8 | final FlashSaleRepository repository; 9 | 10 | GetFlashSaleProductsUseCase({required this.repository}); 11 | 12 | Future>> call(List productIds) async { 13 | return await repository.getFlashSaleProducts(productIds); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/address/get_addresses_by_type_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../entities/address/address_entity.dart'; 3 | import '../../repositories/address_repository.dart'; 4 | import '../../../core/error/failures.dart'; 5 | import '../../../core/enums/address_type.dart'; 6 | 7 | class GetAddressesByTypeUseCase { 8 | final AddressRepository repository; 9 | 10 | GetAddressesByTypeUseCase(this.repository); 11 | 12 | Future>> call(AddressType type) async { 13 | return await repository.getAddressesByType(type); 14 | } 15 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/livestream/get_active_livestreams_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/livestream/livestream_entity.dart'; 4 | import '../../repositories/livestream/livestream_repository.dart'; 5 | 6 | class GetActiveLiveStreamsUseCase { 7 | final LiveStreamRepository repository; 8 | GetActiveLiveStreamsUseCase(this.repository); 9 | 10 | Future>> call({bool? promotedOnly}) { 11 | return repository.getActiveLiveStreams(promotedOnly: promotedOnly); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/core/enums/user_role.dart: -------------------------------------------------------------------------------- 1 | enum UserRole { 2 | customer(1, 'Customer'), // Changed from 0 to 1 3 | seller(2, 'Seller'); // Changed from 1 to 2 4 | 5 | const UserRole(this.value, this.displayName); 6 | 7 | final int value; 8 | final String displayName; 9 | 10 | static UserRole fromValue(int value) { 11 | switch (value) { 12 | case 1: 13 | return UserRole.customer; 14 | case 2: 15 | return UserRole.seller; 16 | default: 17 | return UserRole.customer; 18 | } 19 | } 20 | 21 | static List get allRoles => UserRole.values; 22 | } 23 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/repositories/search_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../entities/search/search_response_entity.dart'; 3 | import '../../core/error/failures.dart'; 4 | 5 | abstract class SearchRepository { 6 | Future> searchProducts({ 7 | required String searchTerm, 8 | int? pageNumber, 9 | int? pageSize, 10 | String? categoryId, 11 | double? minPrice, 12 | double? maxPrice, 13 | String? shopId, 14 | String? sortBy, 15 | bool? inStockOnly, 16 | int? minRating, 17 | bool? onSaleOnly, 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/chat/disconnect_signalr_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../../core/services/signalr_service.dart'; 4 | 5 | class DisconnectSignalRUseCase { 6 | final SignalRService signalRService; 7 | 8 | DisconnectSignalRUseCase(this.signalRService); 9 | 10 | Future> call() async { 11 | try { 12 | await signalRService.disconnect(); 13 | return const Right(null); 14 | } catch (e) { 15 | return Left(NetworkFailure('Failed to disconnect SignalR: $e')); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/notification/mark_notification_as_read_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/notification/notification_entity.dart'; 4 | import '../../repositories/notification_repository.dart'; 5 | 6 | class MarkNotificationAsReadUseCase { 7 | final NotificationRepository repository; 8 | 9 | MarkNotificationAsReadUseCase(this.repository); 10 | 11 | Future> call(String notificationId) async { 12 | return await repository.markAsRead(notificationId); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/review/delete_review_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../repositories/review/review_repository.dart'; 4 | 5 | class DeleteReviewParams { 6 | final String reviewId; 7 | 8 | DeleteReviewParams({required this.reviewId}); 9 | } 10 | 11 | class DeleteReviewUseCase { 12 | final ReviewRepository repository; 13 | 14 | DeleteReviewUseCase(this.repository); 15 | 16 | Future> call(DeleteReviewParams params) async { 17 | return repository.deleteReview(params.reviewId); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/order/delete_order_item_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../repositories/order/order_item_repository.dart'; 4 | 5 | class DeleteOrderItemParams { 6 | final String id; 7 | 8 | DeleteOrderItemParams({required this.id}); 9 | } 10 | 11 | class DeleteOrderItemUseCase { 12 | final OrderItemRepository repository; 13 | 14 | DeleteOrderItemUseCase(this.repository); 15 | 16 | Future> call(DeleteOrderItemParams params) async { 17 | return await repository.deleteOrderItem(params.id); 18 | } 19 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/cart_live/get_livestream_cart_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import '../../../core/services/livestream_cart_service.dart'; 3 | import '../../../data/models/cart_live/preview_order_live_model.dart'; 4 | 5 | class GetLivestreamCartUsecase { 6 | final LivestreamCartService cartService; 7 | GetLivestreamCartUsecase(this.cartService); 8 | 9 | Future call(String livestreamId) async { 10 | try { 11 | await cartService.loadCartWithRetry(livestreamId, maxRetries: 3); 12 | return null; 13 | } catch (e) { 14 | rethrow; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/cart_live/get_preview_order_live_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | 3 | import '../../../core/error/failures.dart'; 4 | import '../../entities/cart_live/preview_order_live_entity.dart'; 5 | import '../../repositories/cart_live/preview_order_live_repository.dart'; 6 | 7 | class GetPreviewOrderLiveUsecase { 8 | final PreviewOrderLiveRepository repository; 9 | GetPreviewOrderLiveUsecase(this.repository); 10 | 11 | Future> call(List cartItemIds) async { 12 | return await repository.getPreviewOrderLive(cartItemIds); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/livestream/join_chat_livestream_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | 3 | import '../../../core/error/failures.dart'; 4 | import '../../entities/livestream/livestream_message_entity.dart'; 5 | import '../../repositories/livestream/livestream_message_repository.dart'; 6 | 7 | class JoinChatLiveStreamUseCase { 8 | final LiveStreamMessageRepository repository; 9 | JoinChatLiveStreamUseCase(this.repository); 10 | 11 | Future> call(String liveStreamId) async { 12 | return await repository.joinChatLiveStream(liveStreamId); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/presentation/blocs/auth/change_password/change_password_state.dart: -------------------------------------------------------------------------------- 1 | import '../../../../domain/entities/auth/change_password_response_entity.dart'; 2 | 3 | abstract class ChangePasswordState {} 4 | 5 | class ChangePasswordInitial extends ChangePasswordState {} 6 | class ChangePasswordLoading extends ChangePasswordState {} 7 | class ChangePasswordSuccess extends ChangePasswordState { 8 | final ChangePasswordResponseEntity response; 9 | ChangePasswordSuccess(this.response); 10 | } 11 | class ChangePasswordFailure extends ChangePasswordState { 12 | final String message; 13 | ChangePasswordFailure(this.message); 14 | } 15 | -------------------------------------------------------------------------------- /stream_cart_mobile/README.md: -------------------------------------------------------------------------------- 1 | # stream_cart_mobile 2 | 3 | A new Flutter project. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) 13 | 14 | For help getting started with Flutter development, view the 15 | [online documentation](https://docs.flutter.dev/), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/livestream/get_livestream_messages_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | 3 | import '../../../core/error/failures.dart'; 4 | import '../../entities/livestream/livestream_message_entity.dart'; 5 | import '../../repositories/livestream/livestream_message_repository.dart'; 6 | 7 | class GetLiveStreamMessagesUseCase { 8 | final LiveStreamMessageRepository repository; 9 | GetLiveStreamMessagesUseCase(this.repository); 10 | 11 | Future>> call(String livestreamId) { 12 | return repository.getLiveStreamMessages(livestreamId); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/auth/change_password_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/auth/change_password_request_entity.dart'; 4 | import '../../entities/auth/change_password_response_entity.dart'; 5 | import '../../repositories/auth_repository.dart'; 6 | 7 | class ChangePasswordUseCase { 8 | final AuthRepository repository; 9 | ChangePasswordUseCase(this.repository); 10 | 11 | Future> call(ChangePasswordRequestEntity request) async { 12 | return await repository.changePassword(request); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/order/cancel_order_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/order/order_entity.dart'; 4 | import '../../repositories/order/order_repository.dart'; 5 | 6 | class CancelOrderParams { 7 | final String id; 8 | 9 | CancelOrderParams({required this.id}); 10 | } 11 | 12 | class CancelOrderUseCase { 13 | final OrderRepository repository; 14 | 15 | CancelOrderUseCase(this.repository); 16 | 17 | Future> call(CancelOrderParams params) async { 18 | return await repository.cancelOrder(params.id); 19 | } 20 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/livestream/get_products_by_livestream_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | 3 | import '../../../core/error/failures.dart'; 4 | import '../../entities/livestream/livestream_product_entity.dart'; 5 | import '../../repositories/livestream/livestream_product_repository.dart'; 6 | 7 | class GetProductsByLiveStreamUseCase { 8 | final LiveStreamProductRepository repository; 9 | GetProductsByLiveStreamUseCase(this.repository); 10 | 11 | Future>> call(String liveStreamId) async { 12 | return await repository.getProductsByLiveStream(liveStreamId); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/order/get_order_by_id_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/order/order_entity.dart'; 4 | import '../../repositories/order/order_repository.dart'; 5 | 6 | class GetOrderByIdParams { 7 | final String id; 8 | 9 | GetOrderByIdParams({required this.id}); 10 | } 11 | 12 | class GetOrderByIdUseCase { 13 | final OrderRepository repository; 14 | 15 | GetOrderByIdUseCase(this.repository); 16 | 17 | Future> call(GetOrderByIdParams params) async { 18 | return await repository.getOrderById(params.id); 19 | } 20 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/presentation/blocs/payment/payment_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import '../../../domain/entities/payment/payment_entity.dart'; 3 | 4 | abstract class PaymentEvent extends Equatable { 5 | const PaymentEvent(); 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class GeneratePaymentQrEvent extends PaymentEvent { 11 | final GeneratePaymentQrRequestEntity request; 12 | const GeneratePaymentQrEvent({required this.request}); 13 | 14 | @override 15 | List get props => [request]; 16 | } 17 | 18 | class ResetPaymentStateEvent extends PaymentEvent { 19 | const ResetPaymentStateEvent(); 20 | } 21 | 22 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/repositories/order/order_item_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | 3 | import '../../../core/error/failures.dart'; 4 | import '../../entities/order/add_order_item_request_entity.dart'; 5 | import '../../entities/order/order_item_entity.dart'; 6 | 7 | abstract class OrderItemRepository { 8 | Future> getOrderItemById(String id); 9 | Future>> getOrderItemsByOrderId(String orderId); 10 | Future> addOrderItem(String orderId, AddOrderItemRequestEntity request); 11 | Future> deleteOrderItem(String id); 12 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/repositories/product/product_attribute_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/products/product_attribute_entity.dart'; 4 | 5 | abstract class ProductAttributeRepository { 6 | Future>> getAllProductAttributes(); 7 | Future> getProductAttributeById(String id); 8 | Future>> getProductAttributesByProduct(String productId); 9 | Future>> getProductAttributesWithValues(String attributeId); 10 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/product_variants/get_product_variants_by_product_id.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/products/product_variants_entity.dart'; 4 | import '../../repositories/product/product_variants_repository.dart'; 5 | 6 | class GetProductVariantsByProductId { 7 | final ProductVariantsRepository repository; 8 | 9 | GetProductVariantsByProductId(this.repository); 10 | 11 | Future>> call(String productId) async { 12 | final result = await repository.getProductVariantsByProductId(productId); 13 | return result; 14 | } 15 | } -------------------------------------------------------------------------------- /stream_cart_mobile/macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/entities/auth/login_response_entity.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import '../account/account_entity.dart'; 3 | 4 | class LoginResponseEntity extends Equatable { 5 | final bool success; 6 | final String token; 7 | final String refreshToken; 8 | final AccountEntity account; 9 | final String message; 10 | 11 | const LoginResponseEntity({ 12 | required this.success, 13 | required this.token, 14 | required this.refreshToken, 15 | required this.account, 16 | required this.message, 17 | }); 18 | 19 | @override 20 | List get props => [success, token, refreshToken, account, message]; 21 | } 22 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/order/get_order_by_code_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/order/order_entity.dart'; 4 | import '../../repositories/order/order_repository.dart'; 5 | 6 | class GetOrderByCodeParams { 7 | final String code; 8 | 9 | GetOrderByCodeParams({required this.code}); 10 | } 11 | 12 | class GetOrderByCodeUseCase { 13 | final OrderRepository repository; 14 | 15 | GetOrderByCodeUseCase(this.repository); 16 | 17 | Future> call(GetOrderByCodeParams params) async { 18 | return await repository.getOrderDetailsByCode(params.code); 19 | } 20 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/repositories/livestream/livestream_message_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | 3 | import '../../../core/error/failures.dart'; 4 | import '../../entities/livestream/livestream_message_entity.dart'; 5 | 6 | abstract class LiveStreamMessageRepository { 7 | Future> joinChatLiveStream(String livestreamId); 8 | Future> sendMessageToLiveStream({ required String livestreamId, required String message, int messageType = 0, String? replyToMessageId}); 9 | Future>> getLiveStreamMessages(String livestreamId); 10 | } 11 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/review/create_review_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/review/review_entity.dart'; 4 | import '../../repositories/review/review_repository.dart'; 5 | 6 | class CreateReviewParams { 7 | final ReviewRequestEntity request; 8 | 9 | CreateReviewParams({required this.request}); 10 | } 11 | 12 | class CreateReviewUseCase { 13 | final ReviewRepository repository; 14 | 15 | CreateReviewUseCase(this.repository); 16 | 17 | Future> call(CreateReviewParams params) async { 18 | return repository.createReview(params.request); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/review/get_review_by_id_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/review/review_entity.dart'; 4 | import '../../repositories/review/review_repository.dart'; 5 | 6 | class GetReviewByIdParams { 7 | final String reviewId; 8 | 9 | GetReviewByIdParams({required this.reviewId}); 10 | } 11 | 12 | class GetReviewByIdUseCase { 13 | final ReviewRepository repository; 14 | 15 | GetReviewByIdUseCase(this.repository); 16 | 17 | Future> call(GetReviewByIdParams params) async { 18 | return repository.getReviewById(params.reviewId); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/presentation/widgets/review/review_empty.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ReviewEmpty extends StatelessWidget { 4 | const ReviewEmpty({super.key}); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | return Center( 9 | child: Padding( 10 | padding: const EdgeInsets.all(24), 11 | child: Column( 12 | mainAxisSize: MainAxisSize.min, 13 | children: const [ 14 | Icon(Icons.reviews_outlined, size: 48, color: Colors.grey), 15 | SizedBox(height: 12), 16 | Text('Chưa có đánh giá nào'), 17 | ], 18 | ), 19 | ), 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/livestream/get_pinned_products_by_livestream_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | 3 | import '../../../core/error/failures.dart'; 4 | import '../../entities/livestream/livestream_product_entity.dart'; 5 | import '../../repositories/livestream/livestream_product_repository.dart'; 6 | 7 | class GetPinnedProductsByLiveStreamUseCase { 8 | final LiveStreamProductRepository repository; 9 | GetPinnedProductsByLiveStreamUseCase(this.repository); 10 | 11 | Future>> call(String liveStreamId, {int? limit}) { 12 | return repository.getPinnedProductsByLiveStream(liveStreamId, limit: limit); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/search/search_products_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../entities/products/product_entity.dart'; 3 | import '../../repositories/home_repository.dart'; 4 | import '../../../core/error/failures.dart'; 5 | 6 | class SearchProductsUseCase { 7 | final HomeRepository repository; 8 | 9 | SearchProductsUseCase(this.repository); 10 | 11 | Future>> call({ 12 | required String query, 13 | int page = 1, 14 | int limit = 20, 15 | }) async { 16 | return await repository.searchProducts( 17 | query: query, 18 | page: page, 19 | limit: limit, 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /stream_cart_mobile/windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/cart/remove_multiple_cart_items_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../repositories/cart_repository.dart'; 3 | import '../../../core/error/failures.dart'; 4 | 5 | class RemoveMultipleCartItemsParams { 6 | final List cartItemIds; 7 | 8 | RemoveMultipleCartItemsParams({required this.cartItemIds}); 9 | } 10 | 11 | class RemoveMultipleCartItemsUseCase { 12 | final CartRepository repository; 13 | 14 | RemoveMultipleCartItemsUseCase(this.repository); 15 | 16 | Future> call(RemoveMultipleCartItemsParams params) async { 17 | return await repository.removeMultipleCartItems(params.cartItemIds); 18 | } 19 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/cart/get_cart_preview_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../entities/cart/cart_entity.dart'; 3 | import '../../repositories/cart_repository.dart'; 4 | import '../../../core/error/failures.dart'; 5 | 6 | class GetCartPreviewParams { 7 | final List cartItemIds; 8 | 9 | GetCartPreviewParams({required this.cartItemIds}); 10 | } 11 | 12 | class GetCartPreviewUseCase { 13 | final CartRepository repository; 14 | 15 | GetCartPreviewUseCase(this.repository); 16 | 17 | Future> call(GetCartPreviewParams params) async { 18 | return await repository.getPreviewOrder(params.cartItemIds); 19 | } 20 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/order/get_order_item_by_id_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/order/order_item_entity.dart'; 4 | import '../../repositories/order/order_item_repository.dart'; 5 | 6 | class GetOrderItemByIdParams { 7 | final String id; 8 | 9 | GetOrderItemByIdParams({required this.id}); 10 | } 11 | 12 | class GetOrderItemByIdUseCase { 13 | final OrderItemRepository repository; 14 | 15 | GetOrderItemByIdUseCase(this.repository); 16 | 17 | Future> call(GetOrderItemByIdParams params) async { 18 | return await repository.getOrderItemById(params.id); 19 | } 20 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/data/models/chat/unread_count_model.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'unread_count_model.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | UnreadCountModel _$UnreadCountModelFromJson(Map json) => 10 | UnreadCountModel( 11 | unreadCounts: Map.from(json['unreadCounts'] as Map), 12 | ); 13 | 14 | Map _$UnreadCountModelToJson(UnreadCountModel instance) => 15 | { 16 | 'unreadCounts': instance.unreadCounts, 17 | }; 18 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/presentation/blocs/profile/profile_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import '../../../data/models/account/update_profile_model.dart'; 3 | 4 | abstract class ProfileEvent extends Equatable { 5 | const ProfileEvent(); 6 | 7 | @override 8 | List get props => []; 9 | } 10 | 11 | class LoadUserProfileEvent extends ProfileEvent {} 12 | 13 | class UpdateUserProfileEvent extends ProfileEvent { 14 | final String userId; 15 | final UpdateProfileRequestModel request; 16 | 17 | const UpdateUserProfileEvent({ 18 | required this.userId, 19 | required this.request, 20 | }); 21 | 22 | @override 23 | List get props => [userId, request]; 24 | } 25 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/repositories/notification_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../core/error/failures.dart'; 3 | import '../entities/notification/notification_entity.dart'; 4 | 5 | abstract class NotificationRepository { 6 | /// Get notifications with pagination 7 | Future> getNotifications({ 8 | String? type, 9 | bool? isRead, 10 | int? pageIndex, 11 | int? pageSize, 12 | }); 13 | 14 | /// Mark notification as read 15 | Future> markAsRead(String notificationId); 16 | 17 | /// Get unread notification count 18 | Future> getUnreadCount(); 19 | } 20 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/review/get_reviews_by_user_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/review/review_entity.dart'; 4 | import '../../repositories/review/review_repository.dart'; 5 | 6 | class GetReviewsByUserParams { 7 | final String userId; 8 | 9 | GetReviewsByUserParams({required this.userId}); 10 | } 11 | 12 | class GetReviewsByUserUseCase { 13 | final ReviewRepository repository; 14 | 15 | GetReviewsByUserUseCase(this.repository); 16 | 17 | Future>> call(GetReviewsByUserParams params) async { 18 | return repository.getReviewsByUserId(params.userId); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /stream_cart_mobile/macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = stream_cart_mobile 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.example.streamCartMobile 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2025 com.example. All rights reserved. 15 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/presentation/widgets/livestream/status_pill.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class StatusPill extends StatelessWidget { 4 | final String text; 5 | final Color color; 6 | const StatusPill({super.key, required this.text, required this.color}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return Container( 11 | padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), 12 | decoration: BoxDecoration( 13 | color: color, 14 | borderRadius: BorderRadius.circular(20), 15 | ), 16 | child: Text(text, style: const TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.bold)), 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/review/get_reviews_by_order_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/review/review_entity.dart'; 4 | import '../../repositories/review/review_repository.dart'; 5 | 6 | class GetReviewsByOrderParams { 7 | final String orderId; 8 | 9 | GetReviewsByOrderParams({required this.orderId}); 10 | } 11 | 12 | class GetReviewsByOrderUseCase { 13 | final ReviewRepository repository; 14 | 15 | GetReviewsByOrderUseCase(this.repository); 16 | 17 | Future>> call(GetReviewsByOrderParams params) async { 18 | return repository.getReviewsByOrderId(params.orderId); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/order/get_preview_order_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../entities/cart/cart_entity.dart'; 3 | import '../../repositories/cart_repository.dart'; 4 | import '../../../core/error/failures.dart'; 5 | 6 | class GetPreviewOrderParams { 7 | final List cartItemIds; 8 | 9 | GetPreviewOrderParams({ 10 | required this.cartItemIds, 11 | }); 12 | } 13 | 14 | class GetPreviewOrderUseCase { 15 | final CartRepository repository; 16 | 17 | GetPreviewOrderUseCase(this.repository); 18 | 19 | Future> call(GetPreviewOrderParams params) async { 20 | return await repository.getPreviewOrder(params.cartItemIds); 21 | } 22 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/presentation/widgets/livestream/error_retry.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ErrorRetry extends StatelessWidget { 4 | final String message; 5 | final VoidCallback onRetry; 6 | const ErrorRetry({super.key, required this.message, required this.onRetry}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return Center( 11 | child: Column( 12 | mainAxisSize: MainAxisSize.min, 13 | children: [ 14 | Text(message, style: const TextStyle(color: Colors.redAccent)), 15 | const SizedBox(height: 12), 16 | ElevatedButton(onPressed: onRetry, child: const Text('Thử lại')), 17 | ], 18 | ), 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /stream_cart_mobile/ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/deliveries/preview_order_delivery_usecase.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:dartz/dartz.dart'; 3 | import '../../entities/deliveries/preview_deliveries_entity.dart'; 4 | import '../../entities/deliveries/preview_deliveries_response_entity.dart'; 5 | import '../../repositories/deliveries/deliveries_repository.dart'; 6 | import '../../../core/error/failures.dart'; 7 | 8 | class PreviewOrderDeliveryUseCase { 9 | final DeliveriesRepository repository; 10 | 11 | PreviewOrderDeliveryUseCase({required this.repository}); 12 | 13 | Future> call( 14 | PreviewDeliveriesEntity request, 15 | ) async { 16 | return await repository.previewOrder(request); 17 | } 18 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/product/get_product_primary_images_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../repositories/home_repository.dart'; 3 | import '../../../core/error/failures.dart'; 4 | 5 | class GetProductPrimaryImagesUseCase { 6 | final HomeRepository repository; 7 | 8 | GetProductPrimaryImagesUseCase(this.repository); 9 | 10 | Future>> call(List productIds) async { 11 | final result = await repository.getProductPrimaryImages(productIds); 12 | return result.fold( 13 | (failure) { 14 | return Left(failure); 15 | }, 16 | (primaryImages) { 17 | return Right(primaryImages); 18 | }, 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/repositories/product/product_variants_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/products/product_variants_entity.dart'; 4 | 5 | abstract class ProductVariantsRepository { 6 | Future>> getProductVariants(); 7 | Future> getProductVariantById(String id); 8 | Future>> getProductVariantsByProductId(String productId); 9 | Future> updateProductVariantPrice(String id, double price); 10 | Future> updateProductVariantStock(String id, int stock); 11 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/order/get_order_items_by_order_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/order/order_item_entity.dart'; 4 | import '../../repositories/order/order_item_repository.dart'; 5 | 6 | class GetOrderItemsByOrderParams { 7 | final String orderId; 8 | 9 | GetOrderItemsByOrderParams({required this.orderId}); 10 | } 11 | 12 | class GetOrderItemsByOrderUseCase { 13 | final OrderItemRepository repository; 14 | 15 | GetOrderItemsByOrderUseCase(this.repository); 16 | 17 | Future>> call(GetOrderItemsByOrderParams params) async { 18 | return await repository.getOrderItemsByOrderId(params.orderId); 19 | } 20 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/payment/generate_payment_qr_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/payment/payment_entity.dart'; 4 | import '../../repositories/payment/payment_repository.dart'; 5 | 6 | class GeneratePaymentQrParams { 7 | final GeneratePaymentQrRequestEntity request; 8 | 9 | GeneratePaymentQrParams({required this.request}); 10 | } 11 | 12 | class GeneratePaymentQrUseCase { 13 | final PaymentRepository repository; 14 | 15 | GeneratePaymentQrUseCase(this.repository); 16 | 17 | Future> call(GeneratePaymentQrParams params) async { 18 | return await repository.generatePaymentQr(params.request); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/presentation/blocs/category_detail/category_detail_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | abstract class CategoryDetailEvent extends Equatable { 4 | const CategoryDetailEvent(); 5 | 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class LoadCategoryDetailEvent extends CategoryDetailEvent { 11 | final String categoryId; 12 | 13 | const LoadCategoryDetailEvent(this.categoryId); 14 | 15 | @override 16 | List get props => [categoryId]; 17 | } 18 | 19 | class LoadProductsByCategoryEvent extends CategoryDetailEvent { 20 | final String categoryId; 21 | 22 | const LoadProductsByCategoryEvent(this.categoryId); 23 | 24 | @override 25 | List get props => [categoryId]; 26 | } 27 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/presentation/blocs/cart_live/preview_order_live_event.dart: -------------------------------------------------------------------------------- 1 | part of 'preview_order_live_bloc.dart'; 2 | 3 | abstract class PreviewOrderLiveEvent extends Equatable { 4 | const PreviewOrderLiveEvent(); 5 | @override 6 | List get props => []; 7 | } 8 | 9 | class RequestPreviewOrderLiveEvent extends PreviewOrderLiveEvent { 10 | final List cartItemIds; 11 | const RequestPreviewOrderLiveEvent(this.cartItemIds); 12 | @override 13 | List get props => [cartItemIds]; 14 | } 15 | 16 | class RefreshPreviewOrderLiveEvent extends PreviewOrderLiveEvent { 17 | final List cartItemIds; 18 | const RefreshPreviewOrderLiveEvent(this.cartItemIds); 19 | @override 20 | List get props => [cartItemIds]; 21 | } 22 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/review/get_reviews_by_livestream_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/review/review_entity.dart'; 4 | import '../../repositories/review/review_repository.dart'; 5 | 6 | class GetReviewsByLivestreamParams { 7 | final String livestreamId; 8 | 9 | GetReviewsByLivestreamParams({required this.livestreamId}); 10 | } 11 | 12 | class GetReviewsByLivestreamUseCase { 13 | final ReviewRepository repository; 14 | 15 | GetReviewsByLivestreamUseCase(this.repository); 16 | 17 | Future>> call(GetReviewsByLivestreamParams params) async { 18 | return repository.getReviewsByLivestreamId(params.livestreamId); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/entities/products/product_detail_attribute_entity.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | class ValueImagePair extends Equatable { 4 | final String value; 5 | final String? imageUrl; 6 | 7 | const ValueImagePair({ 8 | required this.value, 9 | this.imageUrl, 10 | }); 11 | 12 | @override 13 | List get props => [value, imageUrl]; 14 | } 15 | 16 | class ProductDetailAttributeEntity extends Equatable { 17 | final String attributeName; 18 | final List valueImagePairs; 19 | 20 | const ProductDetailAttributeEntity({ 21 | required this.attributeName, 22 | required this.valueImagePairs, 23 | }); 24 | 25 | @override 26 | List get props => [attributeName, valueImagePairs]; 27 | } -------------------------------------------------------------------------------- /stream_cart_mobile/windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/presentation/widgets/review/rating_stars.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import '../../theme/app_colors.dart'; 3 | 4 | class RatingStars extends StatelessWidget { 5 | final int value; // 1..5 6 | final double size; 7 | const RatingStars({super.key, required this.value, this.size = 16}); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return Row( 12 | mainAxisSize: MainAxisSize.min, 13 | children: List.generate(5, (i) { 14 | final filled = i < value; 15 | return Icon( 16 | filled ? Icons.star_rounded : Icons.star_border_rounded, 17 | color: filled ? AppColors.brandPrimary : Colors.grey.shade400, 18 | size: size, 19 | ); 20 | }), 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/chat/mark_chat_room_as_read_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | import '../../repositories/chat_repository.dart'; 4 | import '../../../core/error/failures.dart'; 5 | 6 | class MarkChatRoomAsReadUseCase { 7 | final ChatRepository repository; 8 | 9 | MarkChatRoomAsReadUseCase(this.repository); 10 | 11 | Future> call(MarkChatRoomAsReadParams params) async { 12 | return await repository.markChatRoomAsRead(params.chatRoomId); 13 | } 14 | } 15 | 16 | class MarkChatRoomAsReadParams extends Equatable { 17 | final String chatRoomId; 18 | 19 | const MarkChatRoomAsReadParams({required this.chatRoomId}); 20 | 21 | @override 22 | List get props => [chatRoomId]; 23 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/order/update_order_status_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/order/order_entity.dart'; 4 | import '../../repositories/order/order_repository.dart'; 5 | 6 | class UpdateOrderStatusParams { 7 | final String orderId; 8 | final int status; 9 | 10 | UpdateOrderStatusParams({ 11 | required this.orderId, 12 | required this.status, 13 | }); 14 | } 15 | 16 | class UpdateOrderStatusUseCase { 17 | final OrderRepository repository; 18 | 19 | UpdateOrderStatusUseCase(this.repository); 20 | 21 | Future> call(UpdateOrderStatusParams params) async { 22 | return await repository.updateOrderStatus(params.orderId, params.status); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/cart/update_cart_item_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../entities/cart/cart_entity.dart'; 3 | import '../../repositories/cart_repository.dart'; 4 | import '../../../core/error/failures.dart'; 5 | 6 | class UpdateCartItemParams { 7 | final String cartItemId; 8 | final int quantity; 9 | 10 | UpdateCartItemParams({ 11 | required this.cartItemId, 12 | required this.quantity, 13 | }); 14 | } 15 | 16 | class UpdateCartItemUseCase { 17 | final CartRepository repository; 18 | 19 | UpdateCartItemUseCase(this.repository); 20 | 21 | Future> call(UpdateCartItemParams params) async { 22 | return await repository.updateCartItem( 23 | params.cartItemId, 24 | params.quantity, 25 | ); 26 | } 27 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/order/create_multiple_orders_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/order/create_order_request_entity.dart'; 4 | import '../../entities/order/order_entity.dart'; 5 | import '../../repositories/order/order_repository.dart'; 6 | 7 | class CreateMultipleOrdersParams { 8 | final CreateOrderRequestEntity request; 9 | 10 | CreateMultipleOrdersParams({required this.request}); 11 | } 12 | 13 | class CreateMultipleOrdersUseCase { 14 | final OrderRepository repository; 15 | 16 | CreateMultipleOrdersUseCase(this.repository); 17 | 18 | Future>> call(CreateMultipleOrdersParams params) async { 19 | return await repository.createMultipleOrders(params.request); 20 | } 21 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/presentation/widgets/review/review_summary_section.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'rating_stars.dart'; 3 | 4 | class ReviewSummarySection extends StatelessWidget { 5 | final String productId; 6 | const ReviewSummarySection({super.key, required this.productId}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | // Placeholder summary; wire to real data later 11 | const avg = 4; 12 | return Container( 13 | color: Colors.white, 14 | padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), 15 | child: Row( 16 | children: const [ 17 | RatingStars(value: avg, size: 20), 18 | SizedBox(width: 8), 19 | Text('4.0 · 128 đánh giá'), 20 | ], 21 | ), 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/auth/otp_usecases.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../entities/auth/otp_entities.dart'; 3 | import '../../repositories/auth_repository.dart'; 4 | import '../../../core/error/failures.dart'; 5 | 6 | class VerifyOtpUseCase { 7 | final AuthRepository repository; 8 | 9 | VerifyOtpUseCase(this.repository); 10 | 11 | Future> call(VerifyOtpRequestEntity request) async { 12 | return await repository.verifyOtp(request); 13 | } 14 | } 15 | 16 | class ResendOtpUseCase { 17 | final AuthRepository repository; 18 | 19 | ResendOtpUseCase(this.repository); 20 | 21 | Future> call(ResendOtpRequestEntity request) async { 22 | return await repository.resendOtp(request); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/entities/products/product_image_entity.dart: -------------------------------------------------------------------------------- 1 | class ProductImageEntity { 2 | final String id; 3 | final String productId; 4 | final String? variantId; 5 | final String imageUrl; 6 | final bool isPrimary; 7 | final int displayOrder; 8 | final String? altText; 9 | final DateTime createdAt; 10 | final String createdBy; 11 | final DateTime lastModifiedAt; 12 | final String lastModifiedBy; 13 | 14 | const ProductImageEntity({ 15 | required this.id, 16 | required this.productId, 17 | this.variantId, 18 | required this.imageUrl, 19 | required this.isPrimary, 20 | required this.displayOrder, 21 | this.altText, 22 | required this.createdAt, 23 | required this.createdBy, 24 | required this.lastModifiedAt, 25 | required this.lastModifiedBy, 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/repositories/shop_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../core/error/failures.dart'; 3 | import '../entities/shop/shop.dart'; 4 | import '../entities/products/product_entity.dart'; 5 | import '../../data/models/shop/shop_model.dart'; 6 | 7 | abstract class ShopRepository { 8 | Future> getShops({ 9 | int pageNumber = 1, 10 | int pageSize = 10, 11 | String? status, 12 | String? approvalStatus, 13 | String? searchTerm, 14 | String? sortBy, 15 | bool ascending = true, 16 | }); 17 | 18 | Future> getShopById(String shopId); 19 | 20 | Future>> getProductsByShop(String shopId); 21 | 22 | Future> getProductCountByShop(String shopId); 23 | } 24 | -------------------------------------------------------------------------------- /stream_cart_mobile/android/build.gradle.kts: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | dependencies { 7 | classpath("com.google.gms:google-services:4.4.0") 8 | } 9 | } 10 | 11 | allprojects { 12 | repositories { 13 | google() 14 | mavenCentral() 15 | } 16 | } 17 | 18 | val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get() 19 | rootProject.layout.buildDirectory.value(newBuildDir) 20 | 21 | subprojects { 22 | val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) 23 | project.layout.buildDirectory.value(newSubprojectBuildDir) 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(":app") 27 | } 28 | 29 | tasks.register("clean") { 30 | delete(rootProject.layout.buildDirectory) 31 | } 32 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/entities/deliveries/from_shop_entity.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'shipping_item_entity.dart'; 3 | 4 | class FromShopEntity extends Equatable { 5 | final String fromShopId; 6 | final List items; 7 | 8 | const FromShopEntity({ 9 | required this.fromShopId, 10 | required this.items, 11 | }); 12 | 13 | FromShopEntity copyWith({ 14 | String? fromShopId, 15 | List? items, 16 | }) { 17 | return FromShopEntity( 18 | fromShopId: fromShopId ?? this.fromShopId, 19 | items: items ?? this.items, 20 | ); 21 | } 22 | 23 | @override 24 | List get props => [fromShopId, items]; 25 | 26 | @override 27 | String toString() { 28 | return 'FromShopEntity(fromShopId: $fromShopId, items: $items)'; 29 | } 30 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/cart/get_all_cart_items_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/cart/cart_entity.dart'; 4 | import '../../repositories/cart_repository.dart'; 5 | 6 | class GetAllCartItemsUseCase { 7 | final CartRepository repository; 8 | 9 | GetAllCartItemsUseCase(this.repository); 10 | 11 | Future>> call() async { 12 | final result = await repository.getCartItems(); 13 | 14 | return result.fold( 15 | (failure) => Left(failure), 16 | (cartData) { 17 | List allItems = []; 18 | for (var shop in cartData.cartItemByShop) { 19 | allItems.addAll(shop.products); 20 | } 21 | return Right(allItems); 22 | }, 23 | ); 24 | } 25 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/cart/remove_from_cart_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../repositories/cart_repository.dart'; 3 | import '../../../core/error/failures.dart'; 4 | 5 | @Deprecated('Use RemoveCartItemUseCase instead') 6 | class RemoveFromCartParams { 7 | final String productId; 8 | final String? variantId; 9 | 10 | RemoveFromCartParams({ 11 | required this.productId, 12 | this.variantId, 13 | }); 14 | } 15 | 16 | @Deprecated('Use RemoveCartItemUseCase instead') 17 | class RemoveFromCartUseCase { 18 | final CartRepository repository; 19 | 20 | RemoveFromCartUseCase(this.repository); 21 | 22 | Future> call(RemoveFromCartParams params) async { 23 | return await repository.removeFromCart( 24 | params.productId, 25 | params.variantId, 26 | ); 27 | } 28 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/entities/auth/register_request_entity.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | class RegisterRequestEntity extends Equatable { 4 | final String username; 5 | final String email; 6 | final String password; 7 | final String phoneNumber; 8 | final String fullname; 9 | final String? avatarURL; 10 | final int role; 11 | 12 | const RegisterRequestEntity({ 13 | required this.username, 14 | required this.email, 15 | required this.password, 16 | required this.phoneNumber, 17 | required this.fullname, 18 | this.avatarURL, 19 | required this.role, 20 | }); 21 | 22 | @override 23 | List get props => [ 24 | username, 25 | email, 26 | password, 27 | phoneNumber, 28 | fullname, 29 | avatarURL, 30 | role, 31 | ]; 32 | } 33 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/chat/load_chat_room_detail_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | import '../../entities/chat/chat_room_entity.dart'; 4 | import '../../repositories/chat_repository.dart'; 5 | import '../../../core/error/failures.dart'; 6 | 7 | class LoadChatRoomDetailUseCase { 8 | final ChatRepository repository; 9 | 10 | LoadChatRoomDetailUseCase(this.repository); 11 | 12 | Future> call(LoadChatRoomDetailParams params) async { 13 | return await repository.getChatRoomDetail(params.chatRoomId); 14 | } 15 | } 16 | 17 | class LoadChatRoomDetailParams extends Equatable { 18 | final String chatRoomId; 19 | 20 | const LoadChatRoomDetailParams({required this.chatRoomId}); 21 | 22 | @override 23 | List get props => [chatRoomId]; 24 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/cart/add_to_cart_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../entities/cart/cart_entity.dart'; 3 | import '../../repositories/cart_repository.dart'; 4 | import '../../../core/error/failures.dart'; 5 | 6 | class AddToCartParams { 7 | final String productId; 8 | final String? variantId; 9 | final int quantity; 10 | 11 | AddToCartParams({ 12 | required this.productId, 13 | this.variantId, 14 | required this.quantity, 15 | }); 16 | } 17 | 18 | class AddToCartUseCase { 19 | final CartRepository repository; 20 | 21 | AddToCartUseCase(this.repository); 22 | 23 | Future> call(AddToCartParams params) async { 24 | return await repository.addToCart( 25 | params.productId, 26 | params.variantId, 27 | params.quantity, 28 | ); 29 | } 30 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/order/add_order_item_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/order/add_order_item_request_entity.dart'; 4 | import '../../entities/order/order_item_entity.dart'; 5 | import '../../repositories/order/order_item_repository.dart'; 6 | 7 | class AddOrderItemParams { 8 | final String orderId; 9 | final AddOrderItemRequestEntity request; 10 | 11 | AddOrderItemParams({ 12 | required this.orderId, 13 | required this.request, 14 | }); 15 | } 16 | 17 | class AddOrderItemUseCase { 18 | final OrderItemRepository repository; 19 | 20 | AddOrderItemUseCase(this.repository); 21 | 22 | Future> call(AddOrderItemParams params) async { 23 | return await repository.addOrderItem(params.orderId, params.request); 24 | } 25 | } -------------------------------------------------------------------------------- /stream_cart_mobile/android/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | val flutterSdkPath = run { 3 | val properties = java.util.Properties() 4 | file("local.properties").inputStream().use { properties.load(it) } 5 | val flutterSdkPath = properties.getProperty("flutter.sdk") 6 | require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } 7 | flutterSdkPath 8 | } 9 | 10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") 11 | 12 | repositories { 13 | google() 14 | mavenCentral() 15 | gradlePluginPortal() 16 | } 17 | } 18 | 19 | plugins { 20 | id("dev.flutter.flutter-plugin-loader") version "1.0.0" 21 | id("com.android.application") version "8.7.0" apply false 22 | id("org.jetbrains.kotlin.android") version "1.8.22" apply false 23 | } 24 | 25 | include(":app") 26 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/data/models/delivery/from_shop_model.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'from_shop_model.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | FromShopModel _$FromShopModelFromJson(Map json) => 10 | FromShopModel( 11 | fromShopId: json['fromShopId'] as String, 12 | items: (json['items'] as List) 13 | .map((e) => ShippingItemModel.fromJson(e as Map)) 14 | .toList(), 15 | ); 16 | 17 | Map _$FromShopModelToJson(FromShopModel instance) => 18 | { 19 | 'fromShopId': instance.fromShopId, 20 | 'items': instance.items.map((e) => e.toJson()).toList(), 21 | }; 22 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/chatbot/send_chatbot_message_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | 4 | import '../../../core/error/failures.dart'; 5 | import '../../entities/chatbot/chat_bot_entity.dart'; 6 | import '../../repositories/chatbot/chat_bot_repository.dart'; 7 | 8 | class SendChatBotMessageUseCase { 9 | final ChatBotRepository repository; 10 | 11 | SendChatBotMessageUseCase(this.repository); 12 | 13 | Future> call(SendChatBotMessageParams params) async { 14 | return await repository.sendMessage(message: params.message); 15 | } 16 | } 17 | 18 | class SendChatBotMessageParams extends Equatable { 19 | final String message; 20 | 21 | const SendChatBotMessageParams({required this.message}); 22 | 23 | @override 24 | List get props => [message]; 25 | } 26 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/data/models/auth/login_request_model.dart: -------------------------------------------------------------------------------- 1 | import '../../../domain/entities/auth/login_request_entity.dart'; 2 | 3 | class LoginRequestModel { 4 | final String username; 5 | final String password; 6 | 7 | LoginRequestModel({ 8 | required this.username, 9 | required this.password, 10 | }); 11 | 12 | factory LoginRequestModel.fromJson(Map json) { 13 | return LoginRequestModel( 14 | username: json['username'] ?? '', 15 | password: json['password'] ?? '', 16 | ); 17 | } 18 | 19 | Map toJson() { 20 | return { 21 | 'username': username, 22 | 'password': password, 23 | }; 24 | } 25 | 26 | factory LoginRequestModel.fromEntity(LoginRequestEntity entity) { 27 | return LoginRequestModel( 28 | username: entity.username, 29 | password: entity.password, 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/account/update_user_profile.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/account/user_profile_entity.dart'; 4 | import '../../repositories/profile_repository.dart'; 5 | import '../../../data/models/account/update_profile_model.dart'; 6 | 7 | class UpdateUserProfileUseCase { 8 | final ProfileRepository repository; 9 | 10 | UpdateUserProfileUseCase(this.repository); 11 | 12 | Future> call(UpdateUserProfileParams params) async { 13 | return await repository.updateUserProfile(params.userId, params.request); 14 | } 15 | } 16 | 17 | class UpdateUserProfileParams { 18 | final String userId; 19 | final UpdateProfileRequestModel request; 20 | 21 | UpdateUserProfileParams({ 22 | required this.userId, 23 | required this.request, 24 | }); 25 | } 26 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/chat/send_typing_indicator_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | import '../../repositories/chat_repository.dart'; 4 | import '../../../core/error/failures.dart'; 5 | 6 | class SendTypingIndicatorUseCase { 7 | final ChatRepository repository; 8 | 9 | SendTypingIndicatorUseCase(this.repository); 10 | 11 | Future> call(SendTypingIndicatorParams params) async { 12 | return await repository.sendTypingIndicator(params.chatRoomId, params.isTyping); 13 | } 14 | } 15 | 16 | class SendTypingIndicatorParams extends Equatable { 17 | final String chatRoomId; 18 | final bool isTyping; 19 | 20 | const SendTypingIndicatorParams({ 21 | required this.chatRoomId, 22 | required this.isTyping, 23 | }); 24 | 25 | @override 26 | List get props => [chatRoomId, isTyping]; 27 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/livestream/send_message_livestream_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | 3 | import '../../../core/error/failures.dart'; 4 | import '../../entities/livestream/livestream_message_entity.dart'; 5 | import '../../repositories/livestream/livestream_message_repository.dart'; 6 | 7 | class SendMessageLiveStreamUseCase { 8 | final LiveStreamMessageRepository repository; 9 | SendMessageLiveStreamUseCase(this.repository); 10 | 11 | Future> call({ 12 | required String livestreamId, 13 | required String message, 14 | int messageType = 0, 15 | String? replyToMessageId, 16 | }) { 17 | return repository.sendMessageToLiveStream( 18 | livestreamId: livestreamId, 19 | message: message, 20 | messageType: messageType, 21 | replyToMessageId: replyToMessageId, 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/shop_voucher/apply_shop_voucher_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../entities/shop_voucher/shop_voucher_entity.dart'; 3 | import '../../repositories/shop_voucher/shop_voucher_repository.dart'; 4 | import '../../../core/error/failures.dart'; 5 | 6 | class ApplyShopVoucherParams { 7 | final String code; 8 | final ApplyShopVoucherRequestEntity request; 9 | 10 | const ApplyShopVoucherParams({ 11 | required this.code, 12 | required this.request, 13 | }); 14 | } 15 | 16 | class ApplyShopVoucherUseCase { 17 | final ShopVoucherRepository repository; 18 | 19 | ApplyShopVoucherUseCase(this.repository); 20 | 21 | Future> call(ApplyShopVoucherParams params) { 22 | return repository.applyVoucher( 23 | code: params.code, 24 | request: params.request, 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/chat/join_chat_room_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | import '../../../core/error/failures.dart'; 4 | import '../../../core/services/signalr_service.dart'; 5 | 6 | class JoinChatRoomUseCase { 7 | final SignalRService signalRService; 8 | 9 | JoinChatRoomUseCase(this.signalRService); 10 | 11 | Future> call(JoinChatRoomParams params) async { 12 | try { 13 | await signalRService.joinChatRoom(params.chatRoomId); 14 | return const Right(null); 15 | } catch (e) { 16 | return Left(NetworkFailure('Failed to join chat room: $e')); 17 | } 18 | } 19 | } 20 | 21 | class JoinChatRoomParams extends Equatable { 22 | final String chatRoomId; 23 | 24 | const JoinChatRoomParams({required this.chatRoomId}); 25 | 26 | @override 27 | List get props => [chatRoomId]; 28 | } -------------------------------------------------------------------------------- /stream_cart_mobile/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 12.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/chat/leave_chat_room_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | import '../../../core/error/failures.dart'; 4 | import '../../../core/services/signalr_service.dart'; 5 | 6 | class LeaveChatRoomUseCase { 7 | final SignalRService signalRService; 8 | 9 | LeaveChatRoomUseCase(this.signalRService); 10 | 11 | Future> call(LeaveChatRoomParams params) async { 12 | try { 13 | await signalRService.leaveChatRoom(params.chatRoomId); 14 | return const Right(null); 15 | } catch (e) { 16 | return Left(NetworkFailure('Failed to leave chat room: $e')); 17 | } 18 | } 19 | } 20 | 21 | class LeaveChatRoomParams extends Equatable { 22 | final String chatRoomId; 23 | 24 | const LeaveChatRoomParams({required this.chatRoomId}); 25 | 26 | @override 27 | List get props => [chatRoomId]; 28 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/presentation/pages/order/order_detail_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | 4 | import '../../../core/di/dependency_injection.dart'; 5 | import '../../blocs/order/order_bloc.dart'; 6 | import '../../blocs/order_item/order_item_bloc.dart'; 7 | import '../../widgets/order_detail/order_detail_view_widget.dart'; 8 | 9 | // Chi tiết đơn hàng 10 | class OrderDetailPage extends StatelessWidget { 11 | final String orderId; 12 | 13 | OrderDetailPage({super.key, required this.orderId}); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return MultiBlocProvider( 18 | providers: [ 19 | BlocProvider(create: (context) => getIt()), 20 | BlocProvider(create: (context) => getIt()), 21 | ], 22 | child: OrderDetailViewWidget(orderId: orderId), 23 | ); 24 | } 25 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/product/get_products_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../entities/products/product_entity.dart'; 3 | import '../../repositories/home_repository.dart'; 4 | import '../../../core/error/failures.dart'; 5 | 6 | class GetProductsUseCase { 7 | final HomeRepository repository; 8 | 9 | GetProductsUseCase(this.repository); 10 | 11 | Future>> call({int page = 1, int limit = 20}) async { 12 | return await repository.getProducts(page: page, limit: limit); 13 | } 14 | } 15 | 16 | class SearchProductsUseCase { 17 | final HomeRepository repository; 18 | 19 | SearchProductsUseCase(this.repository); 20 | 21 | Future>> call({required String query, int page = 1, int limit = 20}) async { 22 | return await repository.searchProducts(query: query, page: page, limit: limit); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/product_variants/get_available_variants.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/products/product_variants_entity.dart'; 4 | import '../../repositories/product/product_variants_repository.dart'; 5 | 6 | class GetAvailableVariants { 7 | final ProductVariantsRepository repository; 8 | 9 | GetAvailableVariants(this.repository); 10 | 11 | Future>> call(String productId) async { 12 | final result = await repository.getProductVariantsByProductId(productId); 13 | 14 | return result.fold( 15 | (failure) => Left(failure), 16 | (variants) { 17 | // Lọc chỉ những variant còn hàng 18 | final availableVariants = variants.where((variant) => variant.stock > 0).toList(); 19 | return Right(availableVariants); 20 | }, 21 | ); 22 | } 23 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/data/models/cart_live/price_data_live_model.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'price_data_live_model.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | PriceDataLiveModel _$PriceDataLiveModelFromJson(Map json) => 10 | PriceDataLiveModel( 11 | currentPrice: (json['currentPrice'] as num).toDouble(), 12 | originalPrice: (json['originalPrice'] as num).toDouble(), 13 | discount: (json['discount'] as num).toDouble(), 14 | ); 15 | 16 | Map _$PriceDataLiveModelToJson(PriceDataLiveModel instance) => 17 | { 18 | 'currentPrice': instance.currentPrice, 19 | 'originalPrice': instance.originalPrice, 20 | 'discount': instance.discount, 21 | }; 22 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/data/models/order/add_order_item_request_model.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'add_order_item_request_model.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | AddOrderItemRequestModel _$AddOrderItemRequestModelFromJson( 10 | Map json) => 11 | AddOrderItemRequestModel( 12 | productId: json['productId'] as String, 13 | variantId: json['variantId'] as String?, 14 | quantity: (json['quantity'] as num).toInt(), 15 | ); 16 | 17 | Map _$AddOrderItemRequestModelToJson( 18 | AddOrderItemRequestModel instance) => 19 | { 20 | 'productId': instance.productId, 21 | 'variantId': instance.variantId, 22 | 'quantity': instance.quantity, 23 | }; 24 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/chat/update_message_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | import '../../entities/chat/chat_message_entity.dart'; 4 | import '../../repositories/chat_repository.dart'; 5 | import '../../../core/error/failures.dart'; 6 | 7 | class UpdateMessageUseCase { 8 | final ChatRepository repository; 9 | 10 | UpdateMessageUseCase(this.repository); 11 | 12 | Future> call(UpdateMessageParams params) async { 13 | return await repository.updateMessage( 14 | messageId: params.messageId, 15 | content: params.content, 16 | ); 17 | } 18 | } 19 | 20 | class UpdateMessageParams extends Equatable { 21 | final String messageId; 22 | final String content; 23 | 24 | const UpdateMessageParams({ 25 | required this.messageId, 26 | required this.content, 27 | }); 28 | 29 | @override 30 | List get props => [messageId, content]; 31 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/presentation/widgets/review/merchant_reply.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import '../../theme/app_colors.dart'; 3 | 4 | class MerchantReply extends StatelessWidget { 5 | final String shopName; 6 | final String replyText; 7 | const MerchantReply({super.key, required this.shopName, required this.replyText}); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return Container( 12 | width: double.infinity, 13 | decoration: BoxDecoration( 14 | color: AppColors.bubbleNeutral, 15 | borderRadius: BorderRadius.circular(12), 16 | ), 17 | padding: const EdgeInsets.all(12), 18 | child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ 19 | Text('Phản hồi từ $shopName', style: Theme.of(context).textTheme.labelMedium), 20 | const SizedBox(height: 6), 21 | Text(replyText, style: Theme.of(context).textTheme.bodyMedium), 22 | ]), 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/order/get_orders_by_account_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | import '../../../core/error/failures.dart'; 4 | import '../../entities/order/order_entity.dart'; 5 | import '../../repositories/order/order_repository.dart'; 6 | 7 | class GetOrdersByAccountParams extends Equatable { 8 | final String accountId; 9 | final int? status; 10 | 11 | const GetOrdersByAccountParams({ 12 | required this.accountId, 13 | this.status, 14 | }); 15 | 16 | @override 17 | List get props => [accountId, status]; 18 | } 19 | 20 | class GetOrdersByAccountUseCase { 21 | final OrderRepository repository; 22 | 23 | GetOrdersByAccountUseCase(this.repository); 24 | 25 | Future>> call(GetOrdersByAccountParams params) async { 26 | return await repository.getOrdersByAccountId( 27 | params.accountId, 28 | status: params.status, 29 | ); 30 | } 31 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/core/network/network_config.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import '../config/env.dart'; 3 | import 'auth_interceptor.dart'; 4 | import '../services/storage_service.dart'; 5 | 6 | class NetworkConfig { 7 | static Dio createDio({StorageService? storageService}) { 8 | final dio = Dio(); 9 | dio.options.baseUrl = Env.baseUrl; 10 | // Timeout configuration 11 | dio.options.connectTimeout = const Duration(seconds: 30); 12 | dio.options.receiveTimeout = const Duration(seconds: 30); 13 | dio.options.headers = { 14 | 'Content-Type': 'application/json', 15 | 'Accept': 'application/json', 16 | }; 17 | 18 | dio.interceptors.add(LogInterceptor( 19 | requestBody: true, 20 | responseBody: true, 21 | requestHeader: true, 22 | responseHeader: true, 23 | )); 24 | if (storageService != null) { 25 | dio.interceptors.add(AuthInterceptor(storageService)); 26 | } 27 | 28 | return dio; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/review/update_review_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/review/review_entity.dart'; 4 | import '../../repositories/review/review_repository.dart'; 5 | 6 | class UpdateReviewParams { 7 | final String reviewId; 8 | final int? rating; 9 | final String? reviewText; 10 | final List? imageUrls; 11 | 12 | UpdateReviewParams({ 13 | required this.reviewId, 14 | this.rating, 15 | this.reviewText, 16 | this.imageUrls, 17 | }); 18 | } 19 | 20 | class UpdateReviewUseCase { 21 | final ReviewRepository repository; 22 | 23 | UpdateReviewUseCase(this.repository); 24 | 25 | Future> call(UpdateReviewParams params) async { 26 | return repository.updateReview( 27 | params.reviewId, 28 | rating: params.rating, 29 | reviewText: params.reviewText, 30 | imageUrls: params.imageUrls, 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /stream_cart_mobile/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .build/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | .swiftpm/ 13 | migrate_working_dir/ 14 | 15 | # IntelliJ related 16 | *.iml 17 | *.ipr 18 | *.iws 19 | .idea/ 20 | 21 | # The .vscode folder contains launch configuration and tasks you configure in 22 | # VS Code which you may wish to be included in version control, so this line 23 | # is commented out by default. 24 | #.vscode/ 25 | 26 | # Flutter/Dart/Pub related 27 | **/doc/api/ 28 | **/ios/Flutter/.last_build_id 29 | .dart_tool/ 30 | .flutter-plugins 31 | .flutter-plugins-dependencies 32 | .pub-cache/ 33 | .pub/ 34 | /build/ 35 | 36 | 37 | .env* 38 | 39 | # Symbolication related 40 | app.*.symbols 41 | 42 | # Obfuscation related 43 | app.*.map.json 44 | 45 | # Android Studio will place build artifacts here 46 | /android/app/debug 47 | /android/app/profile 48 | /android/app/release 49 | 50 | # Environment variables (sensitive data) 51 | .env 52 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/entities/order/add_order_item_request_entity.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | class AddOrderItemRequestEntity extends Equatable { 4 | final String productId; 5 | final String? variantId; 6 | final int quantity; 7 | 8 | const AddOrderItemRequestEntity({ 9 | required this.productId, 10 | this.variantId, 11 | required this.quantity, 12 | }); 13 | 14 | AddOrderItemRequestEntity copyWith({ 15 | String? productId, 16 | String? variantId, 17 | int? quantity, 18 | }) { 19 | return AddOrderItemRequestEntity( 20 | productId: productId ?? this.productId, 21 | variantId: variantId ?? this.variantId, 22 | quantity: quantity ?? this.quantity, 23 | ); 24 | } 25 | 26 | @override 27 | List get props => [productId, variantId, quantity]; 28 | 29 | @override 30 | String toString() { 31 | return 'AddOrderItemRequestEntity(productId: $productId, variantId: $variantId, quantity: $quantity)'; 32 | } 33 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/data/models/cart_live/cart_item_by_shop_live_model.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'cart_item_by_shop_live_model.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | CartItemByShopLiveModel _$CartItemByShopLiveModelFromJson( 10 | Map json) => 11 | CartItemByShopLiveModel( 12 | shopId: json['shopId'] as String, 13 | shopName: json['shopName'] as String, 14 | products: 15 | CartItemByShopLiveModel._productsFromJson(json['products'] as List), 16 | ); 17 | 18 | Map _$CartItemByShopLiveModelToJson( 19 | CartItemByShopLiveModel instance) => 20 | { 21 | 'shopId': instance.shopId, 22 | 'shopName': instance.shopName, 23 | 'products': CartItemByShopLiveModel._productsToJson(instance.products), 24 | }; 25 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/product/get_product_images_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../entities/products/product_image_entity.dart'; 3 | import '../../repositories/home_repository.dart'; 4 | import '../../../core/error/failures.dart'; 5 | 6 | class GetProductImagesUseCase { 7 | final HomeRepository repository; 8 | 9 | GetProductImagesUseCase(this.repository); 10 | 11 | Future>> call(String productId) async { 12 | print('[GetProductImagesUseCase] Fetching images for product: $productId'); 13 | 14 | final result = await repository.getProductImages(productId); 15 | 16 | return result.fold( 17 | (failure) { 18 | print('[GetProductImagesUseCase] Error: ${failure.message}'); 19 | return Left(failure); 20 | }, 21 | (images) { 22 | print('[GetProductImagesUseCase] Successfully fetched ${images.length} images'); 23 | return Right(images); 24 | }, 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/entities/cart_live/price_data_live_entity.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | class PriceDataLiveEntity extends Equatable { 4 | final double currentPrice; 5 | final double originalPrice; 6 | final double discount; 7 | 8 | const PriceDataLiveEntity({ 9 | required this.currentPrice, 10 | required this.originalPrice, 11 | required this.discount, 12 | }); 13 | 14 | PriceDataLiveEntity copyWith({ 15 | double? currentPrice, 16 | double? originalPrice, 17 | double? discount, 18 | }) { 19 | return PriceDataLiveEntity( 20 | currentPrice: currentPrice ?? this.currentPrice, 21 | originalPrice: originalPrice ?? this.originalPrice, 22 | discount: discount ?? this.discount, 23 | ); 24 | } 25 | 26 | @override 27 | List get props => [currentPrice, originalPrice, discount]; 28 | 29 | @override 30 | String toString() => 'PriceDataLiveEntity(currentPrice: $currentPrice, originalPrice: $originalPrice, discount: $discount)'; 31 | } 32 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/notification/get_notifications_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/notification/notification_entity.dart'; 4 | import '../../repositories/notification_repository.dart'; 5 | 6 | class GetNotificationsUseCase { 7 | final NotificationRepository repository; 8 | 9 | GetNotificationsUseCase(this.repository); 10 | 11 | Future> call(GetNotificationsParams params) async { 12 | return await repository.getNotifications( 13 | type: params.type, 14 | isRead: params.isRead, 15 | pageIndex: params.pageIndex, 16 | pageSize: params.pageSize, 17 | ); 18 | } 19 | } 20 | 21 | class GetNotificationsParams { 22 | final String? type; 23 | final bool? isRead; 24 | final int? pageIndex; 25 | final int? pageSize; 26 | 27 | GetNotificationsParams({ 28 | this.type, 29 | this.isRead, 30 | this.pageIndex, 31 | this.pageSize, 32 | }); 33 | } 34 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/product_variants/check_variant_availability.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../repositories/product/product_variants_repository.dart'; 4 | 5 | class CheckVariantAvailabilityParams { 6 | final String variantId; 7 | final int requestedQuantity; 8 | 9 | CheckVariantAvailabilityParams({ 10 | required this.variantId, 11 | required this.requestedQuantity, 12 | }); 13 | } 14 | 15 | class CheckVariantAvailability { 16 | final ProductVariantsRepository repository; 17 | 18 | CheckVariantAvailability(this.repository); 19 | 20 | Future> call(CheckVariantAvailabilityParams params) async { 21 | final result = await repository.getProductVariantById(params.variantId); 22 | 23 | return result.fold( 24 | (failure) => Left(failure), 25 | (variant) { 26 | final isAvailable = variant.stock >= params.requestedQuantity; 27 | return Right(isAvailable); 28 | }, 29 | ); 30 | } 31 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/entities/cart_live/cart_item_by_shop_live_entity.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'cart_product_live_entity.dart'; 3 | 4 | class CartItemByShopLiveEntity extends Equatable { 5 | final String shopId; 6 | final String shopName; 7 | final List products; 8 | 9 | const CartItemByShopLiveEntity({ 10 | required this.shopId, 11 | required this.shopName, 12 | required this.products, 13 | }); 14 | 15 | CartItemByShopLiveEntity copyWith({ 16 | String? shopId, 17 | String? shopName, 18 | List? products, 19 | }) { 20 | return CartItemByShopLiveEntity( 21 | shopId: shopId ?? this.shopId, 22 | shopName: shopName ?? this.shopName, 23 | products: products ?? this.products, 24 | ); 25 | } 26 | 27 | @override 28 | List get props => [shopId, shopName, products]; 29 | 30 | @override 31 | String toString() => 'CartItemByShopLiveEntity(shopId: $shopId, shopName: $shopName, products: $products)'; 32 | } 33 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/entities/deliveries/preview_deliveries_response_entity.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | import 'service_response_entity.dart'; 4 | 5 | class PreviewDeliveriesResponseEntity extends Equatable { 6 | final List serviceResponses; 7 | final double totalAmount; 8 | 9 | const PreviewDeliveriesResponseEntity({ 10 | required this.serviceResponses, 11 | required this.totalAmount, 12 | }); 13 | 14 | PreviewDeliveriesResponseEntity copyWith({ 15 | List? serviceResponses, 16 | double? totalAmount, 17 | }) { 18 | return PreviewDeliveriesResponseEntity( 19 | serviceResponses: serviceResponses ?? this.serviceResponses, 20 | totalAmount: totalAmount ?? this.totalAmount, 21 | ); 22 | } 23 | 24 | @override 25 | List get props => [serviceResponses, totalAmount]; 26 | 27 | @override 28 | String toString() { 29 | return 'PreviewDeliveriesResponseEntity(serviceResponses: $serviceResponses, totalAmount: $totalAmount)'; 30 | } 31 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/presentation/blocs/home/home_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | abstract class HomeEvent extends Equatable { 4 | const HomeEvent(); 5 | 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class LoadHomeDataEvent extends HomeEvent { 11 | const LoadHomeDataEvent(); 12 | } 13 | 14 | class RefreshHomeDataEvent extends HomeEvent { 15 | const RefreshHomeDataEvent(); 16 | } 17 | 18 | class LoadMoreProductsEvent extends HomeEvent { 19 | const LoadMoreProductsEvent(); 20 | } 21 | 22 | class LoadProductImagesEvent extends HomeEvent { 23 | final List productIds; 24 | 25 | const LoadProductImagesEvent(this.productIds); 26 | 27 | @override 28 | List get props => [productIds]; 29 | } 30 | 31 | class LoadUserProfileEvent extends HomeEvent { 32 | const LoadUserProfileEvent(); 33 | } 34 | 35 | class LoadFlashSalesEvent extends HomeEvent { 36 | const LoadFlashSalesEvent(); 37 | } 38 | 39 | class RefreshFlashSalesEvent extends HomeEvent { 40 | const RefreshFlashSalesEvent(); 41 | } 42 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/chat/load_chat_rooms_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | import '../../entities/chat/chat_room_entity.dart'; 4 | import '../../repositories/chat_repository.dart'; 5 | import '../../../core/error/failures.dart'; 6 | 7 | class LoadChatRoomsUseCase { 8 | final ChatRepository repository; 9 | 10 | LoadChatRoomsUseCase(this.repository); 11 | 12 | Future> call(LoadChatRoomsParams params) async { 13 | return await repository.getChatRooms( 14 | pageNumber: params.pageNumber, 15 | pageSize: params.pageSize, 16 | isActive: params.isActive, 17 | ); 18 | } 19 | } 20 | 21 | class LoadChatRoomsParams extends Equatable { 22 | final int pageNumber; 23 | final int pageSize; 24 | final bool? isActive; 25 | 26 | const LoadChatRoomsParams({ 27 | this.pageNumber = 1, 28 | this.pageSize = 20, 29 | this.isActive, 30 | }); 31 | 32 | @override 33 | List get props => [pageNumber, pageSize, isActive]; 34 | } -------------------------------------------------------------------------------- /stream_cart_mobile/web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Stream Cart Mobile", 3 | "short_name": "Stream Cart", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#hexcode", 7 | "theme_color": "#hexcode", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/core/error/failures.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | abstract class Failure extends Equatable { 4 | final String message; 5 | 6 | const Failure(this.message); 7 | 8 | @override 9 | List get props => [message]; 10 | } 11 | 12 | class ServerFailure extends Failure { 13 | const ServerFailure(super.message); 14 | } 15 | 16 | class CacheFailure extends Failure { 17 | const CacheFailure(super.message); 18 | } 19 | 20 | class NetworkFailure extends Failure { 21 | const NetworkFailure(super.message); 22 | } 23 | 24 | class UnauthorizedFailure extends Failure { 25 | const UnauthorizedFailure(super.message); 26 | } 27 | 28 | class ValidationFailure extends Failure { 29 | const ValidationFailure(super.message); 30 | } 31 | 32 | class ConflictFailure extends Failure { 33 | const ConflictFailure(super.message); 34 | } 35 | 36 | class ExpiredFailure extends Failure { 37 | const ExpiredFailure(super.message); 38 | } 39 | 40 | class TooManyRequestsFailure extends Failure { 41 | const TooManyRequestsFailure(super.message); 42 | } 43 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/shop_voucher/get_available_shop_vouchers_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/shop_voucher/available_shop_voucher_entity.dart'; 4 | import '../../repositories/shop_voucher/shop_voucher_repository.dart'; 5 | 6 | class GetAvailableShopVouchersParams { 7 | final double orderAmount; 8 | final String? shopId; 9 | final bool sortByDiscountDesc; 10 | 11 | const GetAvailableShopVouchersParams({ 12 | required this.orderAmount, 13 | this.shopId, 14 | this.sortByDiscountDesc = true, 15 | }); 16 | } 17 | 18 | class GetAvailableShopVouchersUseCase { 19 | final ShopVoucherRepository repository; 20 | GetAvailableShopVouchersUseCase(this.repository); 21 | 22 | Future> call(GetAvailableShopVouchersParams params) { 23 | return repository.getAvailableVouchers( 24 | orderAmount: params.orderAmount, 25 | shopId: params.shopId, 26 | sortByDiscountDesc: params.sortByDiscountDesc, 27 | ); 28 | } 29 | } -------------------------------------------------------------------------------- /stream_cart_mobile/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/presentation/widgets/review/review_error.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import '../../theme/app_colors.dart'; 3 | 4 | class ReviewErrorView extends StatelessWidget { 5 | final String message; 6 | final VoidCallback onRetry; 7 | const ReviewErrorView({super.key, required this.message, required this.onRetry}); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return Center( 12 | child: Padding( 13 | padding: const EdgeInsets.all(24), 14 | child: Column(mainAxisSize: MainAxisSize.min, children: [ 15 | const Icon(Icons.error_outline, size: 48, color: Colors.redAccent), 16 | const SizedBox(height: 12), 17 | Text(message, textAlign: TextAlign.center), 18 | const SizedBox(height: 12), 19 | ElevatedButton( 20 | onPressed: onRetry, 21 | style: ElevatedButton.styleFrom(backgroundColor: AppColors.brandPrimary, foregroundColor: Colors.white), 22 | child: const Text('Thử lại'), 23 | ) 24 | ]), 25 | ), 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /stream_cart_mobile/windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /stream_cart_mobile/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/data/models/delivery/preview_deliveries_response_model.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'preview_deliveries_response_model.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | PreviewDeliveriesResponseModel _$PreviewDeliveriesResponseModelFromJson( 10 | Map json) => 11 | PreviewDeliveriesResponseModel( 12 | serviceResponses: (json['serviceResponses'] as List) 13 | .map((e) => ServiceResponseModel.fromJson(e as Map)) 14 | .toList(), 15 | totalAmount: (json['totalAmount'] as num).toDouble(), 16 | ); 17 | 18 | Map _$PreviewDeliveriesResponseModelToJson( 19 | PreviewDeliveriesResponseModel instance) => 20 | { 21 | 'serviceResponses': 22 | instance.serviceResponses.map((e) => e.toJson()).toList(), 23 | 'totalAmount': instance.totalAmount, 24 | }; 25 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/repositories/order/order_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import 'package:stream_cart_mobile/core/error/failures.dart'; 3 | import 'package:stream_cart_mobile/domain/entities/order/order_entity.dart'; 4 | 5 | import '../../entities/order/create_order_request_entity.dart'; 6 | 7 | abstract class OrderRepository { 8 | // GET /api/orders/{id} 9 | Future> getOrderById(String id); 10 | // POST /api/orders/multi 11 | Future>> createMultipleOrders(CreateOrderRequestEntity request); 12 | // POST /api/orders/{id}/cancel 13 | Future> cancelOrder(String id); 14 | // GET /api/orders/account/{accountId} 15 | Future>> getOrdersByAccountId( 16 | String accountId, { 17 | int? status, 18 | }); 19 | //Get order details by order code 20 | Future> getOrderDetailsByCode(String code); 21 | // PUT /api/orders/{id}/status 22 | Future> updateOrderStatus(String id, int status); 23 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/chat/load_shop_chat_rooms_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | import '../../entities/chat/chat_room_entity.dart'; 4 | import '../../repositories/chat_repository.dart'; 5 | import '../../../core/error/failures.dart'; 6 | 7 | class LoadShopChatRoomsUseCase { 8 | final ChatRepository repository; 9 | 10 | LoadShopChatRoomsUseCase(this.repository); 11 | 12 | Future> call(LoadShopChatRoomsParams params) async { 13 | return await repository.getShopChatRooms( 14 | pageNumber: params.pageNumber, 15 | pageSize: params.pageSize, 16 | isActive: params.isActive, 17 | ); 18 | } 19 | } 20 | 21 | class LoadShopChatRoomsParams extends Equatable { 22 | final int pageNumber; 23 | final int pageSize; 24 | final bool? isActive; 25 | 26 | const LoadShopChatRoomsParams({ 27 | this.pageNumber = 1, 28 | this.pageSize = 20, 29 | this.isActive, 30 | }); 31 | 32 | @override 33 | List get props => [pageNumber, pageSize, isActive]; 34 | } -------------------------------------------------------------------------------- /stream_cart_mobile/linux/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | project(runner LANGUAGES CXX) 3 | 4 | # Define the application target. To change its name, change BINARY_NAME in the 5 | # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer 6 | # work. 7 | # 8 | # Any new source files that you add to the application should be added here. 9 | add_executable(${BINARY_NAME} 10 | "main.cc" 11 | "my_application.cc" 12 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 13 | ) 14 | 15 | # Apply the standard set of build settings. This can be removed for applications 16 | # that need different build settings. 17 | apply_standard_settings(${BINARY_NAME}) 18 | 19 | # Add preprocessor definitions for the application ID. 20 | add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") 21 | 22 | # Add dependency libraries. Add any application-specific dependencies here. 23 | target_link_libraries(${BINARY_NAME} PRIVATE flutter) 24 | target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) 25 | 26 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 27 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/chat/load_chat_room_messages_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | import '../../entities/chat/chat_message_entity.dart'; 4 | import '../../repositories/chat_repository.dart'; 5 | import '../../../core/error/failures.dart'; 6 | 7 | class LoadChatRoomMessagesUseCase { 8 | final ChatRepository repository; 9 | 10 | LoadChatRoomMessagesUseCase(this.repository); 11 | 12 | Future>> call(LoadChatRoomMessagesParams params) async { 13 | return await repository.getChatRoomMessages( 14 | params.chatRoomId, 15 | pageNumber: params.pageNumber, 16 | pageSize: params.pageSize, 17 | ); 18 | } 19 | } 20 | 21 | class LoadChatRoomMessagesParams extends Equatable { 22 | final String chatRoomId; 23 | final int pageNumber; 24 | final int pageSize; 25 | 26 | const LoadChatRoomMessagesParams({ 27 | required this.chatRoomId, 28 | this.pageNumber = 1, 29 | this.pageSize = 50, 30 | }); 31 | 32 | @override 33 | List get props => [chatRoomId, pageNumber, pageSize]; 34 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/data/models/delivery/shipping_item_model.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'shipping_item_model.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | ShippingItemModel _$ShippingItemModelFromJson(Map json) => 10 | ShippingItemModel( 11 | name: json['name'] as String, 12 | quantity: (json['quantity'] as num).toInt(), 13 | weight: (json['weight'] as num?)?.toDouble(), 14 | length: (json['length'] as num?)?.toDouble(), 15 | width: (json['width'] as num?)?.toDouble(), 16 | height: (json['height'] as num?)?.toDouble(), 17 | ); 18 | 19 | Map _$ShippingItemModelToJson(ShippingItemModel instance) => 20 | { 21 | 'name': instance.name, 22 | 'quantity': instance.quantity, 23 | 'weight': instance.weight, 24 | 'length': instance.length, 25 | 'width': instance.width, 26 | 'height': instance.height, 27 | }; 28 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/chat/create_chat_room_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | import '../../entities/chat/chat_room_entity.dart'; 4 | import '../../repositories/chat_repository.dart'; 5 | import '../../../core/error/failures.dart'; 6 | 7 | class CreateChatRoomUseCase { 8 | final ChatRepository repository; 9 | 10 | CreateChatRoomUseCase(this.repository); 11 | 12 | Future> call(CreateChatRoomParams params) async { 13 | return await repository.createChatRoom( 14 | shopId: params.shopId, 15 | relatedOrderId: params.relatedOrderId, 16 | initialMessage: params.initialMessage, 17 | ); 18 | } 19 | } 20 | 21 | class CreateChatRoomParams extends Equatable { 22 | final String shopId; 23 | final String? relatedOrderId; 24 | final String initialMessage; 25 | 26 | const CreateChatRoomParams({ 27 | required this.shopId, 28 | this.relatedOrderId, 29 | required this.initialMessage, 30 | }); 31 | 32 | @override 33 | List get props => [shopId, relatedOrderId, initialMessage]; 34 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Huỳnh Thiện Nhân 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/data/models/payment/payment_model.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'payment_model.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | GeneratePaymentQrRequestModel _$GeneratePaymentQrRequestModelFromJson( 10 | Map json) => 11 | GeneratePaymentQrRequestModel( 12 | orderIds: 13 | (json['orderIds'] as List).map((e) => e as String).toList(), 14 | ); 15 | 16 | Map _$GeneratePaymentQrRequestModelToJson( 17 | GeneratePaymentQrRequestModel instance) => 18 | { 19 | 'orderIds': instance.orderIds, 20 | }; 21 | 22 | PaymentQrModel _$PaymentQrModelFromJson(Map json) => 23 | PaymentQrModel( 24 | qrImageUrl: json['qrImageUrl'] as String, 25 | ); 26 | 27 | Map _$PaymentQrModelToJson(PaymentQrModel instance) => 28 | { 29 | 'qrImageUrl': instance.qrImageUrl, 30 | }; 31 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/repositories/home_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../entities/category/category_entity.dart'; 3 | import '../entities/products/product_entity.dart'; 4 | import '../entities/products/product_detail_entity.dart'; 5 | import '../entities/products/product_image_entity.dart'; 6 | import '../../core/error/failures.dart'; 7 | 8 | abstract class HomeRepository { 9 | Future>> getCategories(); 10 | Future> getCategoryDetail(String categoryId); 11 | Future>> getProductsByCategory(String categoryId); 12 | Future>> getProducts({int page = 1, int limit = 20}); 13 | Future>> searchProducts({required String query, int page = 1, int limit = 20}); 14 | Future> getProductDetail(String productId); 15 | Future>> getProductImages(String productId); 16 | Future>> getProductPrimaryImages(List productIds); 17 | } 18 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/presentation/blocs/chatbot/chat_bot_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import '../../../domain/entities/chatbot/chat_bot_entity.dart'; 3 | 4 | abstract class ChatBotEvent extends Equatable { 5 | const ChatBotEvent(); 6 | 7 | @override 8 | List get props => []; 9 | } 10 | 11 | class LoadChatBotHistory extends ChatBotEvent { 12 | final bool isRefresh; 13 | 14 | const LoadChatBotHistory({this.isRefresh = false}); 15 | 16 | @override 17 | List get props => [isRefresh]; 18 | } 19 | 20 | class RefreshChatBotHistory extends ChatBotEvent { 21 | const RefreshChatBotHistory(); 22 | } 23 | 24 | class SendChatBotMessage extends ChatBotEvent { 25 | final String message; 26 | 27 | const SendChatBotMessage({required this.message}); 28 | 29 | @override 30 | List get props => [message]; 31 | } 32 | 33 | class ClearChatBot extends ChatBotEvent { 34 | const ClearChatBot(); 35 | } 36 | 37 | class ShowChatBotItem extends ChatBotEvent { 38 | final ChatBotHistoryItem item; 39 | 40 | const ShowChatBotItem(this.item); 41 | 42 | @override 43 | List get props => [item]; 44 | } 45 | 46 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/data/models/delivery/preview_deliveries_model.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'preview_deliveries_model.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | PreviewDeliveriesModel _$PreviewDeliveriesModelFromJson( 10 | Map json) => 11 | PreviewDeliveriesModel( 12 | fromShops: (json['fromShops'] as List) 13 | .map((e) => FromShopModel.fromJson(e as Map)) 14 | .toList(), 15 | toProvince: json['toProvince'] as String, 16 | toDistrict: json['toDistrict'] as String, 17 | toWard: json['toWard'] as String, 18 | ); 19 | 20 | Map _$PreviewDeliveriesModelToJson( 21 | PreviewDeliveriesModel instance) => 22 | { 23 | 'fromShops': instance.fromShops.map((e) => e.toJson()).toList(), 24 | 'toProvince': instance.toProvince, 25 | 'toDistrict': instance.toDistrict, 26 | 'toWard': instance.toWard, 27 | }; 28 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/presentation/blocs/cart_live/preview_order_live_state.dart: -------------------------------------------------------------------------------- 1 | part of 'preview_order_live_bloc.dart'; 2 | 3 | abstract class PreviewOrderLiveState extends Equatable { 4 | const PreviewOrderLiveState(); 5 | @override 6 | List get props => []; 7 | } 8 | 9 | class PreviewOrderLiveInitial extends PreviewOrderLiveState {} 10 | 11 | class PreviewOrderLiveLoading extends PreviewOrderLiveState {} 12 | 13 | class PreviewOrderLiveRefreshing extends PreviewOrderLiveState { 14 | final PreviewOrderLiveEntity? previous; 15 | const PreviewOrderLiveRefreshing(this.previous); 16 | @override 17 | List get props => [previous]; 18 | } 19 | 20 | class PreviewOrderLiveLoaded extends PreviewOrderLiveState { 21 | final PreviewOrderLiveEntity data; 22 | const PreviewOrderLiveLoaded(this.data); 23 | @override 24 | List get props => [data]; 25 | } 26 | 27 | class PreviewOrderLiveEmpty extends PreviewOrderLiveState {} 28 | 29 | class PreviewOrderLiveError extends PreviewOrderLiveState { 30 | final String message; 31 | const PreviewOrderLiveError(this.message); 32 | @override 33 | List get props => [message]; 34 | } 35 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/presentation/blocs/search/search_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | abstract class SearchEvent extends Equatable { 4 | const SearchEvent(); 5 | 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class SearchQueryChangedEvent extends SearchEvent { 11 | final String query; 12 | 13 | const SearchQueryChangedEvent(this.query); 14 | 15 | @override 16 | List get props => [query]; 17 | } 18 | 19 | class SearchSubmittedEvent extends SearchEvent { 20 | final String query; 21 | 22 | const SearchSubmittedEvent(this.query); 23 | 24 | @override 25 | List get props => [query]; 26 | } 27 | 28 | class ClearSearchEvent extends SearchEvent { 29 | const ClearSearchEvent(); 30 | } 31 | 32 | class SearchHistoryLoadedEvent extends SearchEvent { 33 | const SearchHistoryLoadedEvent(); 34 | } 35 | 36 | class SearchHistoryItemSelectedEvent extends SearchEvent { 37 | final String query; 38 | 39 | const SearchHistoryItemSelectedEvent(this.query); 40 | 41 | @override 42 | List get props => [query]; 43 | } 44 | 45 | class ClearSearchHistoryEvent extends SearchEvent { 46 | const ClearSearchHistoryEvent(); 47 | } 48 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/entities/payment/payment_entity.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | class GeneratePaymentQrRequestEntity extends Equatable { 4 | final List orderIds; 5 | 6 | const GeneratePaymentQrRequestEntity({required this.orderIds}); 7 | 8 | GeneratePaymentQrRequestEntity copyWith({List? orderIds}) => 9 | GeneratePaymentQrRequestEntity(orderIds: orderIds ?? this.orderIds); 10 | 11 | Map toJson() => { 12 | 'orderIds': orderIds, 13 | }; 14 | 15 | @override 16 | List get props => [orderIds]; 17 | 18 | @override 19 | String toString() => 'GeneratePaymentQrRequestEntity(orderIds: $orderIds)'; 20 | } 21 | 22 | class PaymentQrEntity extends Equatable { 23 | final String qrImageUrl; 24 | 25 | const PaymentQrEntity({required this.qrImageUrl}); 26 | 27 | bool get isValidUrl => Uri.tryParse(qrImageUrl)?.hasAbsolutePath == true; 28 | 29 | PaymentQrEntity copyWith({String? qrImageUrl}) => 30 | PaymentQrEntity(qrImageUrl: qrImageUrl ?? this.qrImageUrl); 31 | 32 | @override 33 | List get props => [qrImageUrl]; 34 | 35 | @override 36 | String toString() => 'PaymentQrEntity(qrImageUrl: $qrImageUrl)'; 37 | } 38 | 39 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/chat/send_message_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | import '../../entities/chat/chat_message_entity.dart'; 4 | import '../../repositories/chat_repository.dart'; 5 | import '../../../core/error/failures.dart'; 6 | 7 | class SendMessageUseCase { 8 | final ChatRepository repository; 9 | 10 | SendMessageUseCase(this.repository); 11 | 12 | Future> call(SendMessageParams params) async { 13 | return await repository.sendMessage( 14 | chatRoomId: params.chatRoomId, 15 | content: params.content, 16 | messageType: params.messageType, 17 | attachmentUrl: params.attachmentUrl, 18 | ); 19 | } 20 | } 21 | 22 | class SendMessageParams extends Equatable { 23 | final String chatRoomId; 24 | final String content; 25 | final String messageType; 26 | final String? attachmentUrl; 27 | 28 | const SendMessageParams({ 29 | required this.chatRoomId, 30 | required this.content, 31 | this.messageType = 'Text', 32 | this.attachmentUrl, 33 | }); 34 | 35 | @override 36 | List get props => [chatRoomId, content, messageType, attachmentUrl]; 37 | } -------------------------------------------------------------------------------- /stream_cart_mobile/macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /stream_cart_mobile/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility in the flutter_test package. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:stream_cart_mobile/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(const MyApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/presentation/blocs/category_detail/category_detail_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import '../../../domain/entities/category/category_entity.dart'; 3 | import '../../../domain/entities/products/product_entity.dart'; 4 | 5 | abstract class CategoryDetailState extends Equatable { 6 | const CategoryDetailState(); 7 | 8 | @override 9 | List get props => []; 10 | } 11 | 12 | class CategoryDetailInitial extends CategoryDetailState {} 13 | 14 | class CategoryDetailLoading extends CategoryDetailState {} 15 | 16 | class CategoryDetailLoaded extends CategoryDetailState { 17 | final CategoryEntity category; 18 | final List products; 19 | final Map productImages; 20 | 21 | const CategoryDetailLoaded({ 22 | required this.category, 23 | required this.products, 24 | this.productImages = const {}, 25 | }); 26 | 27 | @override 28 | List get props => [category, products, productImages]; 29 | } 30 | 31 | class CategoryDetailError extends CategoryDetailState { 32 | final String message; 33 | 34 | const CategoryDetailError(this.message); 35 | 36 | @override 37 | List get props => [message]; 38 | } 39 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/data/models/delivery/service_response_model.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'service_response_model.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | ServiceResponseModel _$ServiceResponseModelFromJson( 10 | Map json) => 11 | ServiceResponseModel( 12 | shopId: json['shopId'] as String, 13 | serviceTypeId: (json['serviceTypeId'] as num).toInt(), 14 | serviceName: json['serviceName'] as String, 15 | totalAmount: (json['totalAmount'] as num).toDouble(), 16 | expectedDeliveryDate: 17 | DateTime.parse(json['expectedDeliveryDate'] as String), 18 | ); 19 | 20 | Map _$ServiceResponseModelToJson( 21 | ServiceResponseModel instance) => 22 | { 23 | 'shopId': instance.shopId, 24 | 'serviceTypeId': instance.serviceTypeId, 25 | 'serviceName': instance.serviceName, 26 | 'totalAmount': instance.totalAmount, 27 | 'expectedDeliveryDate': instance.expectedDeliveryDate.toIso8601String(), 28 | }; 29 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/shop_voucher/get_shop_vouchers_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../entities/shop_voucher/shop_voucher_entity.dart'; 3 | import '../../repositories/shop_voucher/shop_voucher_repository.dart'; 4 | import '../../../core/error/failures.dart'; 5 | 6 | class GetShopVouchersParams { 7 | final String shopId; 8 | final bool? isActive; 9 | final int? type; 10 | final bool? isExpired; 11 | final int pageNumber; 12 | final int pageSize; 13 | 14 | const GetShopVouchersParams({ 15 | required this.shopId, 16 | this.isActive, 17 | this.type, 18 | this.isExpired, 19 | this.pageNumber = 1, 20 | this.pageSize = 10, 21 | }); 22 | } 23 | 24 | class GetShopVouchersUseCase { 25 | final ShopVoucherRepository repository; 26 | 27 | GetShopVouchersUseCase(this.repository); 28 | 29 | Future> call(GetShopVouchersParams params) { 30 | return repository.getVouchers( 31 | shopId: params.shopId, 32 | isActive: params.isActive, 33 | type: params.type, 34 | isExpired: params.isExpired, 35 | pageNumber: params.pageNumber, 36 | pageSize: params.pageSize, 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/entities/deliveries/preview_deliveries_entity.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'from_shop_entity.dart'; 3 | 4 | class PreviewDeliveriesEntity extends Equatable { 5 | final List fromShops; 6 | final String toProvince; 7 | final String toDistrict; 8 | final String toWard; 9 | 10 | const PreviewDeliveriesEntity({ 11 | required this.fromShops, 12 | required this.toProvince, 13 | required this.toDistrict, 14 | required this.toWard, 15 | }); 16 | 17 | PreviewDeliveriesEntity copyWith({ 18 | List? fromShops, 19 | String? toProvince, 20 | String? toDistrict, 21 | String? toWard, 22 | }) { 23 | return PreviewDeliveriesEntity( 24 | fromShops: fromShops ?? this.fromShops, 25 | toProvince: toProvince ?? this.toProvince, 26 | toDistrict: toDistrict ?? this.toDistrict, 27 | toWard: toWard ?? this.toWard, 28 | ); 29 | } 30 | 31 | @override 32 | List get props => [fromShops, toProvince, toDistrict, toWard]; 33 | 34 | @override 35 | String toString() { 36 | return 'PreviewDeliveriesEntity(fromShops: $fromShops, toProvince: $toProvince, toDistrict: $toDistrict, toWard: $toWard)'; 37 | } 38 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/repositories/review/review_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/review/review_entity.dart'; 4 | 5 | abstract class ReviewRepository { 6 | Future> createReview( 7 | ReviewRequestEntity request, 8 | ); 9 | Future> getReviewById(String reviewId); 10 | Future> updateReview( 11 | String reviewId, { 12 | int? rating, 13 | String? reviewText, 14 | List? imageUrls, 15 | }); 16 | Future> deleteReview(String reviewId); 17 | Future>> getReviewsByOrderId( 18 | String orderId, 19 | ); 20 | Future>> getReviewsByUserId( 21 | String userId, 22 | ); 23 | Future>> getReviewsByLivestreamId( 24 | String livestreamId, 25 | ); 26 | Future>> getReviewsByProductId( 27 | String productId, { 28 | int pageNumber = 1, 29 | int pageSize = 10, 30 | int? minRating, 31 | int? maxRating, 32 | bool? verifiedOnly, 33 | String? sortBy, 34 | bool ascending = false, 35 | }); 36 | } 37 | 38 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/chat/search_chat_room_messages_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | import '../../entities/chat/chat_message_entity.dart'; 4 | import '../../repositories/chat_repository.dart'; 5 | import '../../../core/error/failures.dart'; 6 | 7 | class SearchChatRoomMessagesUseCase { 8 | final ChatRepository repository; 9 | 10 | SearchChatRoomMessagesUseCase(this.repository); 11 | 12 | Future>> call(SearchChatRoomMessagesParams params) async { 13 | return await repository.searchChatRoomMessages( 14 | params.chatRoomId, 15 | searchTerm: params.searchTerm, 16 | pageNumber: params.pageNumber, 17 | pageSize: params.pageSize, 18 | ); 19 | } 20 | } 21 | 22 | class SearchChatRoomMessagesParams extends Equatable { 23 | final String chatRoomId; 24 | final String searchTerm; 25 | final int pageNumber; 26 | final int pageSize; 27 | 28 | const SearchChatRoomMessagesParams({ 29 | required this.chatRoomId, 30 | required this.searchTerm, 31 | this.pageNumber = 1, 32 | this.pageSize = 20, 33 | }); 34 | 35 | @override 36 | List get props => [chatRoomId, searchTerm, pageNumber, pageSize]; 37 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/repositories/cart_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../entities/cart/cart_entity.dart'; 3 | import '../../core/error/failures.dart'; 4 | 5 | abstract class CartRepository { 6 | Future> addToCart( 7 | String productId, 8 | String? variantId, 9 | int quantity, 10 | ); 11 | 12 | Future> getCartItems(); 13 | 14 | Future> updateCartItem( 15 | String cartItemId, 16 | int quantity, 17 | ); 18 | 19 | Future> removeCartItem(String cartItemId); 20 | 21 | Future> removeMultipleCartItems(List cartItemIds); 22 | 23 | Future> clearCart(); 24 | 25 | Future> getPreviewOrder(List cartItemIds); 26 | 27 | @Deprecated('Use getCartItems() instead') 28 | Future> getCartSummary(); 29 | 30 | @Deprecated('Use removeCartItem() instead') 31 | Future> removeFromCart( 32 | String productId, 33 | String? variantId, 34 | ); 35 | 36 | @Deprecated('Use getPreviewOrder() instead') 37 | Future> getCartPreview(); 38 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/presentation/blocs/product_detail/product_detail_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | abstract class ProductDetailEvent extends Equatable { 4 | const ProductDetailEvent(); 5 | 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class LoadProductDetailEvent extends ProductDetailEvent { 11 | final String productId; 12 | 13 | const LoadProductDetailEvent(this.productId); 14 | 15 | @override 16 | List get props => [productId]; 17 | } 18 | 19 | class AddToCartEvent extends ProductDetailEvent { 20 | final String productId; 21 | final String? variantId; 22 | final int quantity; 23 | 24 | const AddToCartEvent({ 25 | required this.productId, 26 | this.variantId, 27 | this.quantity = 1, 28 | }); 29 | 30 | @override 31 | List get props => [productId, variantId ?? '', quantity]; 32 | } 33 | 34 | class SelectVariantEvent extends ProductDetailEvent { 35 | final String variantId; 36 | 37 | const SelectVariantEvent(this.variantId); 38 | 39 | @override 40 | List get props => [variantId]; 41 | } 42 | 43 | class LoadProductImagesEvent extends ProductDetailEvent { 44 | final String productId; 45 | 46 | const LoadProductImagesEvent(this.productId); 47 | 48 | @override 49 | List get props => [productId]; 50 | } 51 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/presentation/widgets/review/infinite_scroll_listener.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class InfiniteScrollListener extends StatefulWidget { 4 | final Widget child; 5 | final VoidCallback onEndReached; 6 | const InfiniteScrollListener({super.key, required this.child, required this.onEndReached}); 7 | 8 | @override 9 | State createState() => _InfiniteScrollListenerState(); 10 | } 11 | 12 | class _InfiniteScrollListenerState extends State { 13 | final _controller = ScrollController(); 14 | 15 | @override 16 | void initState() { 17 | super.initState(); 18 | _controller.addListener(_onScroll); 19 | } 20 | 21 | void _onScroll() { 22 | if (!_controller.hasClients) return; 23 | final max = _controller.position.maxScrollExtent; 24 | final current = _controller.position.pixels; 25 | if (current >= max - 200) { 26 | widget.onEndReached(); 27 | } 28 | } 29 | 30 | @override 31 | void dispose() { 32 | _controller.removeListener(_onScroll); 33 | _controller.dispose(); 34 | super.dispose(); 35 | } 36 | 37 | @override 38 | Widget build(BuildContext context) { 39 | return PrimaryScrollController( 40 | controller: _controller, 41 | child: widget.child, 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/presentation/blocs/payment/payment_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import '../../../domain/entities/payment/payment_entity.dart'; 3 | 4 | abstract class PaymentState extends Equatable { 5 | const PaymentState(); 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class PaymentInitial extends PaymentState { 11 | const PaymentInitial(); 12 | } 13 | 14 | class PaymentLoading extends PaymentState { 15 | const PaymentLoading(); 16 | } 17 | 18 | class PaymentGenerating extends PaymentState { 19 | final GeneratePaymentQrRequestEntity request; 20 | const PaymentGenerating({required this.request}); 21 | 22 | @override 23 | List get props => [request]; 24 | } 25 | 26 | class PaymentQrLoaded extends PaymentState { 27 | final PaymentQrEntity qr; 28 | const PaymentQrLoaded({required this.qr}); 29 | 30 | @override 31 | List get props => [qr]; 32 | } 33 | 34 | class PaymentError extends PaymentState { 35 | final String message; 36 | const PaymentError({required this.message}); 37 | 38 | @override 39 | List get props => [message]; 40 | } 41 | 42 | class PaymentOperationSuccess extends PaymentState { 43 | final String message; 44 | const PaymentOperationSuccess({required this.message}); 45 | 46 | @override 47 | List get props => [message]; 48 | } 49 | 50 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/presentation/blocs/profile/profile_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import '../../../domain/entities/account/user_profile_entity.dart'; 3 | 4 | abstract class ProfileState extends Equatable { 5 | const ProfileState(); 6 | 7 | @override 8 | List get props => []; 9 | } 10 | 11 | class ProfileInitial extends ProfileState {} 12 | 13 | class ProfileLoading extends ProfileState {} 14 | 15 | class ProfileLoaded extends ProfileState { 16 | final UserProfileEntity profile; 17 | 18 | const ProfileLoaded(this.profile); 19 | 20 | @override 21 | List get props => [profile]; 22 | } 23 | 24 | class ProfileError extends ProfileState { 25 | final String message; 26 | 27 | const ProfileError(this.message); 28 | 29 | @override 30 | List get props => [message]; 31 | } 32 | 33 | class ProfileUpdateLoading extends ProfileState {} 34 | 35 | class ProfileUpdateSuccess extends ProfileState { 36 | final UserProfileEntity updatedProfile; 37 | 38 | const ProfileUpdateSuccess(this.updatedProfile); 39 | 40 | @override 41 | List get props => [updatedProfile]; 42 | } 43 | 44 | class ProfileUpdateError extends ProfileState { 45 | final String message; 46 | 47 | const ProfileUpdateError(this.message); 48 | 49 | @override 50 | List get props => [message]; 51 | } 52 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/entities/products/product_attribute_entity.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | class ProductAttributeEntity extends Equatable { 4 | final String id; 5 | final String name; 6 | final DateTime createdAt; 7 | final String createdBy; 8 | final DateTime? lastModifiedAt; 9 | final String? lastModifiedBy; 10 | 11 | const ProductAttributeEntity({ 12 | required this.id, 13 | required this.name, 14 | required this.createdAt, 15 | required this.createdBy, 16 | this.lastModifiedAt, 17 | this.lastModifiedBy, 18 | }); 19 | 20 | ProductAttributeEntity copyWith({ 21 | String? id, 22 | String? name, 23 | DateTime? createdAt, 24 | String? createdBy, 25 | DateTime? lastModifiedAt, 26 | String? lastModifiedBy, 27 | }) { 28 | return ProductAttributeEntity( 29 | id: id ?? this.id, 30 | name: name ?? this.name, 31 | createdAt: createdAt ?? this.createdAt, 32 | createdBy: createdBy ?? this.createdBy, 33 | lastModifiedAt: lastModifiedAt ?? this.lastModifiedAt, 34 | lastModifiedBy: lastModifiedBy ?? this.lastModifiedBy, 35 | ); 36 | } 37 | 38 | @override 39 | List get props => [ 40 | id, 41 | name, 42 | createdAt, 43 | createdBy, 44 | lastModifiedAt, 45 | lastModifiedBy, 46 | ]; 47 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/data/models/cart_live/preview_order_live_model.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'preview_order_live_model.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | PreviewOrderLiveModel _$PreviewOrderLiveModelFromJson( 10 | Map json) => 11 | PreviewOrderLiveModel( 12 | livestreamId: json['livestreamId'] as String, 13 | totalItem: (json['totalItem'] as num).toInt(), 14 | subTotal: (json['subTotal'] as num).toDouble(), 15 | discount: (json['discount'] as num).toDouble(), 16 | totalAmount: (json['totalAmount'] as num).toDouble(), 17 | listCartItem: PreviewOrderLiveModel._listCartItemFromJson( 18 | json['listCartItem'] as List), 19 | ); 20 | 21 | Map _$PreviewOrderLiveModelToJson( 22 | PreviewOrderLiveModel instance) => 23 | { 24 | 'livestreamId': instance.livestreamId, 25 | 'totalItem': instance.totalItem, 26 | 'subTotal': instance.subTotal, 27 | 'discount': instance.discount, 28 | 'totalAmount': instance.totalAmount, 29 | 'listCartItem': 30 | PreviewOrderLiveModel._listCartItemToJson(instance.listCartItem), 31 | }; 32 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/repositories/shop_voucher/shop_voucher_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import 'package:stream_cart_mobile/core/error/failures.dart'; 3 | 4 | import '../../entities/shop_voucher/shop_voucher_entity.dart'; 5 | import '../../entities/shop_voucher/available_shop_voucher_entity.dart'; 6 | 7 | abstract class ShopVoucherRepository { 8 | /// - [shopId]: Required path parameter of the shop. 9 | /// - [isActive]: Filter by active status. 10 | /// - [type]: 1 = percentage, 2 = fixed amount. 11 | /// - [isExpired]: Filter expired state. 12 | /// - [pageNumber], [pageSize]: Pagination (defaults: 1, 10). 13 | Future> getVouchers({ 14 | required String shopId, 15 | bool? isActive, 16 | int? type, 17 | bool? isExpired, 18 | int pageNumber = 1, 19 | int pageSize = 10, 20 | }); 21 | 22 | /// - [code]: Voucher code (path parameter). 23 | /// - [request]: Body contains code, orderAmount, orderId. 24 | Future> applyVoucher({ 25 | required String code, 26 | required ApplyShopVoucherRequestEntity request, 27 | }); 28 | 29 | Future> getAvailableVouchers({ 30 | required double orderAmount, 31 | String? shopId, 32 | bool sortByDiscountDesc = true, 33 | }); 34 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/entities/deliveries/shipping_item_entity.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | class ShippingItemEntity extends Equatable { 4 | final String name; 5 | final int quantity; 6 | final double? weight; 7 | final double? length; 8 | final double? width; 9 | final double? height; 10 | 11 | const ShippingItemEntity({ 12 | required this.name, 13 | required this.quantity, 14 | this.weight, 15 | this.length, 16 | this.width, 17 | this.height, 18 | }); 19 | 20 | ShippingItemEntity copyWith({ 21 | String? name, 22 | int? quantity, 23 | double? weight, 24 | double? length, 25 | double? width, 26 | double? height, 27 | }) { 28 | return ShippingItemEntity( 29 | name: name ?? this.name, 30 | quantity: quantity ?? this.quantity, 31 | weight: weight ?? this.weight, 32 | length: length ?? this.length, 33 | width: width ?? this.width, 34 | height: height ?? this.height, 35 | ); 36 | } 37 | 38 | @override 39 | List get props => [ 40 | name, 41 | quantity, 42 | weight, 43 | length, 44 | width, 45 | height, 46 | ]; 47 | 48 | @override 49 | String toString() { 50 | return 'ShippingItemEntity(name: $name, quantity: $quantity, weight: $weight, length: $length, width: $width, height: $height)'; 51 | } 52 | } -------------------------------------------------------------------------------- /stream_cart_mobile/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | stream_cart_mobile 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /stream_cart_mobile/android/app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "531713282766", 4 | "project_id": "stream-cart-mobile", 5 | "storage_bucket": "stream-cart-mobile.firebasestorage.app" 6 | }, 7 | "client": [ 8 | { 9 | "client_info": { 10 | "mobilesdk_app_id": "1:531713282766:android:de3209a1702c41a761f69f", 11 | "android_client_info": { 12 | "package_name": "com.example.stream_cart_mobile" 13 | } 14 | }, 15 | "oauth_client": [], 16 | "api_key": [ 17 | { 18 | "current_key": "AIzaSyASRcQu4nsDNyASv8Z6CLFrim-_T3Q-66I" 19 | } 20 | ], 21 | "services": { 22 | "appinvite_service": { 23 | "other_platform_oauth_client": [] 24 | } 25 | } 26 | }, 27 | { 28 | "client_info": { 29 | "mobilesdk_app_id": "1:531713282766:android:6ae8ce852a81ff2f61f69f", 30 | "android_client_info": { 31 | "package_name": "com.yourcompany.streamcart" 32 | } 33 | }, 34 | "oauth_client": [], 35 | "api_key": [ 36 | { 37 | "current_key": "AIzaSyASRcQu4nsDNyASv8Z6CLFrim-_T3Q-66I" 38 | } 39 | ], 40 | "services": { 41 | "appinvite_service": { 42 | "other_platform_oauth_client": [] 43 | } 44 | } 45 | } 46 | ], 47 | "configuration_version": "1" 48 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/entities/auth/otp_entities.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | class VerifyOtpRequestEntity extends Equatable { 4 | final String accountId; // Changed from email to accountId 5 | final String otp; 6 | 7 | const VerifyOtpRequestEntity({ 8 | required this.accountId, 9 | required this.otp, 10 | }); 11 | 12 | @override 13 | List get props => [accountId, otp]; 14 | } 15 | 16 | class VerifyOtpResponseEntity extends Equatable { 17 | final bool success; 18 | final String message; 19 | final List? errors; 20 | 21 | const VerifyOtpResponseEntity({ 22 | required this.success, 23 | required this.message, 24 | this.errors, 25 | }); 26 | 27 | @override 28 | List get props => [success, message, errors]; 29 | } 30 | 31 | class ResendOtpRequestEntity extends Equatable { 32 | final String email; 33 | 34 | const ResendOtpRequestEntity({ 35 | required this.email, 36 | }); 37 | 38 | @override 39 | List get props => [email]; 40 | } 41 | 42 | class ResendOtpResponseEntity extends Equatable { 43 | final bool success; 44 | final String message; 45 | final List? errors; 46 | 47 | const ResendOtpResponseEntity({ 48 | required this.success, 49 | required this.message, 50 | this.errors, 51 | }); 52 | 53 | @override 54 | List get props => [success, message, errors]; 55 | } 56 | -------------------------------------------------------------------------------- /stream_cart_mobile/lib/domain/usecases/product_variants/get_cheapest_variant.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../core/error/failures.dart'; 3 | import '../../entities/products/product_variants_entity.dart'; 4 | import '../../repositories/product/product_variants_repository.dart'; 5 | 6 | class GetCheapestVariant { 7 | final ProductVariantsRepository repository; 8 | 9 | GetCheapestVariant(this.repository); 10 | 11 | Future> call(String productId) async { 12 | final result = await repository.getProductVariantsByProductId(productId); 13 | 14 | return result.fold( 15 | (failure) => Left(failure), 16 | (variants) { 17 | if (variants.isEmpty) { 18 | return const Right(null); 19 | } 20 | 21 | // Tìm variant có giá thấp nhất (ưu tiên flash sale price nếu có) 22 | ProductVariantEntity cheapest = variants.first; 23 | for (final variant in variants) { 24 | final currentPrice = variant.flashSalePrice > 0 ? variant.flashSalePrice : variant.price; 25 | final cheapestPrice = cheapest.flashSalePrice > 0 ? cheapest.flashSalePrice : cheapest.price; 26 | 27 | if (currentPrice < cheapestPrice && variant.stock > 0) { 28 | cheapest = variant; 29 | } 30 | } 31 | 32 | return Right(cheapest); 33 | }, 34 | ); 35 | } 36 | } -------------------------------------------------------------------------------- /stream_cart_mobile/lib/core/enums/address_type.dart: -------------------------------------------------------------------------------- 1 | enum AddressType { 2 | residential(0, 'Nhà riêng'), 3 | business(1, 'Cơ sở kinh doanh'), 4 | shipping(2, 'Địa chỉ giao hàng'), 5 | billing(3, 'Địa chỉ thanh toán'), 6 | both(4, 'Vừa giao hàng vừa thanh toán'); 7 | 8 | const AddressType(this.value, this.displayName); 9 | 10 | final int value; 11 | final String displayName; 12 | 13 | static AddressType fromValue(int value) { 14 | switch (value) { 15 | case 0: 16 | return AddressType.residential; 17 | case 1: 18 | return AddressType.business; 19 | case 2: 20 | return AddressType.shipping; 21 | case 3: 22 | return AddressType.billing; 23 | case 4: 24 | return AddressType.both; 25 | default: 26 | return AddressType.residential; 27 | } 28 | } 29 | 30 | static AddressType? fromName(String name) { 31 | switch (name.toLowerCase()) { 32 | case 'residential': 33 | return AddressType.residential; 34 | case 'business': 35 | return AddressType.business; 36 | case 'shipping': 37 | return AddressType.shipping; 38 | case 'billing': 39 | return AddressType.billing; 40 | case 'both': 41 | return AddressType.both; 42 | default: 43 | return null; 44 | } 45 | } 46 | 47 | String toJson() => name; 48 | 49 | @override 50 | String toString() => displayName; 51 | } --------------------------------------------------------------------------------