Merge pull request 'added submit and reject for liked tenders' (#99) from liked_tenders_approval into main
Reviewed-on: https://repo.ravanertebat.com/TM/tm_app/pulls/99
This commit is contained in:
@@ -16,7 +16,8 @@ class AppConfig {
|
|||||||
// // Handle different platforms for local development
|
// // Handle different platforms for local development
|
||||||
if (kIsWeb) {
|
if (kIsWeb) {
|
||||||
// For web, use localhost
|
// For web, use localhost
|
||||||
return 'https://app.opplens.com';
|
// return 'https://app.opplens.com';
|
||||||
|
return 'http://localhost:8081';
|
||||||
} else {
|
} else {
|
||||||
// For Android emulator, use 10.0.2.2 (special IP for host machine)
|
// For Android emulator, use 10.0.2.2 (special IP for host machine)
|
||||||
return 'http://10.0.2.2:8081';
|
return 'http://10.0.2.2:8081';
|
||||||
|
|||||||
+2
-2
@@ -17,7 +17,7 @@ void main() async {
|
|||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
||||||
|
|
||||||
// Initialize the logger
|
// Initialize the logger
|
||||||
appLogger.init();
|
appLogger.init();
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ class _MyAppState extends State<MyApp> {
|
|||||||
final themeProvider = context.watch<ThemeProvider>();
|
final themeProvider = context.watch<ThemeProvider>();
|
||||||
|
|
||||||
return MaterialApp.router(
|
return MaterialApp.router(
|
||||||
title: 'Flutter Demo',
|
title: 'OppLens',
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
scrollBehavior: MaterialScrollBehavior().copyWith(
|
scrollBehavior: MaterialScrollBehavior().copyWith(
|
||||||
dragDevices: {
|
dragDevices: {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:tm_app/data/repositories/liked_tenders_repository.dart';
|
|||||||
import 'package:tm_app/data/repositories/tenders_repository.dart';
|
import 'package:tm_app/data/repositories/tenders_repository.dart';
|
||||||
import 'package:tm_app/data/services/model/liked_tender_feedback/liked_tender_feedback.dart';
|
import 'package:tm_app/data/services/model/liked_tender_feedback/liked_tender_feedback.dart';
|
||||||
import 'package:tm_app/data/services/model/liked_tenders_response/liked_tenders_response.dart';
|
import 'package:tm_app/data/services/model/liked_tenders_response/liked_tenders_response.dart';
|
||||||
|
import 'package:tm_app/data/services/model/tender_approvals_toggle_response/tender_approvals_toggle_response.dart';
|
||||||
|
|
||||||
import '../data/services/model/feedback_response/feedback_response.dart';
|
import '../data/services/model/feedback_response/feedback_response.dart';
|
||||||
import '../data/services/model/tender_approvals_by_id_response/tender_approvals_by_id_response.dart';
|
import '../data/services/model/tender_approvals_by_id_response/tender_approvals_by_id_response.dart';
|
||||||
@@ -34,6 +35,18 @@ class LikedTendersViewModel with ChangeNotifier {
|
|||||||
|
|
||||||
int currentPage = 1;
|
int currentPage = 1;
|
||||||
|
|
||||||
|
// Approval status properties
|
||||||
|
bool approvalStatus = false;
|
||||||
|
String _status = '';
|
||||||
|
String? _errorMessage;
|
||||||
|
bool _isSubmitApprovalLoading = false;
|
||||||
|
bool _isRejectApprovalLoading = false;
|
||||||
|
|
||||||
|
String get status => _status;
|
||||||
|
String? get approvalErrorMessage => _errorMessage;
|
||||||
|
bool get isSubmitApprovalLoading => _isSubmitApprovalLoading;
|
||||||
|
bool get isRejectApprovalLoading => _isRejectApprovalLoading;
|
||||||
|
|
||||||
Future<void> getLikedTenders({bool reset = false}) async {
|
Future<void> getLikedTenders({bool reset = false}) async {
|
||||||
if (!reset && (isLoadingMore || !hasMore)) {
|
if (!reset && (isLoadingMore || !hasMore)) {
|
||||||
return;
|
return;
|
||||||
@@ -199,61 +212,157 @@ class LikedTendersViewModel with ChangeNotifier {
|
|||||||
return tenderApprovals.any((a) => a.tenderId == tenderId);
|
return tenderApprovals.any((a) => a.tenderId == tenderId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Future<void> submitTenderApproval({
|
/// Get approval status for a tender safely
|
||||||
// required String tenderId,
|
String? getApprovalStatusForTenderId(String tenderId) {
|
||||||
// required String submissionMode,
|
try {
|
||||||
// }) async {
|
final approval = tenderApprovals.firstWhere(
|
||||||
|
(a) => a.tenderId == tenderId,
|
||||||
|
);
|
||||||
|
return approval.status;
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// errorMessage = null;
|
/// Check if tender is submitted
|
||||||
// notifyListeners();
|
bool isTenderSubmitted(String tenderId) {
|
||||||
// final result = await _tendersRepository.toggleApprovals(
|
return getApprovalStatusForTenderId(tenderId) == 'submitted';
|
||||||
// tenderId: tenderId,
|
}
|
||||||
// status: 'submitted',
|
|
||||||
// submissionMode: submissionMode,
|
|
||||||
// );
|
|
||||||
// switch (result) {
|
|
||||||
// case Ok<TenderApprovalsToggleResponse>():
|
|
||||||
// approvalStatus = true;
|
|
||||||
// _status = result.value.data?.status ?? '';
|
|
||||||
// break;
|
|
||||||
// case Error<TenderApprovalsToggleResponse>():
|
|
||||||
// _errorMessage = result.error.toString();
|
|
||||||
// approvalStatus = false;
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// _isSubmitApprovalLoading = false;
|
|
||||||
// notifyListeners();
|
|
||||||
// _errorMessage = null;
|
|
||||||
// approvalStatus = false;
|
|
||||||
// notifyListeners();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Future<void> rejectTenderApproval({
|
/// Check if tender is rejected
|
||||||
// required String tenderId,
|
bool isTenderRejected(String tenderId) {
|
||||||
// required String submissionMode,
|
return getApprovalStatusForTenderId(tenderId) == 'rejected';
|
||||||
// }) async {
|
}
|
||||||
// _isRejectApprovalLoading = true;
|
|
||||||
// _errorMessage = null;
|
Future<void> submitTenderApproval({
|
||||||
// notifyListeners();
|
required String tenderId,
|
||||||
// final result = await _tendersRepository.toggleApprovals(
|
required String submissionMode,
|
||||||
// tenderId: tenderId,
|
}) async {
|
||||||
// status: 'rejected',
|
_isSubmitApprovalLoading = true;
|
||||||
// submissionMode: submissionMode,
|
_errorMessage = null;
|
||||||
// );
|
notifyListeners();
|
||||||
// switch (result) {
|
|
||||||
// case Ok<TenderApprovalsToggleResponse>():
|
// Store original state for rollback
|
||||||
// approvalStatus = true;
|
final originalApprovals = List<TenderApprovalsData>.of(tenderApprovals);
|
||||||
// _status = result.value.data?.status ?? '';
|
|
||||||
// break;
|
// Check current approval status and update optimistically
|
||||||
// case Error<TenderApprovalsToggleResponse>():
|
final existingApproval = getApprovalStatusForTender(tenderId);
|
||||||
// _errorMessage = result.error.toString();
|
|
||||||
// approvalStatus = false;
|
if (existingApproval != null) {
|
||||||
// break;
|
if (existingApproval.status == 'submitted') {
|
||||||
// }
|
// Remove from list if already submitted
|
||||||
// _isRejectApprovalLoading = false;
|
tenderApprovals.removeWhere((a) => a.tenderId == tenderId);
|
||||||
// notifyListeners();
|
} else if (existingApproval.status == 'rejected') {
|
||||||
// _errorMessage = null;
|
// Change rejected to submitted
|
||||||
// approvalStatus = false;
|
final updatedApproval = existingApproval.copyWith(status: 'submitted');
|
||||||
// notifyListeners();
|
final index = tenderApprovals.indexWhere((a) => a.tenderId == tenderId);
|
||||||
// }
|
if (index != -1) {
|
||||||
|
tenderApprovals[index] = updatedApproval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Add new approval with submitted status
|
||||||
|
final newApproval = TenderApprovalsData(
|
||||||
|
id: null,
|
||||||
|
status: 'submitted',
|
||||||
|
tenderId: tenderId,
|
||||||
|
companyId: null,
|
||||||
|
submissionMode: submissionMode,
|
||||||
|
createdAt: null,
|
||||||
|
tender: null,
|
||||||
|
);
|
||||||
|
tenderApprovals.add(newApproval);
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyListeners();
|
||||||
|
|
||||||
|
// Make server request
|
||||||
|
final result = await _tendersRepository.toggleApprovals(
|
||||||
|
tenderId: tenderId,
|
||||||
|
status: 'submitted',
|
||||||
|
submissionMode: submissionMode,
|
||||||
|
);
|
||||||
|
|
||||||
|
switch (result) {
|
||||||
|
case Ok<TenderApprovalsToggleResponse>():
|
||||||
|
approvalStatus = true;
|
||||||
|
_status = result.value.data?.status ?? '';
|
||||||
|
break;
|
||||||
|
case Error<TenderApprovalsToggleResponse>():
|
||||||
|
// Rollback changes on error
|
||||||
|
tenderApprovals = originalApprovals;
|
||||||
|
_errorMessage = result.error.toString();
|
||||||
|
approvalStatus = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isSubmitApprovalLoading = false;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> rejectTenderApproval({
|
||||||
|
required String tenderId,
|
||||||
|
required String submissionMode,
|
||||||
|
}) async {
|
||||||
|
_isRejectApprovalLoading = true;
|
||||||
|
_errorMessage = null;
|
||||||
|
notifyListeners();
|
||||||
|
|
||||||
|
// Store original state for rollback
|
||||||
|
final originalApprovals = List<TenderApprovalsData>.of(tenderApprovals);
|
||||||
|
|
||||||
|
// Check current approval status and update optimistically
|
||||||
|
final existingApproval = getApprovalStatusForTender(tenderId);
|
||||||
|
|
||||||
|
if (existingApproval != null) {
|
||||||
|
if (existingApproval.status == 'rejected') {
|
||||||
|
// Remove from list if already rejected
|
||||||
|
tenderApprovals.removeWhere((a) => a.tenderId == tenderId);
|
||||||
|
} else if (existingApproval.status == 'submitted') {
|
||||||
|
// Change submitted to rejected
|
||||||
|
final updatedApproval = existingApproval.copyWith(status: 'rejected');
|
||||||
|
final index = tenderApprovals.indexWhere((a) => a.tenderId == tenderId);
|
||||||
|
if (index != -1) {
|
||||||
|
tenderApprovals[index] = updatedApproval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Add new approval with rejected status
|
||||||
|
final newApproval = TenderApprovalsData(
|
||||||
|
id: null,
|
||||||
|
status: 'rejected',
|
||||||
|
tenderId: tenderId,
|
||||||
|
companyId: null,
|
||||||
|
submissionMode: submissionMode,
|
||||||
|
createdAt: null,
|
||||||
|
tender: null,
|
||||||
|
);
|
||||||
|
tenderApprovals.add(newApproval);
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyListeners();
|
||||||
|
|
||||||
|
// Make server request
|
||||||
|
final result = await _tendersRepository.toggleApprovals(
|
||||||
|
tenderId: tenderId,
|
||||||
|
status: 'rejected',
|
||||||
|
submissionMode: submissionMode,
|
||||||
|
);
|
||||||
|
|
||||||
|
switch (result) {
|
||||||
|
case Ok<TenderApprovalsToggleResponse>():
|
||||||
|
approvalStatus = true;
|
||||||
|
_status = result.value.data?.status ?? '';
|
||||||
|
break;
|
||||||
|
case Error<TenderApprovalsToggleResponse>():
|
||||||
|
// Rollback changes on error
|
||||||
|
tenderApprovals = originalApprovals;
|
||||||
|
_errorMessage = result.error.toString();
|
||||||
|
approvalStatus = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isRejectApprovalLoading = false;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:country_flags/country_flags.dart';
|
import 'package:country_flags/country_flags.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
@@ -10,6 +11,8 @@ import 'package:tm_app/view_models/liked_tenders_view_model.dart';
|
|||||||
import 'package:tm_app/views/home/strings/home_strings.dart';
|
import 'package:tm_app/views/home/strings/home_strings.dart';
|
||||||
|
|
||||||
import '../../../data/services/model/tender_approvals_data/tender_approvals_data.dart';
|
import '../../../data/services/model/tender_approvals_data/tender_approvals_data.dart';
|
||||||
|
import '../../shared/select_submission_bottom_sheet.dart';
|
||||||
|
import '../../shared/select_submission_dialog.dart';
|
||||||
|
|
||||||
class LikedListItem extends StatelessWidget {
|
class LikedListItem extends StatelessWidget {
|
||||||
const LikedListItem({
|
const LikedListItem({
|
||||||
@@ -32,7 +35,6 @@ class LikedListItem extends StatelessWidget {
|
|||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
border: Border.all(color: AppColors.grey30),
|
border: Border.all(color: AppColors.grey30),
|
||||||
),
|
),
|
||||||
margin: EdgeInsetsDirectional.only(end: 16.0.w()),
|
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: 250.0.h(),
|
height: 250.0.h(),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@@ -172,52 +174,99 @@ class LikedListItem extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
Container(
|
InkWell(
|
||||||
width: 32.0.w(),
|
onTap: () {
|
||||||
height: 32.0.h(),
|
viewModel.rejectTenderApproval(
|
||||||
decoration: BoxDecoration(
|
tenderId: tender.id!,
|
||||||
shape: BoxShape.circle,
|
submissionMode: 'self-apply',
|
||||||
border: Border.all(
|
);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
width: 32.0.w(),
|
||||||
|
height: 32.0.h(),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
border: Border.all(
|
||||||
|
color:
|
||||||
|
viewModel.isTenderRejected(tender.id!)
|
||||||
|
? AppColors.errorColor
|
||||||
|
: AppColors.grey60,
|
||||||
|
width: 1.5.w(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Icon(
|
||||||
|
Icons.close,
|
||||||
color:
|
color:
|
||||||
approval?.status == 'rejected'
|
viewModel.isTenderRejected(tender.id!)
|
||||||
? AppColors.errorColor
|
? AppColors.errorColor
|
||||||
: AppColors.grey60,
|
: AppColors.grey60,
|
||||||
width: 1.5.w(),
|
size: 16,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
alignment: Alignment.center,
|
|
||||||
child: Icon(
|
|
||||||
Icons.close,
|
|
||||||
color:
|
|
||||||
approval?.status == 'rejected'
|
|
||||||
? AppColors.errorColor
|
|
||||||
: AppColors.grey60,
|
|
||||||
size: 16,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
SizedBox(width: 30.0.w()),
|
SizedBox(width: 30.0.w()),
|
||||||
Container(
|
InkWell(
|
||||||
width: 32.0.w(),
|
onTap: () {
|
||||||
height: 32.0.h(),
|
if (viewModel.isTenderSubmitted(tender.id!)) {
|
||||||
decoration: BoxDecoration(
|
viewModel.submitTenderApproval(
|
||||||
shape: BoxShape.circle,
|
tenderId: tender.id!,
|
||||||
border: Border.all(
|
submissionMode: 'self-apply',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
kIsWeb
|
||||||
|
? showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return SelectSubmissionDialog(
|
||||||
|
onConfirm: (value) {
|
||||||
|
viewModel.submitTenderApproval(
|
||||||
|
tenderId: tender.id!,
|
||||||
|
submissionMode: value,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: showModalBottomSheet(
|
||||||
|
isDismissible: true,
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return SelectSubmissionBottomSheet(
|
||||||
|
onConfirm: (value) {
|
||||||
|
viewModel.submitTenderApproval(
|
||||||
|
tenderId: tender.id!,
|
||||||
|
submissionMode: value,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
width: 32.0.w(),
|
||||||
|
height: 32.0.h(),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
border: Border.all(
|
||||||
|
color:
|
||||||
|
viewModel.isTenderSubmitted(tender.id!)
|
||||||
|
? AppColors.successColor
|
||||||
|
: AppColors.grey60,
|
||||||
|
width: 1.5.w(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Icon(
|
||||||
|
Icons.check,
|
||||||
color:
|
color:
|
||||||
approval?.status == 'submitted'
|
viewModel.isTenderSubmitted(tender.id!)
|
||||||
? AppColors.successColor
|
? AppColors.successColor
|
||||||
: AppColors.grey60,
|
: AppColors.grey60,
|
||||||
width: 1.5.w(),
|
size: 16,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
alignment: Alignment.center,
|
|
||||||
child: Icon(
|
|
||||||
Icons.check,
|
|
||||||
color:
|
|
||||||
approval?.status == 'submitted'
|
|
||||||
? AppColors.successColor
|
|
||||||
: AppColors.grey60,
|
|
||||||
size: 16,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user