feat: Implement tenant selection logic and load saved tenant from local storage

This commit is contained in:
Vaibhav Surve 2025-09-25 11:27:03 +05:30
parent 53fefbba50
commit 1900e944e5
4 changed files with 130 additions and 71 deletions

View File

@ -1,6 +1,8 @@
import 'dart:convert';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:marco/helpers/services/app_logger.dart'; import 'package:marco/helpers/services/app_logger.dart';
import 'package:marco/helpers/services/tenant_service.dart'; import 'package:marco/helpers/services/tenant_service.dart';
import 'package:marco/helpers/services/storage/local_storage.dart';
import 'package:marco/model/tenant/tenant_list_model.dart'; import 'package:marco/model/tenant/tenant_list_model.dart';
import 'package:marco/helpers/widgets/my_snackbar.dart'; import 'package:marco/helpers/widgets/my_snackbar.dart';
@ -13,18 +15,36 @@ class TenantSelectionController extends GetxController {
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
loadTenants(); _checkExistingTenant();
}
/// Check if a tenant was previously selected
Future<void> _checkExistingTenant() async {
try {
final savedTenant = await TenantService.loadSavedTenant();
if (savedTenant != null) {
// Tenant already selected go to dashboard
Get.offAllNamed('/dashboard');
} else {
// No tenant saved load from API
await loadTenants();
}
} catch (e, st) {
logSafe("❌ Error checking saved tenant",
level: LogLevel.error, error: e, stackTrace: st);
await loadTenants();
}
} }
/// Load tenants from API /// Load tenants from API
Future<void> loadTenants() async { Future<void> loadTenants() async {
isLoading.value = true;
try { try {
isLoading.value = true;
final data = await _tenantService.getTenants(); final data = await _tenantService.getTenants();
if (data != null) { if (data != null && data.isNotEmpty) {
tenants.value = data.map((e) => Tenant.fromJson(e)).toList(); tenants.value = data.map((e) => Tenant.fromJson(e)).toList();
// Automatically select if only one tenant // Auto-select if only one tenant
if (tenants.length == 1) { if (tenants.length == 1) {
await onTenantSelected(tenants.first.id); await onTenantSelected(tenants.first.id);
} }
@ -35,37 +55,34 @@ class TenantSelectionController extends GetxController {
} catch (e, st) { } catch (e, st) {
logSafe("❌ Exception in loadTenants", logSafe("❌ Exception in loadTenants",
level: LogLevel.error, error: e, stackTrace: st); level: LogLevel.error, error: e, stackTrace: st);
tenants.clear();
} finally { } finally {
isLoading.value = false; isLoading.value = false;
} }
} }
/// Select tenant /// Handle tenant selection
Future<void> onTenantSelected(String tenantId) async { Future<void> onTenantSelected(String tenantId) async {
isLoading.value = true;
try { try {
isLoading.value = true;
final success = await _tenantService.selectTenant(tenantId); final success = await _tenantService.selectTenant(tenantId);
if (success) { if (success) {
logSafe("✅ Tenant selection successful: $tenantId"); final tenant = tenants.firstWhere((t) => t.id == tenantId);
// Store selected tenant in memory // Save selected tenant in TenantService and LocalStorage
TenantService.setSelectedTenant( TenantService.setSelectedTenant(tenant);
tenants.firstWhere((t) => t.id == tenantId)); await LocalStorage.saveString(
'selectedTenant', jsonEncode(tenant.toJson()));
// Navigate to dashboard/home // Navigate to dashboard
Get.offAllNamed('/dashboard'); Get.offAllNamed('/dashboard');
// Optional: show success snackbar
showAppSnackbar( showAppSnackbar(
title: "Success", title: "Success",
message: "Organization selected successfully.", message: "Organization selected successfully.",
type: SnackbarType.success, type: SnackbarType.success,
); );
} else { } else {
logSafe("❌ Tenant selection failed for: $tenantId",
level: LogLevel.warning);
// Show error snackbar
showAppSnackbar( showAppSnackbar(
title: "Error", title: "Error",
message: "Unable to select organization. Please try again.", message: "Unable to select organization. Please try again.",
@ -76,7 +93,6 @@ class TenantSelectionController extends GetxController {
logSafe("❌ Exception in onTenantSelected", logSafe("❌ Exception in onTenantSelected",
level: LogLevel.error, error: e, stackTrace: st); level: LogLevel.error, error: e, stackTrace: st);
// Show error snackbar for exception
showAppSnackbar( showAppSnackbar(
title: "Error", title: "Error",
message: "An unexpected error occurred while selecting organization.", message: "An unexpected error occurred while selecting organization.",

View File

@ -97,6 +97,22 @@ class TenantService implements ITenantService {
} }
} }
/// Load previously selected tenant from local storage
static Future<Tenant?> loadSavedTenant() async {
final jsonString = LocalStorage.getString('selectedTenant');
if (jsonString == null) return null;
try {
final tenantMap = jsonDecode(jsonString) as Map<String, dynamic>;
final tenant = Tenant.fromJson(tenantMap);
currentTenant = tenant;
return tenant;
} catch (e) {
logSafe("❌ Failed to load saved tenant: $e", level: LogLevel.warning);
return null;
}
}
@override @override
Future<bool> selectTenant(String tenantId, {bool hasRetried = false}) async { Future<bool> selectTenant(String tenantId, {bool hasRetried = false}) async {
try { try {

View File

@ -32,8 +32,9 @@ class Tenant {
contactName: json['contactName'] ?? '', contactName: json['contactName'] ?? '',
contactNumber: json['contactNumber'] ?? '', contactNumber: json['contactNumber'] ?? '',
logoImage: json['logoImage'] is String ? json['logoImage'] : null, logoImage: json['logoImage'] is String ? json['logoImage'] : null,
organizationSize: organizationSize: json['organizationSize'] is String
json['organizationSize'] is String ? json['organizationSize'] : null, ? json['organizationSize']
: null,
industry: json['industry'] != null industry: json['industry'] != null
? Industry.fromJson(json['industry']) ? Industry.fromJson(json['industry'])
: null, : null,
@ -42,6 +43,21 @@ class Tenant {
: null, : null,
); );
} }
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'email': email,
'domainName': domainName,
'contactName': contactName,
'contactNumber': contactNumber,
'logoImage': logoImage,
'organizationSize': organizationSize,
'industry': industry?.toJson(),
'tenantStatus': tenantStatus?.toJson(),
};
}
} }
class Industry { class Industry {
@ -59,6 +75,13 @@ class Industry {
name: json['name'] ?? '', name: json['name'] ?? '',
); );
} }
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
};
}
} }
class TenantStatus { class TenantStatus {
@ -76,4 +99,11 @@ class TenantStatus {
name: json['name'] ?? '', name: json['name'] ?? '',
); );
} }
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
};
}
} }

