feature/payment #77
@ -4,27 +4,16 @@ import 'package:marco/helpers/services/payment_service.dart';
|
||||
import 'package:marco/helpers/services/app_logger.dart';
|
||||
|
||||
class PaymentController with ChangeNotifier {
|
||||
final Razorpay _razorpay = Razorpay();
|
||||
Razorpay? _razorpay;
|
||||
final PaymentService _paymentService = PaymentService();
|
||||
|
||||
bool isProcessing = false;
|
||||
BuildContext? _context; // For showing dialogs/snackbars
|
||||
|
||||
PaymentController() {
|
||||
// Razorpay event listeners
|
||||
_razorpay.on(Razorpay.EVENT_PAYMENT_SUCCESS, _handlePaymentSuccess);
|
||||
_razorpay.on(Razorpay.EVENT_PAYMENT_ERROR, _handlePaymentError);
|
||||
_razorpay.on(Razorpay.EVENT_EXTERNAL_WALLET, _handleExternalWallet);
|
||||
}
|
||||
|
||||
void disposeController() {
|
||||
_razorpay.clear();
|
||||
}
|
||||
BuildContext? _context;
|
||||
|
||||
/// ==============================
|
||||
/// START PAYMENT
|
||||
/// START PAYMENT (Safe init)
|
||||
/// ==============================
|
||||
Future<void> startPayment({
|
||||
Future<bool> startPayment({
|
||||
required double amount,
|
||||
required String description,
|
||||
required BuildContext context,
|
||||
@ -35,44 +24,74 @@ class PaymentController with ChangeNotifier {
|
||||
|
||||
logSafe("🟢 Starting payment for ₹$amount - $description");
|
||||
|
||||
// Call backend to create Razorpay order
|
||||
final result = await _paymentService.createOrder(amount);
|
||||
// ✅ Clear any old instance (prevents freeze on re-init)
|
||||
_cleanup();
|
||||
|
||||
if (result == null) {
|
||||
_showError(context, "Failed to connect to server.");
|
||||
isProcessing = false;
|
||||
notifyListeners();
|
||||
return;
|
||||
// ✅ Create order first (no login dependency)
|
||||
Map<String, dynamic>? result;
|
||||
try {
|
||||
result = await _paymentService
|
||||
.createOrder(amount)
|
||||
.timeout(const Duration(seconds: 10));
|
||||
} catch (e) {
|
||||
logSafe("⏱️ API Timeout or Exception while creating order: $e",
|
||||
level: LogLevel.error);
|
||||
}
|
||||
|
||||
final orderId = result['orderId'];
|
||||
final key = result['key'];
|
||||
if (result == null) {
|
||||
_showError(context, "Failed to connect to server or timeout.");
|
||||
isProcessing = false;
|
||||
notifyListeners();
|
||||
return false;
|
||||
}
|
||||
|
||||
final orderId = result['data']?['orderId'];
|
||||
final key = result['data']?['key'];
|
||||
if (orderId == null || key == null) {
|
||||
_showError(context, "Invalid response from server.");
|
||||
isProcessing = false;
|
||||
notifyListeners();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
var options = {
|
||||
// ✅ Safe initialization of Razorpay (deferred)
|
||||
try {
|
||||
logSafe("🟡 Initializing Razorpay instance...");
|
||||
_razorpay = Razorpay();
|
||||
_razorpay?.on(Razorpay.EVENT_PAYMENT_SUCCESS, _handlePaymentSuccess);
|
||||
_razorpay?.on(Razorpay.EVENT_PAYMENT_ERROR, _handlePaymentError);
|
||||
_razorpay?.on(Razorpay.EVENT_EXTERNAL_WALLET, _handleExternalWallet);
|
||||
logSafe("✅ Razorpay instance initialized successfully.");
|
||||
} catch (e) {
|
||||
logSafe("❌ Razorpay init failed: $e", level: LogLevel.error);
|
||||
_showError(context, "Payment system initialization failed.");
|
||||
isProcessing = false;
|
||||
notifyListeners();
|
||||
return false;
|
||||
}
|
||||
|
||||
// ✅ Prepare payment options
|
||||
final options = {
|
||||
'key': key,
|
||||
'amount': (amount * 100).toInt(), // Razorpay takes amount in paise
|
||||
'amount': (amount * 100).toInt(),
|
||||
'name': 'Your Company Name',
|
||||
'description': description,
|
||||
'order_id': orderId,
|
||||
'theme': {'color': '#0D47A1'},
|
||||
'timeout': 120, // 2 minutes timeout
|
||||
'timeout': 120, // seconds
|
||||
};
|
||||
|
||||
try {
|
||||
logSafe("🟠 Opening Razorpay with options: $options");
|
||||
_razorpay.open(options);
|
||||
logSafe("🟠 Opening Razorpay checkout with options: $options");
|
||||
_razorpay!.open(options);
|
||||
return true;
|
||||
} catch (e) {
|
||||
logSafe("❌ Error opening Razorpay: $e", level: LogLevel.error);
|
||||
_showError(context, "Error opening payment gateway.");
|
||||
_cleanup();
|
||||
isProcessing = false;
|
||||
notifyListeners();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,15 +100,21 @@ class PaymentController with ChangeNotifier {
|
||||
/// ==============================
|
||||
void _handlePaymentSuccess(PaymentSuccessResponse response) async {
|
||||
logSafe("✅ Payment Success: ${response.paymentId}");
|
||||
|
||||
isProcessing = true;
|
||||
notifyListeners();
|
||||
|
||||
final result = await _paymentService.verifyPayment(
|
||||
paymentId: response.paymentId!,
|
||||
orderId: response.orderId!,
|
||||
signature: response.signature!,
|
||||
);
|
||||
Map<String, dynamic>? result;
|
||||
try {
|
||||
result = await _paymentService
|
||||
.verifyPayment(
|
||||
paymentId: response.paymentId!,
|
||||
orderId: response.orderId!,
|
||||
signature: response.signature!,
|
||||
)
|
||||
.timeout(const Duration(seconds: 10));
|
||||
} catch (e) {
|
||||
logSafe("⏱️ Verification timeout/error: $e", level: LogLevel.error);
|
||||
}
|
||||
|
||||
isProcessing = false;
|
||||
notifyListeners();
|
||||
@ -107,6 +132,8 @@ class PaymentController with ChangeNotifier {
|
||||
success: false,
|
||||
);
|
||||
}
|
||||
|
||||
_cleanup();
|
||||
}
|
||||
|
||||
void _handlePaymentError(PaymentFailureResponse response) {
|
||||
@ -119,6 +146,8 @@ class PaymentController with ChangeNotifier {
|
||||
message: "Reason: ${response.message ?? 'Unknown error'}",
|
||||
success: false,
|
||||
);
|
||||
|
||||
_cleanup();
|
||||
}
|
||||
|
||||
void _handleExternalWallet(ExternalWalletResponse response) {
|
||||
@ -126,7 +155,25 @@ class PaymentController with ChangeNotifier {
|
||||
}
|
||||
|
||||
/// ==============================
|
||||
/// HELPER DIALOGS / SNACKBARS
|
||||
/// CLEANUP / DISPOSE
|
||||
/// ==============================
|
||||
void _cleanup() {
|
||||
try {
|
||||
_razorpay?.clear();
|
||||
} catch (_) {}
|
||||
_razorpay = null;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_cleanup();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void disposeController() => _cleanup();
|
||||
|
||||
/// ==============================
|
||||
/// HELPER UI FUNCTIONS
|
||||
/// ==============================
|
||||
void _showDialog({
|
||||
required String title,
|
||||
@ -145,9 +192,14 @@ class PaymentController with ChangeNotifier {
|
||||
child: const Text("OK"),
|
||||
onPressed: () {
|
||||
Navigator.of(ctx).pop();
|
||||
if (success) {
|
||||
Navigator.of(_context!).pop(true); // Return success
|
||||
}
|
||||
ScaffoldMessenger.of(_context!).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(success
|
||||
? "Payment verified successfully!"
|
||||
: "Payment failed or could not be verified."),
|
||||
backgroundColor: success ? Colors.green : Colors.red,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
|
||||
@ -102,6 +102,6 @@ class ApiEndpoints {
|
||||
static const String getAssignedServices = "/Project/get/assigned/services";
|
||||
|
||||
// Payment Module API Endpoints
|
||||
static const String createOrder = "/api/payment/create-order";
|
||||
static const String verifyPayment = "/api/payment/verify-payment";
|
||||
static const String createOrder = "/payment/create-order";
|
||||
static const String verifyPayment = "/payment/verify-payment";
|
||||
}
|
||||
|
||||
@ -75,10 +75,17 @@ class ApiService {
|
||||
return token;
|
||||
}
|
||||
|
||||
static Map<String, String> _headers(String token) => {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
};
|
||||
static Map<String, String> _headers(String? token) {
|
||||
final headers = {
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
// 👇 Only add Authorization header if token is available
|
||||
if (token != null && token.isNotEmpty) {
|
||||
headers['Authorization'] = 'Bearer $token';
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
static void _log(String message) {
|
||||
if (enableLogs) logSafe(message);
|
||||
@ -123,32 +130,49 @@ class ApiService {
|
||||
String endpoint, {
|
||||
Map<String, String>? queryParams,
|
||||
bool hasRetried = false,
|
||||
bool requireAuth = true,
|
||||
}) async {
|
||||
String? token = await _getToken();
|
||||
if (token == null) {
|
||||
logSafe("Token is null. Forcing logout from GET request.",
|
||||
level: LogLevel.error);
|
||||
await LocalStorage.logout();
|
||||
return null;
|
||||
// ✅ Allow public (no-login) API calls for subscription & payment
|
||||
final isPublicEndpoint =
|
||||
endpoint.contains("/subscription") || endpoint.contains("/payment");
|
||||
|
||||
String? token;
|
||||
if (requireAuth && !isPublicEndpoint) {
|
||||
token = await _getToken();
|
||||
if (token == null) {
|
||||
logSafe("⛔ Token is null. Forcing logout from GET request.",
|
||||
level: LogLevel.error);
|
||||
await LocalStorage.logout();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
final uri = Uri.parse("${ApiEndpoints.baseUrl}$endpoint")
|
||||
.replace(queryParameters: queryParams);
|
||||
|
||||
logSafe("Initiating GET request", level: LogLevel.debug);
|
||||
logSafe("🌐 Initiating GET request", level: LogLevel.debug);
|
||||
logSafe("URL: $uri", level: LogLevel.debug);
|
||||
logSafe("Query Parameters: ${queryParams ?? {}}", level: LogLevel.debug);
|
||||
logSafe("Headers: ${_headers(token)}", level: LogLevel.debug);
|
||||
|
||||
try {
|
||||
final response = await http
|
||||
.get(uri, headers: _headers(token))
|
||||
.get(
|
||||
uri,
|
||||
headers: (isPublicEndpoint || !requireAuth)
|
||||
? _headers(null)
|
||||
: _headers(token),
|
||||
)
|
||||
.timeout(extendedTimeout);
|
||||
|
||||
logSafe("Response Status: ${response.statusCode}", level: LogLevel.debug);
|
||||
logSafe("Response Body: ${response.body}", level: LogLevel.debug);
|
||||
|
||||
if (response.statusCode == 401 && !hasRetried) {
|
||||
// 🔒 Retry only for private endpoints
|
||||
if (response.statusCode == 401 &&
|
||||
!hasRetried &&
|
||||
requireAuth &&
|
||||
!isPublicEndpoint) {
|
||||
logSafe("Unauthorized (401). Attempting token refresh...",
|
||||
level: LogLevel.warning);
|
||||
|
||||
@ -159,6 +183,7 @@ class ApiService {
|
||||
endpoint,
|
||||
queryParams: queryParams,
|
||||
hasRetried: true,
|
||||
requireAuth: requireAuth,
|
||||
);
|
||||
}
|
||||
|
||||
@ -176,33 +201,68 @@ class ApiService {
|
||||
|
||||
static Future<http.Response?> _postRequest(
|
||||
String endpoint,
|
||||
dynamic body, {
|
||||
Duration customTimeout = extendedTimeout,
|
||||
dynamic data, {
|
||||
// <-- changed from Map<String, dynamic> to dynamic
|
||||
Map<String, String>? queryParams,
|
||||
bool hasRetried = false,
|
||||
bool requireAuth = true,
|
||||
Duration? customTimeout,
|
||||
}) async {
|
||||
String? token = await _getToken();
|
||||
if (token == null) return null;
|
||||
// ✅ Allow public (no-login) API calls for subscription & payment
|
||||
final isPublicEndpoint =
|
||||
endpoint.contains("/subscription") || endpoint.contains("/payment");
|
||||
|
||||
final uri = Uri.parse("${ApiEndpoints.baseUrl}$endpoint");
|
||||
logSafe(
|
||||
"POST $uri\nHeaders: ${_headers(token)}\nBody: $body",
|
||||
);
|
||||
String? token = await _getToken();
|
||||
|
||||
if (token == null && requireAuth && !isPublicEndpoint) {
|
||||
logSafe("⛔ Token missing for private POST: $endpoint",
|
||||
level: LogLevel.error);
|
||||
await LocalStorage.logout();
|
||||
return null;
|
||||
}
|
||||
|
||||
final uri = Uri.parse("${ApiEndpoints.baseUrl}$endpoint")
|
||||
.replace(queryParameters: queryParams);
|
||||
|
||||
logSafe("🌐 POST $uri", level: LogLevel.debug);
|
||||
logSafe("Headers: ${_headers(token)}", level: LogLevel.debug);
|
||||
logSafe("Body: $data", level: LogLevel.debug);
|
||||
|
||||
try {
|
||||
final response = await http
|
||||
.post(uri, headers: _headers(token), body: jsonEncode(body))
|
||||
.timeout(customTimeout);
|
||||
.post(
|
||||
uri,
|
||||
headers: (isPublicEndpoint || !requireAuth)
|
||||
? _headers(null)
|
||||
: _headers(token),
|
||||
body: jsonEncode(data), // handles both Map and List
|
||||
)
|
||||
.timeout(customTimeout ?? extendedTimeout);
|
||||
|
||||
if (response.statusCode == 401 && !hasRetried) {
|
||||
logSafe("Unauthorized POST. Attempting token refresh...");
|
||||
logSafe("Response ${response.statusCode}: ${response.body}",
|
||||
level: LogLevel.debug);
|
||||
|
||||
// Retry token refresh for private routes only
|
||||
if (response.statusCode == 401 &&
|
||||
!hasRetried &&
|
||||
requireAuth &&
|
||||
!isPublicEndpoint) {
|
||||
if (await AuthService.refreshToken()) {
|
||||
return await _postRequest(endpoint, body,
|
||||
customTimeout: customTimeout, hasRetried: true);
|
||||
return await _postRequest(
|
||||
endpoint,
|
||||
data,
|
||||
queryParams: queryParams,
|
||||
hasRetried: true,
|
||||
requireAuth: requireAuth,
|
||||
customTimeout: customTimeout,
|
||||
);
|
||||
}
|
||||
await LocalStorage.logout();
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (e) {
|
||||
logSafe("HTTP POST Exception: $e", level: LogLevel.error);
|
||||
logSafe("❌ HTTP POST Exception: $e", level: LogLevel.error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -1963,11 +2023,12 @@ class ApiService {
|
||||
static Future<Map<String, dynamic>?> getSubscriptionPlans(
|
||||
String frequency) async {
|
||||
try {
|
||||
final endpoint =
|
||||
"/api/market/list/subscription-plan?frequency=$frequency";
|
||||
final endpoint = "/market/list/subscription-plan?frequency=$frequency";
|
||||
logSafe("Fetching subscription plans for frequency: $frequency");
|
||||
|
||||
final response = await _getRequest(endpoint);
|
||||
// 👇 Pass `requireAuth: false` to make this API public
|
||||
final response = await _getRequest(endpoint, requireAuth: false);
|
||||
|
||||
if (response == null) {
|
||||
logSafe("Subscription plans request failed: null response",
|
||||
level: LogLevel.error);
|
||||
@ -2135,11 +2196,18 @@ class ApiService {
|
||||
static Future<Map<String, dynamic>?> createPaymentOrder(double amount) async {
|
||||
const endpoint = ApiEndpoints.createOrder; // endpoint for order creation
|
||||
try {
|
||||
final response = await _postRequest(endpoint, {'amount': amount});
|
||||
// ✅ Allow this API call without requiring login/token
|
||||
final response = await _postRequest(
|
||||
endpoint,
|
||||
{'amount': amount},
|
||||
requireAuth: false, // 👈 this is the key change
|
||||
);
|
||||
|
||||
if (response == null) return null;
|
||||
return _parseResponse(response, label: "Create Payment Order");
|
||||
} catch (e) {
|
||||
logSafe("Exception during createPaymentOrder: $e", level: LogLevel.error);
|
||||
logSafe("❌ Exception during createPaymentOrder: $e",
|
||||
level: LogLevel.error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -2152,11 +2220,15 @@ class ApiService {
|
||||
}) async {
|
||||
const endpoint = ApiEndpoints.verifyPayment;
|
||||
try {
|
||||
final response = await _postRequest(endpoint, {
|
||||
'orderId': orderId,
|
||||
'paymentId': paymentId,
|
||||
'signature': signature,
|
||||
});
|
||||
final response = await _postRequest(
|
||||
endpoint,
|
||||
{
|
||||
'orderId': orderId,
|
||||
'paymentId': paymentId,
|
||||
'signature': signature,
|
||||
},
|
||||
requireAuth: false,
|
||||
);
|
||||
if (response == null) return null;
|
||||
return _parseResponse(response, label: "Verify Payment");
|
||||
} catch (e) {
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
// payment_service.dart
|
||||
import 'package:marco/helpers/services/api_service.dart';
|
||||
import 'package:marco/helpers/services/app_logger.dart';
|
||||
|
||||
|
||||
204
lib/routes.dart
204
lib/routes.dart
@ -25,112 +25,120 @@ import 'package:marco/view/payment/payment_screen.dart';
|
||||
import 'package:marco/view/subscriptions/subscriptions_screen.dart';
|
||||
|
||||
class AuthMiddleware extends GetMiddleware {
|
||||
@override
|
||||
RouteSettings? redirect(String? route) {
|
||||
if (!AuthService.isLoggedIn) {
|
||||
if (route != '/auth/login-option') {
|
||||
return const RouteSettings(name: '/auth/login-option');
|
||||
}
|
||||
} else if (!TenantService.isTenantSelected) {
|
||||
if (route != '/select-tenant') {
|
||||
return const RouteSettings(name: '/select-tenant');
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@override
|
||||
RouteSettings? redirect(String? route) {
|
||||
// Public routes (no auth required)
|
||||
const publicRoutes = [
|
||||
'/auth/login-option',
|
||||
'/auth/login',
|
||||
'/subscription', // 👈 Allow this route without auth
|
||||
'/payment',
|
||||
'/select-tenant',
|
||||
];
|
||||
|
||||
// Skip auth checks for public routes
|
||||
if (publicRoutes.contains(route)) return null;
|
||||
|
||||
if (!AuthService.isLoggedIn) {
|
||||
return const RouteSettings(name: '/auth/login-option');
|
||||
}
|
||||
|
||||
if (!TenantService.isTenantSelected) {
|
||||
return const RouteSettings(name: '/select-tenant');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
getPageRoute() {
|
||||
var routes = [
|
||||
GetPage(
|
||||
name: '/',
|
||||
page: () => DashboardScreen(),
|
||||
middlewares: [AuthMiddleware()]),
|
||||
GetPage(
|
||||
name: '/dashboard',
|
||||
page: () => DashboardScreen(), // or your actual home screen
|
||||
middlewares: [AuthMiddleware()],
|
||||
),
|
||||
GetPage(
|
||||
name: '/select-tenant',
|
||||
page: () => const TenantSelectionScreen(),
|
||||
middlewares: [AuthMiddleware()]),
|
||||
var routes = [
|
||||
GetPage(
|
||||
name: '/',
|
||||
page: () => DashboardScreen(),
|
||||
middlewares: [AuthMiddleware()]),
|
||||
GetPage(
|
||||
name: '/dashboard',
|
||||
page: () => DashboardScreen(), // or your actual home screen
|
||||
middlewares: [AuthMiddleware()],
|
||||
),
|
||||
GetPage(
|
||||
name: '/select-tenant',
|
||||
page: () => const TenantSelectionScreen(),
|
||||
middlewares: [AuthMiddleware()]),
|
||||
|
||||
// Dashboard
|
||||
GetPage(
|
||||
name: '/dashboard/attendance',
|
||||
page: () => AttendanceScreen(),
|
||||
middlewares: [AuthMiddleware()]),
|
||||
GetPage(
|
||||
name: '/dashboard',
|
||||
page: () => DashboardScreen(),
|
||||
middlewares: [AuthMiddleware()]),
|
||||
GetPage(
|
||||
name: '/dashboard/employees',
|
||||
page: () => EmployeesScreen(),
|
||||
middlewares: [AuthMiddleware()]),
|
||||
GetPage(
|
||||
name: '/dashboard/attendance',
|
||||
page: () => AttendanceScreen(),
|
||||
middlewares: [AuthMiddleware()]),
|
||||
// GetPage(
|
||||
// name: '/dashboard',
|
||||
// page: () => DashboardScreen(),
|
||||
// middlewares: [AuthMiddleware()]),
|
||||
GetPage(
|
||||
name: '/dashboard/employees',
|
||||
page: () => EmployeesScreen(),
|
||||
middlewares: [AuthMiddleware()]),
|
||||
// Daily Task Planning
|
||||
GetPage(
|
||||
name: '/dashboard/daily-task-Planning',
|
||||
page: () => DailyTaskPlanningScreen(),
|
||||
middlewares: [AuthMiddleware()]),
|
||||
GetPage(
|
||||
name: '/dashboard/daily-task-progress',
|
||||
page: () => DailyProgressReportScreen(),
|
||||
middlewares: [AuthMiddleware()]),
|
||||
GetPage(
|
||||
name: '/dashboard/directory-main-page',
|
||||
page: () => DirectoryMainScreen(),
|
||||
middlewares: [AuthMiddleware()]),
|
||||
GetPage(
|
||||
name: '/dashboard/daily-task-Planning',
|
||||
page: () => DailyTaskPlanningScreen(),
|
||||
middlewares: [AuthMiddleware()]),
|
||||
GetPage(
|
||||
name: '/dashboard/daily-task-progress',
|
||||
page: () => DailyProgressReportScreen(),
|
||||
middlewares: [AuthMiddleware()]),
|
||||
GetPage(
|
||||
name: '/dashboard/directory-main-page',
|
||||
page: () => DirectoryMainScreen(),
|
||||
middlewares: [AuthMiddleware()]),
|
||||
// Expense
|
||||
GetPage(
|
||||
name: '/dashboard/expense-main-page',
|
||||
page: () => ExpenseMainScreen(),
|
||||
middlewares: [AuthMiddleware()]),
|
||||
GetPage(
|
||||
name: '/dashboard/expense-main-page',
|
||||
page: () => ExpenseMainScreen(),
|
||||
middlewares: [AuthMiddleware()]),
|
||||
// Documents
|
||||
GetPage(
|
||||
name: '/dashboard/document-main-page',
|
||||
page: () => UserDocumentsPage(),
|
||||
middlewares: [AuthMiddleware()]),
|
||||
GetPage(
|
||||
name: '/dashboard/document-main-page',
|
||||
page: () => UserDocumentsPage(),
|
||||
middlewares: [AuthMiddleware()]),
|
||||
// Payment
|
||||
GetPage(
|
||||
name: '/dashboard/payment',
|
||||
page: () => PaymentScreen(),
|
||||
middlewares: [AuthMiddleware()]),
|
||||
GetPage(
|
||||
name: '/subscription',
|
||||
page: () => SubscriptionScreen(),
|
||||
middlewares: [AuthMiddleware()]),
|
||||
GetPage(name: '/payment', page: () => PaymentScreen()),
|
||||
GetPage(
|
||||
name: '/subscription',
|
||||
page: () => SubscriptionScreen(),
|
||||
),
|
||||
// Authentication
|
||||
GetPage(name: '/auth/login', page: () => LoginScreen()),
|
||||
GetPage(name: '/auth/login-option', page: () => LoginOptionScreen()),
|
||||
GetPage(name: '/auth/mpin', page: () => MPINScreen()),
|
||||
GetPage(name: '/auth/mpin-auth', page: () => MPINAuthScreen()),
|
||||
GetPage(
|
||||
name: '/auth/register_account',
|
||||
page: () => const RegisterAccountScreen()),
|
||||
GetPage(name: '/auth/forgot_password', page: () => ForgotPasswordScreen()),
|
||||
GetPage(
|
||||
name: '/auth/reset_password', page: () => const ResetPasswordScreen()),
|
||||
GetPage(name: '/auth/login', page: () => LoginScreen()),
|
||||
GetPage(name: '/auth/login-option', page: () => LoginOptionScreen()),
|
||||
GetPage(name: '/auth/mpin', page: () => MPINScreen()),
|
||||
GetPage(name: '/auth/mpin-auth', page: () => MPINAuthScreen()),
|
||||
GetPage(
|
||||
name: '/auth/register_account',
|
||||
page: () => const RegisterAccountScreen()),
|
||||
GetPage(name: '/auth/forgot_password', page: () => ForgotPasswordScreen()),
|
||||
GetPage(
|
||||
name: '/auth/reset_password', page: () => const ResetPasswordScreen()),
|
||||
// Error
|
||||
GetPage(
|
||||
name: '/error/coming_soon',
|
||||
page: () => ComingSoonScreen(),
|
||||
middlewares: [AuthMiddleware()]),
|
||||
GetPage(
|
||||
name: '/error/500',
|
||||
page: () => Error500Screen(),
|
||||
middlewares: [AuthMiddleware()]),
|
||||
GetPage(
|
||||
name: '/error/404',
|
||||
page: () => Error404Screen(),
|
||||
middlewares: [AuthMiddleware()]),
|
||||
];
|
||||
return routes
|
||||
.map((e) => GetPage(
|
||||
name: e.name,
|
||||
page: e.page,
|
||||
middlewares: e.middlewares,
|
||||
transition: Transition.noTransition))
|
||||
.toList();
|
||||
}
|
||||
GetPage(
|
||||
name: '/error/coming_soon',
|
||||
page: () => ComingSoonScreen(),
|
||||
middlewares: [AuthMiddleware()]),
|
||||
GetPage(
|
||||
name: '/error/500',
|
||||
page: () => Error500Screen(),
|
||||
middlewares: [AuthMiddleware()]),
|
||||
GetPage(
|
||||
name: '/error/404',
|
||||
page: () => Error404Screen(),
|
||||
middlewares: [AuthMiddleware()]),
|
||||
];
|
||||
return routes
|
||||
.map((e) => GetPage(
|
||||
name: e.name,
|
||||
page: e.page,
|
||||
middlewares: e.middlewares,
|
||||
transition: Transition.noTransition))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@ -7,6 +7,8 @@ import 'package:marco/view/auth/otp_login_form.dart';
|
||||
import 'package:marco/helpers/services/api_endpoints.dart';
|
||||
import 'package:marco/view/auth/request_demo_bottom_sheet.dart';
|
||||
import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
|
||||
enum LoginOption { email, otp }
|
||||
|
||||
@ -133,6 +135,13 @@ class _WelcomeScreenState extends State<WelcomeScreen>
|
||||
option: LoginOption.otp,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildActionButton(
|
||||
context,
|
||||
label: "Subscribe",
|
||||
icon: LucideIcons.bell,
|
||||
option: null,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildActionButton(
|
||||
context,
|
||||
label: "Request a Demo",
|
||||
@ -237,6 +246,10 @@ class _WelcomeScreenState extends State<WelcomeScreen>
|
||||
shadowColor: Colors.black26,
|
||||
),
|
||||
onPressed: () {
|
||||
if (label == "Subscribe") {
|
||||
Get.toNamed('/subscription'); // Navigate to Subscription screen
|
||||
return;
|
||||
}
|
||||
if (option == null) {
|
||||
OrganizationFormBottomSheet.show(context);
|
||||
} else {
|
||||
|
||||
@ -303,14 +303,14 @@ class _UserProfileBarState extends State<UserProfileBar>
|
||||
onTap: _onProfileTap,
|
||||
),
|
||||
SizedBox(height: spacingHeight),
|
||||
_menuItemRow(
|
||||
icon: LucideIcons.bell,
|
||||
label: 'Subscribe',
|
||||
onTap: _onSubscribeTap,
|
||||
iconColor: Colors.redAccent,
|
||||
textColor: Colors.redAccent,
|
||||
),
|
||||
SizedBox(height: spacingHeight),
|
||||
// _menuItemRow(
|
||||
// icon: LucideIcons.bell,
|
||||
// label: 'Subscribe',
|
||||
// onTap: _onSubscribeTap,
|
||||
// iconColor: Colors.redAccent,
|
||||
// textColor: Colors.redAccent,
|
||||
// ),
|
||||
// SizedBox(height: spacingHeight),
|
||||
_menuItemRow(
|
||||
icon: LucideIcons.settings,
|
||||
label: 'Settings',
|
||||
@ -380,9 +380,9 @@ class _UserProfileBarState extends State<UserProfileBar>
|
||||
));
|
||||
}
|
||||
|
||||
void _onSubscribeTap() {
|
||||
Get.toNamed("/subscription");
|
||||
}
|
||||
// void _onSubscribeTap() {
|
||||
// Get.toNamed("/subscription");
|
||||
// }
|
||||
|
||||
|
||||
void _onMpinTap() {
|
||||
|
||||
@ -10,7 +10,7 @@ class PaymentScreen extends StatefulWidget {
|
||||
const PaymentScreen({
|
||||
super.key,
|
||||
this.amount = 0.0,
|
||||
this.description = "No description",
|
||||
this.description = "No description",
|
||||
});
|
||||
|
||||
@override
|
||||
@ -66,8 +66,8 @@ class _PaymentScreenState extends State<PaymentScreen> {
|
||||
final controller = _controller!;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text("Payment"),
|
||||
backgroundColor: Colors.blueAccent,
|
||||
title: const Text("Payment", style: TextStyle(color: Colors.black),),
|
||||
backgroundColor: Colors.white,
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user