744 lines
23 KiB
Dart
744 lines
23 KiB
Dart
/// Account Page
|
|
///
|
|
/// Displays user account information and settings menu.
|
|
/// Features:
|
|
/// - User profile card with avatar, name, role, tier, and phone
|
|
/// - Account menu section (personal info, orders, addresses, etc.)
|
|
/// - Support section (contact, FAQ, about)
|
|
/// - Logout button
|
|
library;
|
|
|
|
import 'dart:async';
|
|
|
|
import 'package:cached_network_image/cached_network_image.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
import 'package:worker/core/constants/ui_constants.dart';
|
|
import 'package:worker/core/database/hive_initializer.dart';
|
|
import 'package:worker/core/database/models/enums.dart';
|
|
import 'package:worker/core/router/app_router.dart';
|
|
import 'package:worker/core/theme/colors.dart';
|
|
import 'package:worker/features/account/domain/entities/user_info.dart'
|
|
as domain;
|
|
import 'package:worker/features/account/presentation/providers/user_info_provider.dart'
|
|
hide UserInfo;
|
|
import 'package:worker/features/account/presentation/widgets/account_menu_item.dart';
|
|
import 'package:worker/features/auth/presentation/providers/auth_provider.dart';
|
|
|
|
/// Account Page
|
|
///
|
|
/// Main account/settings page accessible from the bottom navigation bar.
|
|
class AccountPage extends ConsumerWidget {
|
|
const AccountPage({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final colorScheme = Theme.of(context).colorScheme;
|
|
|
|
return Scaffold(
|
|
backgroundColor: colorScheme.surfaceContainerLowest,
|
|
body: SafeArea(
|
|
child: RefreshIndicator(
|
|
onRefresh: () async {
|
|
await ref.read(userInfoProvider.notifier).refresh();
|
|
},
|
|
child: SingleChildScrollView(
|
|
physics: const AlwaysScrollableScrollPhysics(),
|
|
child: Column(
|
|
children: [
|
|
// Simple Header
|
|
_buildHeader(context),
|
|
const SizedBox(height: AppSpacing.md),
|
|
|
|
// User Profile Card - only this depends on provider
|
|
const _ProfileCardSection(),
|
|
const SizedBox(height: AppSpacing.md),
|
|
|
|
// Account Menu Section - independent
|
|
_buildAccountMenu(context),
|
|
const SizedBox(height: AppSpacing.md),
|
|
|
|
// Support Section - independent
|
|
_buildSupportSection(context),
|
|
const SizedBox(height: AppSpacing.md),
|
|
|
|
// Logout Button - independent (uses ref only for logout action)
|
|
_LogoutButton(),
|
|
|
|
const SizedBox(height: AppSpacing.lg),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// Build simple header with title
|
|
Widget _buildHeader(BuildContext context) {
|
|
final colorScheme = Theme.of(context).colorScheme;
|
|
|
|
return Container(
|
|
width: double.infinity,
|
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
|
|
decoration: BoxDecoration(
|
|
color: colorScheme.surface,
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: colorScheme.shadow.withValues(alpha: 0.05),
|
|
blurRadius: 8,
|
|
offset: const Offset(0, 2),
|
|
),
|
|
],
|
|
),
|
|
child: Text(
|
|
'Tài khoản',
|
|
style: TextStyle(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.bold,
|
|
color: colorScheme.onSurface,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// Build account menu section
|
|
Widget _buildAccountMenu(BuildContext context) {
|
|
final colorScheme = Theme.of(context).colorScheme;
|
|
|
|
return Container(
|
|
margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
|
decoration: BoxDecoration(
|
|
color: colorScheme.surface,
|
|
borderRadius: BorderRadius.circular(AppRadius.card),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: colorScheme.shadow.withValues(alpha: 0.05),
|
|
blurRadius: 8,
|
|
offset: const Offset(0, 2),
|
|
),
|
|
],
|
|
),
|
|
child: Column(
|
|
children: [
|
|
AccountMenuItem(
|
|
icon: FontAwesomeIcons.penToSquare,
|
|
title: 'Thông tin cá nhân',
|
|
subtitle: 'Cập nhật thông tin tài khoản',
|
|
onTap: () {
|
|
context.push(RouteNames.profile);
|
|
},
|
|
),
|
|
AccountMenuItem(
|
|
icon: FontAwesomeIcons.clockRotateLeft,
|
|
title: 'Lịch sử đơn hàng',
|
|
subtitle: 'Xem các đơn hàng đã đặt',
|
|
onTap: () {
|
|
context.push(RouteNames.orders);
|
|
},
|
|
),
|
|
AccountMenuItem(
|
|
icon: FontAwesomeIcons.fileInvoiceDollar,
|
|
title: 'Hóa đơn đã mua',
|
|
subtitle: 'Xem các hóa đơn đã xuất',
|
|
onTap: () {
|
|
context.push(RouteNames.invoices);
|
|
},
|
|
),
|
|
AccountMenuItem(
|
|
icon: FontAwesomeIcons.locationDot,
|
|
title: 'Địa chỉ đã lưu',
|
|
subtitle: 'Quản lý địa chỉ giao hàng',
|
|
onTap: () {
|
|
context.push(RouteNames.addresses);
|
|
},
|
|
),
|
|
AccountMenuItem(
|
|
icon: FontAwesomeIcons.bell,
|
|
title: 'Cài đặt thông báo',
|
|
subtitle: 'Quản lý thông báo đẩy',
|
|
onTap: () {
|
|
_showComingSoon(context);
|
|
},
|
|
),
|
|
AccountMenuItem(
|
|
icon: FontAwesomeIcons.lock,
|
|
title: 'Đổi mật khẩu',
|
|
subtitle: 'Cập nhật mật khẩu mới',
|
|
onTap: () {
|
|
context.push(RouteNames.changePassword);
|
|
},
|
|
),
|
|
// AccountMenuItem(
|
|
// icon: FontAwesomeIcons.language,
|
|
// title: 'Ngôn ngữ',
|
|
// subtitle: 'Tiếng Việt',
|
|
// onTap: () {
|
|
// _showComingSoon(context);
|
|
// },
|
|
// ),
|
|
AccountMenuItem(
|
|
icon: FontAwesomeIcons.palette,
|
|
title: 'Giao diện',
|
|
subtitle: 'Màu sắc và chế độ hiển thị',
|
|
onTap: () {
|
|
context.push(RouteNames.themeSettings);
|
|
},
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
/// Build support section
|
|
Widget _buildSupportSection(BuildContext context) {
|
|
final colorScheme = Theme.of(context).colorScheme;
|
|
|
|
return Container(
|
|
margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
|
decoration: BoxDecoration(
|
|
color: colorScheme.surface,
|
|
borderRadius: BorderRadius.circular(AppRadius.card),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: colorScheme.shadow.withValues(alpha: 0.05),
|
|
blurRadius: 8,
|
|
offset: const Offset(0, 2),
|
|
),
|
|
],
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// Section title
|
|
Padding(
|
|
padding: const EdgeInsets.fromLTRB(
|
|
AppSpacing.md,
|
|
AppSpacing.md,
|
|
AppSpacing.md,
|
|
AppSpacing.sm,
|
|
),
|
|
child: Text(
|
|
'Hỗ trợ',
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w600,
|
|
color: colorScheme.onSurface,
|
|
),
|
|
),
|
|
),
|
|
|
|
// Support menu items
|
|
AccountMenuItem(
|
|
icon: FontAwesomeIcons.headset,
|
|
title: 'Liên hệ hỗ trợ',
|
|
subtitle: 'Hotline: 1900 1234',
|
|
trailing: FaIcon(
|
|
FontAwesomeIcons.phone,
|
|
size: 18,
|
|
color: colorScheme.onSurfaceVariant,
|
|
),
|
|
onTap: () {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(
|
|
content: Text('Hotline: 1900 1234'),
|
|
duration: Duration(seconds: 2),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
AccountMenuItem(
|
|
icon: FontAwesomeIcons.circleQuestion,
|
|
title: 'Câu hỏi thường gặp',
|
|
onTap: () {
|
|
_showComingSoon(context);
|
|
},
|
|
),
|
|
AccountMenuItem(
|
|
icon: FontAwesomeIcons.circleInfo,
|
|
title: 'Về ứng dụng',
|
|
subtitle: 'Phiên bản 1.0.0',
|
|
onTap: () {
|
|
_showAboutDialog(context);
|
|
},
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
/// Show coming soon message
|
|
void _showComingSoon(BuildContext context) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(
|
|
content: Text('Tính năng đang phát triển'),
|
|
duration: Duration(seconds: 1),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// Show about dialog
|
|
void _showAboutDialog(BuildContext context) {
|
|
showDialog<void>(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
title: const Text('Về ứng dụng'),
|
|
content: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
const Text(
|
|
'EuroTile & Vasta Stone Worker',
|
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
|
|
),
|
|
const SizedBox(height: 8),
|
|
const Text('Phiên bản: 1.0.0'),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
'Ứng dụng dành cho thầu thợ, kiến trúc sư, đại lý và môi giới trong ngành gạch ốp lát và nội thất.',
|
|
style: TextStyle(fontSize: 14, color: Theme.of(context).colorScheme.onSurfaceVariant),
|
|
),
|
|
],
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.of(context).pop(),
|
|
child: const Text('Đóng'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Profile Card Section Widget
|
|
///
|
|
/// Isolated widget that depends on userInfoProvider.
|
|
/// Shows loading/error/data states independently.
|
|
class _ProfileCardSection extends ConsumerWidget {
|
|
const _ProfileCardSection();
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final colorScheme = Theme.of(context).colorScheme;
|
|
final userInfoAsync = ref.watch(userInfoProvider);
|
|
|
|
return Container(
|
|
margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
|
child: userInfoAsync.when(
|
|
loading: () => _buildLoadingCard(colorScheme),
|
|
error: (error, stack) => _buildErrorCard(context, ref, error, colorScheme),
|
|
data: (userInfo) => _buildProfileCard(context, userInfo, colorScheme),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildLoadingCard(ColorScheme colorScheme) {
|
|
return Container(
|
|
padding: const EdgeInsets.all(AppSpacing.md),
|
|
decoration: BoxDecoration(
|
|
color: colorScheme.surface,
|
|
borderRadius: BorderRadius.circular(AppRadius.card),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: colorScheme.shadow.withValues(alpha: 0.05),
|
|
blurRadius: 8,
|
|
offset: const Offset(0, 2),
|
|
),
|
|
],
|
|
),
|
|
child: Row(
|
|
children: [
|
|
// Avatar placeholder
|
|
Container(
|
|
width: 80,
|
|
height: 80,
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
color: colorScheme.surfaceContainerHighest,
|
|
),
|
|
child: Center(
|
|
child: CircularProgressIndicator(
|
|
color: colorScheme.primary,
|
|
strokeWidth: 2,
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: AppSpacing.md),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Container(
|
|
height: 20,
|
|
width: 150,
|
|
decoration: BoxDecoration(
|
|
color: colorScheme.surfaceContainerHighest,
|
|
borderRadius: BorderRadius.circular(4),
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
Container(
|
|
height: 14,
|
|
width: 100,
|
|
decoration: BoxDecoration(
|
|
color: colorScheme.surfaceContainerHighest,
|
|
borderRadius: BorderRadius.circular(4),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildErrorCard(BuildContext context, WidgetRef ref, Object error, ColorScheme colorScheme) {
|
|
return Container(
|
|
padding: const EdgeInsets.all(AppSpacing.md),
|
|
decoration: BoxDecoration(
|
|
color: colorScheme.surface,
|
|
borderRadius: BorderRadius.circular(AppRadius.card),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: colorScheme.shadow.withValues(alpha: 0.05),
|
|
blurRadius: 8,
|
|
offset: const Offset(0, 2),
|
|
),
|
|
],
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Container(
|
|
width: 80,
|
|
height: 80,
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
color: colorScheme.surfaceContainerHighest,
|
|
),
|
|
child: const Center(
|
|
child: FaIcon(
|
|
FontAwesomeIcons.circleExclamation,
|
|
color: AppColors.danger,
|
|
size: 32,
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: AppSpacing.md),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'Không thể tải thông tin',
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w600,
|
|
color: colorScheme.onSurface,
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
GestureDetector(
|
|
onTap: () => ref.read(userInfoProvider.notifier).refresh(),
|
|
child: Text(
|
|
'Nhấn để thử lại',
|
|
style: TextStyle(
|
|
fontSize: 14,
|
|
color: colorScheme.primary,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildProfileCard(BuildContext context, domain.UserInfo userInfo, ColorScheme colorScheme) {
|
|
return Container(
|
|
padding: const EdgeInsets.all(AppSpacing.md),
|
|
decoration: BoxDecoration(
|
|
color: colorScheme.surface,
|
|
borderRadius: BorderRadius.circular(AppRadius.card),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: colorScheme.shadow.withValues(alpha: 0.05),
|
|
blurRadius: 8,
|
|
offset: const Offset(0, 2),
|
|
),
|
|
],
|
|
),
|
|
child: Row(
|
|
children: [
|
|
// Avatar with API data or gradient fallback
|
|
userInfo.avatarUrl != null
|
|
? ClipOval(
|
|
child: CachedNetworkImage(
|
|
imageUrl: userInfo.avatarUrl!,
|
|
width: 80,
|
|
height: 80,
|
|
fit: BoxFit.cover,
|
|
placeholder: (context, url) => Container(
|
|
width: 80,
|
|
height: 80,
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
color: colorScheme.primaryContainer,
|
|
),
|
|
child: Center(
|
|
child: CircularProgressIndicator(
|
|
color: colorScheme.onPrimaryContainer,
|
|
strokeWidth: 2,
|
|
),
|
|
),
|
|
),
|
|
errorWidget: (context, url, error) => Container(
|
|
width: 80,
|
|
height: 80,
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
color: colorScheme.primaryContainer,
|
|
),
|
|
child: Center(
|
|
child: Text(
|
|
userInfo.initials,
|
|
style: TextStyle(
|
|
color: colorScheme.onPrimaryContainer,
|
|
fontSize: 32,
|
|
fontWeight: FontWeight.w700,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
)
|
|
: Container(
|
|
width: 80,
|
|
height: 80,
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
color: colorScheme.primaryContainer,
|
|
),
|
|
child: Center(
|
|
child: Text(
|
|
userInfo.initials,
|
|
style: TextStyle(
|
|
color: colorScheme.onPrimaryContainer,
|
|
fontSize: 32,
|
|
fontWeight: FontWeight.w700,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: AppSpacing.md),
|
|
|
|
// User info from API
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
userInfo.fullName,
|
|
style: TextStyle(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.bold,
|
|
color: colorScheme.onSurface,
|
|
),
|
|
),
|
|
const SizedBox(height: AppSpacing.xs),
|
|
Text(
|
|
'${_getRoleDisplayName(userInfo.role)} · Hạng ${userInfo.tierDisplayName}',
|
|
style: TextStyle(
|
|
fontSize: 13,
|
|
color: colorScheme.onSurfaceVariant,
|
|
),
|
|
),
|
|
if (userInfo.phoneNumber != null) ...[
|
|
const SizedBox(height: AppSpacing.xs),
|
|
Text(
|
|
userInfo.phoneNumber!,
|
|
style: TextStyle(
|
|
fontSize: 13,
|
|
color: colorScheme.primary,
|
|
),
|
|
),
|
|
],
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
/// Get Vietnamese display name for user role
|
|
String _getRoleDisplayName(UserRole role) {
|
|
switch (role) {
|
|
case UserRole.customer:
|
|
return 'Khách hàng';
|
|
case UserRole.distributor:
|
|
return 'Đại lý phân phối';
|
|
case UserRole.admin:
|
|
return 'Quản trị viên';
|
|
case UserRole.staff:
|
|
return 'Nhân viên';
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Logout Button Widget
|
|
///
|
|
/// Isolated widget that handles logout functionality.
|
|
class _LogoutButton extends ConsumerWidget {
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final colorScheme = Theme.of(context).colorScheme;
|
|
|
|
return Container(
|
|
margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
|
width: double.infinity,
|
|
child: OutlinedButton.icon(
|
|
onPressed: () {
|
|
_showLogoutConfirmation(context, ref, colorScheme);
|
|
},
|
|
icon: const FaIcon(FontAwesomeIcons.arrowRightFromBracket, size: 18),
|
|
label: const Text('Đăng xuất'),
|
|
style: OutlinedButton.styleFrom(
|
|
foregroundColor: AppColors.danger,
|
|
side: const BorderSide(color: AppColors.danger, width: 1.5),
|
|
padding: const EdgeInsets.symmetric(vertical: 14),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(AppRadius.button),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// Show logout confirmation dialog
|
|
void _showLogoutConfirmation(BuildContext context, WidgetRef ref, ColorScheme colorScheme) {
|
|
showDialog<void>(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
backgroundColor: colorScheme.surface,
|
|
title: Text(
|
|
'Đăng xuất',
|
|
style: TextStyle(color: colorScheme.onSurface),
|
|
),
|
|
content: Text(
|
|
'Bạn có chắc chắn muốn đăng xuất?',
|
|
style: TextStyle(color: colorScheme.onSurfaceVariant),
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.of(context).pop(),
|
|
child: const Text('Hủy'),
|
|
),
|
|
TextButton(
|
|
onPressed: () => _performLogout(context, ref),
|
|
style: TextButton.styleFrom(foregroundColor: AppColors.danger),
|
|
child: const Text('Đăng xuất'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
/// Perform logout operation
|
|
///
|
|
/// Handles the complete logout process:
|
|
/// 1. Close confirmation dialog
|
|
/// 2. Show loading indicator
|
|
/// 3. Clear ALL Hive local data (reset, not just user data)
|
|
/// 4. Clear ALL Flutter Secure Storage keys
|
|
/// 5. Call auth provider logout (clears session, gets new public session)
|
|
/// 6. Navigate to login screen (handled by router redirect)
|
|
/// 7. Show success message
|
|
Future<void> _performLogout(BuildContext context, WidgetRef ref) async {
|
|
// Close confirmation dialog
|
|
Navigator.of(context).pop();
|
|
|
|
// Show loading dialog
|
|
unawaited(showDialog<void>(
|
|
context: context,
|
|
barrierDismissible: false,
|
|
builder: (context) => const Center(
|
|
child: Card(
|
|
child: Padding(
|
|
padding: EdgeInsets.all(24),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
CircularProgressIndicator(),
|
|
SizedBox(height: 16),
|
|
Text('Đang đăng xuất...'),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
));
|
|
|
|
try {
|
|
// 1. Clear ALL Hive data (complete reset)
|
|
await HiveInitializer.reset();
|
|
|
|
// 2. Clear ALL Flutter Secure Storage keys
|
|
const secureStorage = FlutterSecureStorage(
|
|
aOptions: AndroidOptions(encryptedSharedPreferences: true),
|
|
iOptions: IOSOptions(accessibility: KeychainAccessibility.first_unlock),
|
|
);
|
|
await secureStorage.deleteAll();
|
|
|
|
// 3. Call auth provider logout
|
|
// This will:
|
|
// - Clear FrappeAuthService session
|
|
// - Get new public session for login/registration
|
|
// - Update auth state to null (logged out)
|
|
await ref.read(authProvider.notifier).logout();
|
|
|
|
// Close loading dialog
|
|
if (context.mounted) {
|
|
Navigator.of(context).pop();
|
|
}
|
|
|
|
// Show success message
|
|
if (context.mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(
|
|
content: Text('Đã đăng xuất thành công'),
|
|
duration: Duration(seconds: 2),
|
|
backgroundColor: AppColors.success,
|
|
),
|
|
);
|
|
}
|
|
|
|
// Navigation to login screen is handled automatically by GoRouter redirect
|
|
// when auth state becomes null
|
|
} catch (e) {
|
|
// Close loading dialog
|
|
if (context.mounted) {
|
|
Navigator.of(context).pop();
|
|
}
|
|
|
|
// Show error message
|
|
if (context.mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text('Lỗi đăng xuất: ${e.toString()}'),
|
|
duration: const Duration(seconds: 3),
|
|
backgroundColor: AppColors.danger,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|