diff --git a/assets/icons/empty_home.svg b/assets/icons/empty_home.svg
new file mode 100644
index 0000000..3aa4aa6
--- /dev/null
+++ b/assets/icons/empty_home.svg
@@ -0,0 +1,307 @@
+
diff --git a/assets/icons/ri_search-2-line.svg b/assets/icons/ri_search-2-line.svg
new file mode 100644
index 0000000..1b1471e
--- /dev/null
+++ b/assets/icons/ri_search-2-line.svg
@@ -0,0 +1,3 @@
+
diff --git a/lib/core/config/api_config.dart b/lib/core/config/api_config.dart
index 775a665..13b2fc7 100644
--- a/lib/core/config/api_config.dart
+++ b/lib/core/config/api_config.dart
@@ -1,3 +1,5 @@
+// lib/core/config/api_config.dart
+
class ApiConfig {
// Private constructor to prevent instantiation
ApiConfig._();
@@ -35,4 +37,16 @@ class ApiConfig {
/// Headers: {'Authorization': 'Bearer '}
static const String addDiscount = '$baseUrl/discount/add';
static const String getDiscounts = '$baseUrl/discount/get';
-}
+ static const String getActiveDiscounts = '$baseUrl/discount/get?status=1';
+
+ /// Endpoint to get a single discount by its ID.
+ /// Method: GET
+ /// Headers: {'Authorization': 'Bearer '}
+ static String getDiscountById(String id) => '$baseUrl/discount/get/$id';
+
+ /// Endpoint to edit an existing discount.
+ /// Method: POST
+ /// Body: FormData
+ /// Headers: {'Authorization': 'Bearer '}
+ static String editDiscount(String id) => '$baseUrl/discount/edit/$id';
+}
\ No newline at end of file
diff --git a/lib/domain/entities/discount_entity.dart b/lib/domain/entities/discount_entity.dart
index 621331c..42ef9db 100644
--- a/lib/domain/entities/discount_entity.dart
+++ b/lib/domain/entities/discount_entity.dart
@@ -1,3 +1,5 @@
+// lib/domain/entities/discount_entity.dart
+
class DiscountEntity {
final String id;
final String name;
@@ -29,19 +31,23 @@ class DiscountEntity {
}
return [];
}
-
+
return DiscountEntity(
- id: json['_id'] ?? '',
+ // --- FIX IS HERE: Reading "ID" instead of "_id" ---
+ id: json['ID'] ?? '', // Changed from '_id' to 'ID'
name: json['Name'] ?? 'بدون نام',
// Safely access nested properties
- shopName: (json['Shop'] != null ? json['Shop']['Name'] : 'فروشگاه') ?? 'فروشگاه',
+ shopName:
+ (json['Shop'] != null ? json['Shop']['Name'] : 'فروشگاه') ?? 'فروشگاه',
images: _parseImages(json['Images']),
- type: (json['Type'] != null ? json['Type']['Description'] : 'عمومی') ?? 'عمومی',
+ type: (json['Type'] != null ? json['Type']['Description'] : 'عمومی') ??
+ 'عمومی',
description: json['Description'] ?? '',
price: (json['Price'] as num? ?? 0).toDouble(),
nPrice: (json['NPrice'] as num? ?? 0).toDouble(),
// Handle potential null or invalid date
- endDate: json['EndDate'] != null ? DateTime.tryParse(json['EndDate']) : null,
+ endDate:
+ json['EndDate'] != null ? DateTime.tryParse(json['EndDate']) : null,
);
}
}
\ No newline at end of file
diff --git a/lib/gen/assets.gen.dart b/lib/gen/assets.gen.dart
index 90d88bc..0c3962e 100644
--- a/lib/gen/assets.gen.dart
+++ b/lib/gen/assets.gen.dart
@@ -129,6 +129,9 @@ class $AssetsIconsGen {
/// File path: assets/icons/edit.svg
String get edit => 'assets/icons/edit.svg';
+ /// File path: assets/icons/empty_home.svg
+ String get emptyHome => 'assets/icons/empty_home.svg';
+
/// File path: assets/icons/error.svg
String get error => 'assets/icons/error.svg';
@@ -171,6 +174,9 @@ class $AssetsIconsGen {
/// File path: assets/icons/resturan.svg
String get resturan => 'assets/icons/resturan.svg';
+ /// File path: assets/icons/ri_search-2-line.svg
+ String get riSearch2Line => 'assets/icons/ri_search-2-line.svg';
+
/// File path: assets/icons/routing.svg
String get routing => 'assets/icons/routing.svg';
@@ -266,6 +272,7 @@ class $AssetsIconsGen {
documentText,
edit02,
edit,
+ emptyHome,
error,
fastfood,
galleryAdd,
@@ -280,6 +287,7 @@ class $AssetsIconsGen {
radar2,
receiptDisscount,
resturan,
+ riSearch2Line,
routing,
scanBarcode,
shop,
diff --git a/lib/main.dart b/lib/main.dart
index ecc251b..8dbd866 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,9 +1,10 @@
import 'package:business_panel/core/config/app_colors.dart';
import 'package:business_panel/presentation/auth/bloc/auth_bloc.dart';
-import 'package:business_panel/presentation/pages/onboarding_page.dart';
+import 'package:business_panel/presentation/home/bloc/home_bloc.dart';
import 'package:business_panel/presentation/pages/splash_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
+// ignore: depend_on_referenced_packages
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:persian_datetime_picker/persian_datetime_picker.dart';
@@ -16,8 +17,13 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
- return BlocProvider(
- create: (context) => AuthBloc(),
+ return MultiBlocProvider(
+ providers: [
+ BlocProvider(create: (context) => AuthBloc()),
+ BlocProvider(
+ create: (context) => HomeBloc()..add(FetchDiscounts()),
+ ),
+ ],
child: MaterialApp(
title: 'Proxibuy',
debugShowCheckedModeBanner: false,
@@ -29,9 +35,9 @@ class MyApp extends StatelessWidget {
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [
- Locale("fa", "IR"),
- Locale("en", "US"),
- ],
+ Locale("fa", "IR"),
+ Locale("en", "US"),
+ ],
locale: const Locale("fa", "IR"),
theme: ThemeData(
fontFamily: 'Dana',
@@ -104,5 +110,4 @@ class MyApp extends StatelessWidget {
),
);
}
-}
-
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/lib/presentation/auth/bloc/auth_bloc.dart b/lib/presentation/auth/bloc/auth_bloc.dart
index c9acc22..5bd65d1 100644
--- a/lib/presentation/auth/bloc/auth_bloc.dart
+++ b/lib/presentation/auth/bloc/auth_bloc.dart
@@ -1,5 +1,6 @@
+import 'dart:io'; // کتابخانه برای SocketException اضافه شد
import 'package:bloc/bloc.dart';
-import 'package:business_panel/core/config/api_config.dart'; // <-- این خط را اضافه کنید
+import 'package:business_panel/core/config/api_config.dart';
import 'package:business_panel/core/services/token_storage_service.dart';
import 'package:dio/dio.dart';
import 'package:business_panel/core/utils/logging_interceptor.dart';
@@ -29,18 +30,26 @@ class AuthBloc extends Bloc {
return;
}
- // Use the ApiConfig class
await _dio.get(
- ApiConfig.checkShopStatus, // <-- تغییر در این خط
+ ApiConfig.checkShopStatus,
options: Options(
headers: {'Authorization': 'Bearer $token'},
),
);
-
+
emit(ShopExists());
} on DioException catch (e) {
- if (e.response?.statusCode == 404) {
+ // --- منطق تشخیص آفلاین بودن بهبود یافت ---
+ final isOffline = e.error is SocketException ||
+ e.type == DioExceptionType.connectionError ||
+ e.type == DioExceptionType.sendTimeout ||
+ e.type == DioExceptionType.receiveTimeout;
+
+ if (isOffline) {
+ // اگر کاربر آفلاین باشد، این حالت صادر میشود
+ emit(AuthOffline());
+ } else if (e.response?.statusCode == 404) {
emit(NoShop());
} else {
emit(AuthFailure(e.response?.data?['message'] ?? 'An error occurred while checking shop status.'));
@@ -53,9 +62,8 @@ class AuthBloc extends Bloc {
on((event, emit) async {
emit(AuthLoading());
try {
- // Use the ApiConfig class
final response = await _dio.post(
- ApiConfig.sendOtp, // <-- تغییر در این خط
+ ApiConfig.sendOtp,
data: {'Phone': event.phoneNumber, 'Code': event.countryCode},
);
@@ -72,9 +80,8 @@ class AuthBloc extends Bloc {
on((event, emit) async {
emit(AuthLoading());
try {
- // Use the ApiConfig class
final response = await _dio.post(
- ApiConfig.verifyOtp, // <-- تغییر در این خط
+ ApiConfig.verifyOtp,
data: {
'Phone': event.phoneNumber,
'Code': event.countryCode,
diff --git a/lib/presentation/auth/bloc/auth_state.dart b/lib/presentation/auth/bloc/auth_state.dart
index 5e7e52a..46fea13 100644
--- a/lib/presentation/auth/bloc/auth_state.dart
+++ b/lib/presentation/auth/bloc/auth_state.dart
@@ -18,6 +18,10 @@ class ShopExists extends AuthState {}
// ADDED: State for when the user is logged in but has no shop
class NoShop extends AuthState {}
+// *** CHANGE IS HERE: Added state for offline mode ***
+// ADDED: State for when the user is authenticated but offline
+class AuthOffline extends AuthState {}
+
class AuthCodeSentSuccess extends AuthState {}
class AuthVerified extends AuthState {}
diff --git a/lib/presentation/discount/bloc/discount_bloc.dart b/lib/presentation/discount/bloc/discount_bloc.dart
index ba2c9cf..045464f 100644
--- a/lib/presentation/discount/bloc/discount_bloc.dart
+++ b/lib/presentation/discount/bloc/discount_bloc.dart
@@ -1,23 +1,114 @@
+// lib/presentation/discount/bloc/discount_bloc.dart
+
+import 'dart:developer';
import 'package:business_panel/core/config/api_config.dart';
import 'package:business_panel/core/services/token_storage_service.dart';
+import 'package:business_panel/core/utils/logging_interceptor.dart';
import 'package:dio/dio.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/intl.dart';
import 'discount_event.dart';
import 'discount_state.dart';
+import 'dart:convert'; // Import for jsonEncode
class DiscountBloc extends Bloc {
+ final Dio _dio = Dio();
+ final TokenStorageService _tokenStorage = TokenStorageService();
+
DiscountBloc() : super(const DiscountState()) {
-
- final Dio _dio = Dio();
- final TokenStorageService _tokenStorage = TokenStorageService();
+ _dio.interceptors.add(LoggingInterceptor());
+
+ on((event, emit) async {
+ emit(state.copyWith(isLoadingDetails: true, errorMessage: null));
+ try {
+ final token = await _tokenStorage.getAccessToken();
+ if (token == null) {
+ emit(
+ state.copyWith(
+ isLoadingDetails: false,
+ errorMessage: "خطای احراز هویت.",
+ ),
+ );
+ return;
+ }
+
+ final response = await _dio.get(
+ ApiConfig.getDiscountById(event.discountId),
+ options: Options(headers: {'Authorization': 'Bearer $token'}),
+ );
+
+ if (response.statusCode == 200 && response.data['data'] != null) {
+ final data = response.data['data'];
+
+ List