diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist
index 7c56964..391a902 100644
--- a/ios/Flutter/AppFrameworkInfo.plist
+++ b/ios/Flutter/AppFrameworkInfo.plist
@@ -20,7 +20,5 @@
????
CFBundleVersion
1.0
- MinimumOSVersion
- 12.0
diff --git a/ios/Podfile b/ios/Podfile
index e549ee2..620e46e 100644
--- a/ios/Podfile
+++ b/ios/Podfile
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
-# platform :ios, '12.0'
+# platform :ios, '13.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index d08bbb7..e13d49d 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -14,6 +14,7 @@
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+ 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -55,6 +56,7 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -62,6 +64,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -79,6 +82,7 @@
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
+ 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */,
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
@@ -142,6 +146,9 @@
productType = "com.apple.product-type.bundle.unit-test";
};
97C146ED1CF9000F007C117D /* Runner */ = {
+ packageProductDependencies = (
+ 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */,
+ );
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
@@ -165,6 +172,9 @@
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
+ packageReferences = (
+ 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */,
+ );
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
@@ -346,7 +356,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -472,7 +482,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -523,7 +533,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -611,6 +621,18 @@
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
+/* Begin XCLocalSwiftPackageReference section */
+ 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = {
+ isa = XCLocalSwiftPackageReference;
+ relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage;
+ };
+/* End XCLocalSwiftPackageReference section */
+/* Begin XCSwiftPackageProductDependency section */
+ 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = {
+ isa = XCSwiftPackageProductDependency;
+ productName = FlutterGeneratedPluginSwiftPackage;
+ };
+/* End XCSwiftPackageProductDependency section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}
diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
new file mode 100644
index 0000000..7e63959
--- /dev/null
+++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -0,0 +1,176 @@
+{
+ "pins" : [
+ {
+ "identity" : "abseil-cpp-binary",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/google/abseil-cpp-binary.git",
+ "state" : {
+ "revision" : "bbe8b69694d7873315fd3a4ad41efe043e1c07c5",
+ "version" : "1.2024072200.0"
+ }
+ },
+ {
+ "identity" : "app-check",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/google/app-check.git",
+ "state" : {
+ "revision" : "61b85103a1aeed8218f17c794687781505fbbef5",
+ "version" : "11.2.0"
+ }
+ },
+ {
+ "identity" : "dkcamera",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/zhangao0086/DKCamera",
+ "state" : {
+ "branch" : "master",
+ "revision" : "5c691d11014b910aff69f960475d70e65d9dcc96"
+ }
+ },
+ {
+ "identity" : "dkimagepickercontroller",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/zhangao0086/DKImagePickerController",
+ "state" : {
+ "branch" : "4.3.9",
+ "revision" : "0bdfeacefa308545adde07bef86e349186335915"
+ }
+ },
+ {
+ "identity" : "dkphotogallery",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/zhangao0086/DKPhotoGallery",
+ "state" : {
+ "branch" : "master",
+ "revision" : "311c1bc7a94f1538f82773a79c84374b12a2ef3d"
+ }
+ },
+ {
+ "identity" : "firebase-ios-sdk",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/firebase/firebase-ios-sdk",
+ "state" : {
+ "revision" : "8d5b4189f1f482df8d5c58c9985ea70491ef5382",
+ "version" : "12.14.0"
+ }
+ },
+ {
+ "identity" : "google-ads-on-device-conversion-ios-sdk",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/googleads/google-ads-on-device-conversion-ios-sdk",
+ "state" : {
+ "revision" : "9bfcc6cf435b2e7c5562c1900b8680c594fa9a64",
+ "version" : "3.6.0"
+ }
+ },
+ {
+ "identity" : "googleappmeasurement",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/google/GoogleAppMeasurement.git",
+ "state" : {
+ "revision" : "219e564a8510e983e675c94f77f7f7c50049f22d",
+ "version" : "12.14.0"
+ }
+ },
+ {
+ "identity" : "googledatatransport",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/google/GoogleDataTransport.git",
+ "state" : {
+ "revision" : "617af071af9aa1d6a091d59a202910ac482128f9",
+ "version" : "10.1.0"
+ }
+ },
+ {
+ "identity" : "googleutilities",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/google/GoogleUtilities.git",
+ "state" : {
+ "revision" : "60da361632d0de02786f709bdc0c4df340f7613e",
+ "version" : "8.1.0"
+ }
+ },
+ {
+ "identity" : "grpc-binary",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/google/grpc-binary.git",
+ "state" : {
+ "revision" : "75b31c842f664a0f46a2e590a570e370249fd8f6",
+ "version" : "1.69.1"
+ }
+ },
+ {
+ "identity" : "gtm-session-fetcher",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/google/gtm-session-fetcher.git",
+ "state" : {
+ "revision" : "c0ac7575d70050c2973ba2318bd5af47f8e8153a",
+ "version" : "5.3.0"
+ }
+ },
+ {
+ "identity" : "interop-ios-for-google-sdks",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/google/interop-ios-for-google-sdks.git",
+ "state" : {
+ "revision" : "040d087ac2267d2ddd4cca36c757d1c6a05fdbfe",
+ "version" : "101.0.0"
+ }
+ },
+ {
+ "identity" : "leveldb",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/firebase/leveldb.git",
+ "state" : {
+ "revision" : "a0bc79961d7be727d258d33d5a6b2f1023270ba1",
+ "version" : "1.22.5"
+ }
+ },
+ {
+ "identity" : "nanopb",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/firebase/nanopb.git",
+ "state" : {
+ "revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1",
+ "version" : "2.30910.0"
+ }
+ },
+ {
+ "identity" : "promises",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/google/promises.git",
+ "state" : {
+ "revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac",
+ "version" : "2.4.0"
+ }
+ },
+ {
+ "identity" : "sdwebimage",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/SDWebImage/SDWebImage",
+ "state" : {
+ "revision" : "2de3a496eaf6df9a1312862adcfd54acd73c39c0",
+ "version" : "5.21.7"
+ }
+ },
+ {
+ "identity" : "swiftygif",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/kirualex/SwiftyGif.git",
+ "state" : {
+ "revision" : "4430cbc148baa3907651d40562d96325426f409a",
+ "version" : "5.4.5"
+ }
+ },
+ {
+ "identity" : "tocropviewcontroller",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/TimOliver/TOCropViewController",
+ "state" : {
+ "revision" : "d4a6d8100f4b886fdbc8ae399bf144ff3e9afb7e",
+ "version" : "2.8.0"
+ }
+ }
+ ],
+ "version" : 2
+}
diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
index 15cada4..c3fedb2 100644
--- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
+++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -5,6 +5,24 @@
+
+
+
+
+
+
+
+
+
+
Bool {
- GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
+
+ func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) {
+ GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry)
+ }
}
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index 3abf06c..a328b97 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -2,6 +2,8 @@
+ CADisableMinimumFrameDurationOnPhone
+
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
CFBundleDisplayName
@@ -24,6 +26,29 @@
$(FLUTTER_BUILD_NUMBER)
LSRequiresIPhoneOS
+ UIApplicationSceneManifest
+
+ UIApplicationSupportsMultipleScenes
+
+ UISceneConfigurations
+
+ UIWindowSceneSessionRoleApplication
+
+
+ UISceneClassName
+ UIWindowScene
+ UISceneConfigurationName
+ flutter
+ UISceneDelegateClassName
+ FlutterSceneDelegate
+ UISceneStoryboardFile
+ Main
+
+
+
+
+ UIApplicationSupportsIndirectInputEvents
+
UILaunchStoryboardName
LaunchScreen
UIMainStoryboardFile
@@ -41,9 +66,5 @@
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
- CADisableMinimumFrameDurationOnPhone
-
- UIApplicationSupportsIndirectInputEvents
-
diff --git a/lib/core/config/dependencies.dart b/lib/core/config/dependencies.dart
index 3461b4b..3ed519d 100644
--- a/lib/core/config/dependencies.dart
+++ b/lib/core/config/dependencies.dart
@@ -121,6 +121,7 @@ List get repositories {
(context) => HomeRepository(
homeService: context.read(),
yourTendersService: context.read(),
+ likedTendersService: context.read(),
),
lazy: true,
),
diff --git a/lib/core/enums/task_status.dart b/lib/core/enums/task_status.dart
deleted file mode 100644
index 5298c23..0000000
--- a/lib/core/enums/task_status.dart
+++ /dev/null
@@ -1,29 +0,0 @@
-enum TaskStatus { toDo, inProgress, done }
-
-extension TaskStatusExtension on TaskStatus {
- // تبدیل enum به string
- String get value => name;
-
- // تبدیل string به enum
- static TaskStatus fromString(String? status) {
- if (status == null) {
- return TaskStatus.toDo;
- }
-
- return TaskStatus.values.firstWhere(
- (e) => e.name == status,
- orElse: () => TaskStatus.toDo,
- );
- }
-
- String get displayNameEn {
- switch (this) {
- case TaskStatus.toDo:
- return 'To Do';
- case TaskStatus.inProgress:
- return 'In Progress';
- case TaskStatus.done:
- return 'Done';
- }
- }
-}
diff --git a/lib/core/extensions/board_data_extension.dart b/lib/core/extensions/board_data_extension.dart
deleted file mode 100644
index 31039de..0000000
--- a/lib/core/extensions/board_data_extension.dart
+++ /dev/null
@@ -1,12 +0,0 @@
-import '../../data/services/model/board_response/board_response.dart';
-import '../enums/task_status.dart';
-
-extension BoardDataExtension on BoardData {
- // تبدیل status string به TaskStatus enum
- TaskStatus get statusEnum => TaskStatusExtension.fromString(status);
-
- // کپی BoardData با status جدید
- BoardData copyWithStatus(TaskStatus newStatus) {
- return copyWith(status: newStatus.value);
- }
-}
diff --git a/lib/core/routes/app_routes.dart b/lib/core/routes/app_routes.dart
index f8aa9e6..4b039fe 100644
--- a/lib/core/routes/app_routes.dart
+++ b/lib/core/routes/app_routes.dart
@@ -250,11 +250,14 @@ class FinalCompletionOfDocumentsRouteData extends GoRouteData
@TypedGoRoute(path: '/completion_of_documents')
class CompletionOfDocumentsRouteData extends GoRouteData
with _$CompletionOfDocumentsRouteData {
- const CompletionOfDocumentsRouteData();
+ final String? tenderId;
+ const CompletionOfDocumentsRouteData({this.tenderId});
@override
Widget build(BuildContext context, GoRouterState state) {
- return const CompletionOfDocumentsScreen();
+ return tenderDetailProvider(
+ child: CompletionOfDocumentsScreen(tenderId: tenderId),
+ );
}
}
diff --git a/lib/core/routes/app_routes.g.dart b/lib/core/routes/app_routes.g.dart
index 261a276..d4e96d6 100644
--- a/lib/core/routes/app_routes.g.dart
+++ b/lib/core/routes/app_routes.g.dart
@@ -308,10 +308,20 @@ RouteBase get $completionOfDocumentsRouteData => GoRouteData.$route(
mixin _$CompletionOfDocumentsRouteData on GoRouteData {
static CompletionOfDocumentsRouteData _fromState(GoRouterState state) =>
- const CompletionOfDocumentsRouteData();
+ CompletionOfDocumentsRouteData(
+ tenderId: state.uri.queryParameters['tender-id'],
+ );
+
+ CompletionOfDocumentsRouteData get _self =>
+ this as CompletionOfDocumentsRouteData;
@override
- String get location => GoRouteData.$location('/completion_of_documents');
+ String get location => GoRouteData.$location(
+ '/completion_of_documents',
+ queryParams: {
+ if (_self.tenderId != null) 'tender-id': _self.tenderId!,
+ },
+ );
@override
void go(BuildContext context) => context.go(location);
diff --git a/lib/core/utils/date_utils.dart b/lib/core/utils/date_utils.dart
index 77ea623..3cadf73 100644
--- a/lib/core/utils/date_utils.dart
+++ b/lib/core/utils/date_utils.dart
@@ -1,9 +1,41 @@
import 'package:intl/intl.dart';
-String timeConvertor(int timestamp) {
- // Convert seconds → milliseconds
- final date = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000);
+/// Parses an API timestamp field that may arrive as int, double, or string.
+/// Returns null for missing, empty, unparseable, or non-positive values
+/// (a non-positive epoch is treated as "no value" by the API).
+int? unixTimestampFromJson(dynamic value) {
+ if (value == null) return null;
+ int? parsed;
+ if (value is int) {
+ parsed = value;
+ } else if (value is num) {
+ parsed = value.toInt();
+ } else if (value is String) {
+ final trimmed = value.trim();
+ if (trimmed.isEmpty) return null;
+ parsed = int.tryParse(trimmed);
+ }
+ if (parsed == null || parsed <= 0) return null;
+ return parsed;
+}
- // Format to yyyy-MM-dd
+/// Converts a Unix timestamp from the API to a display string.
+///
+/// The API contract is seconds since epoch, but some legacy fields still
+/// emit milliseconds. Anything past the year 2286 in seconds (>1e10) is
+/// assumed to be milliseconds and converted down. Remove this branch once
+/// every endpoint has been migrated to seconds.
+String timeConvertor(int? unix, {bool hasTime = false}) {
+ if (unix == null || unix <= 0) return '-';
+
+ final seconds = unix > 9999999999 ? unix ~/ 1000 : unix;
+ final date = DateTime.fromMillisecondsSinceEpoch(
+ seconds * 1000,
+ isUtc: true,
+ ).toLocal();
+
+ if (hasTime) {
+ return DateFormat('yyyy-MM-dd HH:mm').format(date);
+ }
return DateFormat('yyyy-MM-dd').format(date);
}
diff --git a/lib/data/repositories/board_repositories.dart b/lib/data/repositories/board_repositories.dart
index 703ef5b..6f2cca1 100644
--- a/lib/data/repositories/board_repositories.dart
+++ b/lib/data/repositories/board_repositories.dart
@@ -7,7 +7,17 @@ class BoardRepository {
BoardRepository({required BoardService boardService})
: _boardService = boardService;
- Future> getBoard() async {
- return _boardService.getBoard();
+ Future> getBoard() => _boardService.getBoard();
+
+ Future> moveCard({
+ required String cardId,
+ required String columnId,
+ required int newOrder,
+ }) {
+ return _boardService.moveCard(
+ cardId: cardId,
+ columnId: columnId,
+ newOrder: newOrder,
+ );
}
}
diff --git a/lib/data/repositories/home_repository.dart b/lib/data/repositories/home_repository.dart
index 1855bb1..50a9a7a 100644
--- a/lib/data/repositories/home_repository.dart
+++ b/lib/data/repositories/home_repository.dart
@@ -1,21 +1,25 @@
import 'package:tm_app/core/utils/result.dart';
import 'package:tm_app/data/services/home_service.dart';
+import 'package:tm_app/data/services/liked_tenders_service.dart';
import 'package:tm_app/data/services/model/recommended_tenders_response/recommended_tenders_response.dart';
import 'package:tm_app/data/services/model/tender_approvals_response/tender_approvals_response.dart';
import 'package:tm_app/data/services/model/tender_approvals_stats_response/tender_approvals_stats_response.dart';
-import '../services/model/feedback_stats_response/feedback_stat_response.dart';
import '../services/model/request_models/get_tenders_request_model/get_tenders_request_model.dart';
+import '../services/model/request_models/tender_feedback_request_model/tender_feedback_request_model.dart';
import '../services/your_tenders_service.dart';
class HomeRepository {
HomeRepository({
required HomeService homeService,
required YourTendersService yourTendersService,
+ required LikedTendersService likedTendersService,
}) : _homeService = homeService,
- _yourTendersService = yourTendersService;
+ _yourTendersService = yourTendersService,
+ _likedTendersService = likedTendersService;
final HomeService _homeService;
final YourTendersService _yourTendersService;
+ final LikedTendersService _likedTendersService;
Future> getHomeRecommendedTenders({
required int limit,
@@ -34,11 +38,25 @@ class HomeRepository {
return _homeService.getTendersApprovalsStats();
}
- Future> getFeedbackStats() {
- return _homeService.getFeedbackStats();
- }
- Future>> checkUnreadNotifications() async {
- return _homeService.checkUnreadNotifications();
-}
+ /// Returns how many tenders the current user has liked (not company-wide).
+ Future> getUserLikedTendersCount() async {
+ final result = await _likedTendersService.getLikedTenders(
+ requestModel: TenderFeedbackRequestModel(
+ feedbackType: 'like',
+ limit: 1,
+ offset: 0,
+ ),
+ );
+ switch (result) {
+ case Ok(value: final response):
+ return Result.ok(response.data?.meta?.total ?? 0);
+ case Error(error: final error):
+ return Result.error(error);
+ }
+ }
+
+ Future>> checkUnreadNotifications() async {
+ return _homeService.checkUnreadNotifications();
+ }
}
diff --git a/lib/data/repositories/tenders_repository.dart b/lib/data/repositories/tenders_repository.dart
index 567e010..d8a38d7 100644
--- a/lib/data/repositories/tenders_repository.dart
+++ b/lib/data/repositories/tenders_repository.dart
@@ -13,7 +13,8 @@ class TendersRepository {
Future> getTenders({
required int limit,
- required int offset,
+ int? offset,
+ String? cursor,
String? query,
String? sortBy,
String? sortOrder,
@@ -23,6 +24,7 @@ class TendersRepository {
return _tendersService.getTenders(
limit: limit,
offset: offset,
+ cursor: cursor,
query: query,
sortBy: sortBy,
sortOrder: sortOrder,
diff --git a/lib/data/services/api/tenders_api.dart b/lib/data/services/api/tenders_api.dart
index 1b582e4..96dc88a 100644
--- a/lib/data/services/api/tenders_api.dart
+++ b/lib/data/services/api/tenders_api.dart
@@ -4,14 +4,20 @@ class TendersApi {
static const String tenders = '/api/v1/tenders';
static String getTenders({
required int limit,
- required int offset,
+ int? offset,
+ String? cursor,
String? query,
String? sortBy,
String? sortOrder,
String? publicationDateFrom,
String? publicationDateTo,
}) {
- String url = '$tenders?limit=$limit&offset=$offset';
+ String url = '$tenders?limit=$limit';
+ if (cursor != null && cursor.isNotEmpty) {
+ url += '&cursor=${Uri.encodeComponent(cursor)}';
+ } else if (offset != null) {
+ url += '&offset=$offset';
+ }
if (query != null && query.isNotEmpty) {
url += '&q=${Uri.encodeComponent(query)}';
}
diff --git a/lib/data/services/board_service.dart b/lib/data/services/board_service.dart
index 7d2f579..5343c55 100644
--- a/lib/data/services/board_service.dart
+++ b/lib/data/services/board_service.dart
@@ -1,5 +1,6 @@
-import '../../../core/network/network_manager.dart';
-import '../../core/config/app_config.dart';
+import 'dart:convert';
+
+import '../../core/network/network_manager.dart';
import '../../core/utils/result.dart';
import 'model/board_response/board_response.dart';
@@ -9,72 +10,45 @@ class BoardService {
final NetworkManager _networkManager;
+ static const _basePath = '/admin/v1/kanban/kanban';
+
Future> getBoard() async {
- final uri = '${AppConfig.apiBaseUrl}/api/v1/board';
- await Future.delayed(const Duration(seconds: 2));
+ return _networkManager.makeRequest(
+ '$_basePath/boards/default',
+ (json) => BoardResponse.fromJson(json),
+ method: 'GET',
+ );
+ }
- // final result = await _networkManager.makeRequest(
- // uri,
- // (json) => BoardResponse.fromJson(json),
- // method: 'GET',
- // );
- // return result;
-
- return Result.ok(
- BoardResponse(
- success: true,
- message: 'Board data fetched successfully',
- data: tasks,
- error: null,
- ),
+ Future> moveCard({
+ required String cardId,
+ required String columnId,
+ required int newOrder,
+ }) async {
+ final body = jsonEncode({
+ 'card_id': cardId,
+ 'column_id': columnId,
+ 'new_order': newOrder,
+ });
+ return _networkManager.makeRequest(
+ '$_basePath/cards/move',
+ (json) => MoveCardResponse.fromJson(json),
+ method: 'PUT',
+ data: body,
);
}
}
-List tasks = [
- BoardData(
- id: '1',
- description:
- 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua',
- deadline: DateTime(2025, 6, 15),
- assignedTo: 'Pratyush Tewari',
- isReady: false,
- status: 'toDo',
- ),
- BoardData(
- id: '2',
- description:
- 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua',
- deadline: DateTime(2025, 6, 15),
- assignedTo: 'Pratyush Tewari',
- isReady: true,
- status: 'toDo',
- ),
- BoardData(
- id: '3',
- description:
- 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua',
- deadline: DateTime(2025, 6, 15),
- assignedTo: 'Pratyush Tewari',
- isReady: false,
- status: 'inProgress',
- ),
- BoardData(
- id: '4',
- description:
- 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua',
- deadline: DateTime(2025, 6, 15),
- assignedTo: 'Pratyush Tewari',
- isReady: true,
- status: 'inProgress',
- ),
- BoardData(
- id: '5',
- description:
- 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua',
- deadline: DateTime(2025, 6, 15),
- assignedTo: 'Pratyush Tewari',
- isReady: false,
- status: 'done',
- ),
-];
+class MoveCardResponse {
+ MoveCardResponse({required this.success, required this.message});
+
+ final bool? success;
+ final String? message;
+
+ factory MoveCardResponse.fromJson(Map json) {
+ return MoveCardResponse(
+ success: json['success'] as bool?,
+ message: json['message'] as String?,
+ );
+ }
+}
diff --git a/lib/data/services/model/board_response/board_response.dart b/lib/data/services/model/board_response/board_response.dart
index 8bf6c6b..1b0f140 100644
--- a/lib/data/services/model/board_response/board_response.dart
+++ b/lib/data/services/model/board_response/board_response.dart
@@ -1,34 +1,229 @@
-import 'package:freezed_annotation/freezed_annotation.dart';
-
+import '../../../../core/utils/date_utils.dart';
import '../error/error_model.dart';
-part 'board_response.freezed.dart';
-part 'board_response.g.dart';
+class BoardResponse {
+ BoardResponse({
+ required this.success,
+ required this.message,
+ required this.data,
+ required this.error,
+ });
-@freezed
-abstract class BoardResponse with _$BoardResponse {
- const factory BoardResponse({
- required bool? success,
- required String? message,
- required List? data,
- required ErrorModel? error,
- }) = _BoardResponse;
+ final bool? success;
+ final String? message;
+ final BoardDetailData? data;
+ final ErrorModel? error;
- factory BoardResponse.fromJson(Map json) =>
- _$BoardResponseFromJson(json);
+ factory BoardResponse.fromJson(Map json) {
+ final dataJson = json['data'];
+ final errorJson = json['error'];
+ return BoardResponse(
+ success: json['success'] as bool?,
+ message: json['message'] as String?,
+ data:
+ dataJson is Map
+ ? BoardDetailData.fromJson(dataJson)
+ : null,
+ error:
+ errorJson is Map
+ ? ErrorModel.fromJson(errorJson)
+ : null,
+ );
+ }
}
-@freezed
-abstract class BoardData with _$BoardData {
- const factory BoardData({
- required String? id,
- required String? description,
- required DateTime? deadline,
- required String? assignedTo,
- required bool? isReady,
- required String? status,
- }) = _BoardData;
+class BoardDetailData {
+ BoardDetailData({required this.board});
- factory BoardData.fromJson(Map json) =>
- _$BoardDataFromJson(json);
+ final Board? board;
+
+ factory BoardDetailData.fromJson(Map json) {
+ final boardJson = json['board'];
+ return BoardDetailData(
+ board:
+ boardJson is Map ? Board.fromJson(boardJson) : null,
+ );
+ }
+}
+
+class Board {
+ Board({
+ required this.id,
+ required this.name,
+ required this.description,
+ required this.columns,
+ required this.isDefault,
+ required this.userId,
+ required this.createdAt,
+ required this.updatedAt,
+ });
+
+ final String? id;
+ final String? name;
+ final String? description;
+ final List columns;
+ final bool? isDefault;
+ final String? userId;
+ final int? createdAt;
+ final int? updatedAt;
+
+ factory Board.fromJson(Map json) {
+ final columnsJson = json['columns'];
+ return Board(
+ id: json['id'] as String?,
+ name: json['name'] as String?,
+ description: json['description'] as String?,
+ columns:
+ columnsJson is List
+ ? columnsJson
+ .whereType