View File

@ -165,7 +165,7 @@ class _BetaBadge extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.orangeAccent, color: Colors.orangeAccent,
borderRadius: BorderRadius.circular(6), borderRadius: BorderRadius.circular(5),
), ),
child: MyText( child: MyText(
'BETA', 'BETA',
@ -246,59 +246,56 @@ class _TenantCard extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Card( return InkWell(
elevation: 3, onTap: onTap,
shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(5),
borderRadius: BorderRadius.circular(12), child: Card(
), elevation: 3,
margin: const EdgeInsets.only(bottom: 20), shape: RoundedRectangleBorder(
child: Padding( borderRadius: BorderRadius.circular(5),
padding: const EdgeInsets.all(16), ),
child: Row( margin: const EdgeInsets.only(bottom: 20),
crossAxisAlignment: CrossAxisAlignment.start, child: Padding(
children: [ padding: const EdgeInsets.all(16),
ClipRRect( child: Row(
borderRadius: BorderRadius.circular(8), crossAxisAlignment: CrossAxisAlignment.start,
child: Container( children: [
width: 60, ClipRRect(
height: 60, borderRadius: BorderRadius.circular(5),
color: Colors.grey.shade200, child: Container(
child: TenantLogo(logoImage: tenant.logoImage), width: 60,
), height: 60,
), color: Colors.grey.shade200,
const SizedBox(width: 16), child: TenantLogo(logoImage: tenant.logoImage),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MyText(
tenant.name,
fontSize: 18,
fontWeight: 700,
color: Colors.black87,
),
const SizedBox(height: 6),
MyText(
"Industry: ${tenant.industry?.name ?? "-"}",
fontSize: 13,
color: Colors.black54,
),
],
),
),
InkWell(
onTap: onTap,
child: Container(
height: 60,
alignment: Alignment.center,
child: Icon(
Icons.arrow_forward_ios,
size: 24,
color: Colors.red,
), ),
), ),
), const SizedBox(width: 16),
], Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MyText(
tenant.name,
fontSize: 18,
fontWeight: 700,
color: Colors.black87,
),
const SizedBox(height: 6),
MyText(
"Industry: ${tenant.industry?.name ?? "-"}",
fontSize: 13,
color: Colors.black54,
),
],
),
),
Icon(
Icons.arrow_forward_ios,
size: 24,
color: Colors.red,
),
],
),
), ),
), ),
); );