marco.pms.mobileapp/lib/view/subscriptions/current_plan_screen.dart

217 lines
8.1 KiB
Dart

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:marco/helpers/services/api_service.dart';
import 'package:marco/helpers/services/app_logger.dart';
class CurrentPlanScreen extends StatefulWidget {
const CurrentPlanScreen({super.key});
@override
State<CurrentPlanScreen> createState() => _CurrentPlanScreenState();
}
class _CurrentPlanScreenState extends State<CurrentPlanScreen> {
bool _loading = true;
Map<String, dynamic>? _plan;
@override
void initState() {
super.initState();
_fetchCurrentPlan();
}
Future<void> _fetchCurrentPlan() async {
try {
setState(() => _loading = true);
final resp = await ApiService.getCurrentSubscription();
logSafe("💡 Get Current Subscription Response: $resp");
if (resp == null || resp['success'] != true) {
setState(() {
_plan = null;
_loading = false;
});
return;
}
final data = resp['data'] ?? resp;
setState(() {
_plan = data is Map ? Map<String, dynamic>.from(data) : null;
_loading = false;
});
} catch (e, s) {
logSafe("❌ Exception while fetching current plan: $e\n$s",
level: LogLevel.error);
if (mounted) setState(() => _loading = false);
}
}
Widget _buildPlaceholder() {
return Center(
child: Column(mainAxisSize: MainAxisSize.min, children: [
const Icon(Icons.info_outline, size: 48, color: Colors.grey),
const SizedBox(height: 12),
const Text("No active subscription",
style: TextStyle(fontSize: 16, color: Colors.grey)),
const SizedBox(height: 8),
ElevatedButton(
onPressed: () => Get.toNamed('/subscription'),
child: const Text("View Plans"),
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all(Colors.green),
),
),
]),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Current Plan",
style: TextStyle(fontWeight: FontWeight.w600, color: Colors.black)),
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios_new),
onPressed: () => Get.back(),
),
),
body: _loading
? const Center(child: CircularProgressIndicator())
: Padding(
padding: const EdgeInsets.all(16),
child: _plan == null
? _buildPlaceholder()
: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
elevation: 3,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_planDisplayName(_plan!),
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
Text(_plan!['description'] ?? '',
style:
const TextStyle(color: Colors.grey)),
const SizedBox(height: 12),
Row(
children: [
Text("Price: ",
style: TextStyle(
color: Colors.grey[700])),
Text(
"${_currencySymbol(_plan!)}${_plan!['price'] ?? 0}",
style: const TextStyle(
fontWeight: FontWeight.bold)),
const Spacer(),
Text(
"Trial: ${_plan!['trialDays'] ?? 0} days",
style: const TextStyle(
color: Colors.grey)),
],
),
const SizedBox(height: 12),
if (_plan!['startedAt'] != null)
Text("Started: ${_plan!['startedAt']}",
style: const TextStyle(
color: Colors.grey)),
if (_plan!['expiresAt'] != null)
Text("Expires: ${_plan!['expiresAt']}",
style: const TextStyle(
color: Colors.grey)),
]),
),
),
const SizedBox(height: 16),
// Features / modules (if present)
if (_plan!['features'] != null)
Expanded(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: _buildFeaturesList(_plan!),
),
),
)
else
const SizedBox.shrink(),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () {
// navigate to subscription page (renew flow)
// pass current plan id so subscription screen can preselect if needed
Get.toNamed('/subscription',
arguments: {'currentPlanId': _plan!['id']});
},
style: ElevatedButton.styleFrom(
padding:
const EdgeInsets.symmetric(vertical: 14)),
child: const Text("Renew Plan"),
),
],
),
),
);
}
String _planDisplayName(Map<String, dynamic> plan) {
return plan['planName'] ?? plan['name'] ?? 'Plan';
}
String _currencySymbol(Map<String, dynamic> plan) {
try {
return plan['currency']?['symbol'] ?? '';
} catch (_) {
return '';
}
}
List<Widget> _buildFeaturesList(Map<String, dynamic> plan) {
final features = <String>[];
try {
final modules = plan['features']?['modules'] ?? {};
if (modules is Map) {
modules.forEach((k, v) {
if (v is Map && v['enabled'] == true) features.add(v['name'] ?? k);
});
}
final supports = plan['features']?['supports'] ?? {};
if (supports is Map) {
supports.forEach((k, v) {
if (v == true)
features.add(
k.toString().replaceAll(RegExp(r'([a-z])([A-Z])'), r'\1 \2'));
});
}
} catch (_) {}
if (features.isEmpty) return [const SizedBox.shrink()];
return [
const SizedBox(height: 12),
const Text("Included features",
style: TextStyle(fontWeight: FontWeight.w600)),
const SizedBox(height: 8),
...features.map((f) => Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(children: [
const Icon(Icons.check, size: 16, color: Colors.green),
const SizedBox(width: 8),
Expanded(child: Text(f))
]),
)),
];
}
}