145 lines
4.8 KiB
Dart
145 lines
4.8 KiB
Dart
import '../../../../shared/widgets/app_card.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
import '../../../../core/utils/formatters.dart';
|
|
|
|
import '../../../../core/errors/failure.dart';
|
|
import '../../../../shared/widgets/app_button.dart';
|
|
import '../../../../shared/widgets/app_confirmation_dialog.dart';
|
|
import '../../../../shared/widgets/app_loading_view.dart';
|
|
import '../../../../shared/widgets/app_status_chip.dart';
|
|
import '../../../../shared/widgets/error_view.dart';
|
|
import '../../../../shared/widgets/page_header.dart';
|
|
import '../../../../shared/models/user_management_models.dart';
|
|
import '../providers/users_provider.dart';
|
|
import '../../../../shared/widgets/app_toast.dart';
|
|
|
|
class UserDetailScreen extends ConsumerWidget {
|
|
const UserDetailScreen({super.key, required this.userId});
|
|
|
|
final String userId;
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final userAsync = ref.watch(userDetailProvider(userId));
|
|
|
|
return Padding(
|
|
padding: const EdgeInsets.all(24),
|
|
child: userAsync.when(
|
|
loading: () => const AppLoadingView(message: 'Loading user details...'),
|
|
error: (error, _) => ErrorView.fromFailure(
|
|
error is Failure ? error : Failure.unknown(message: error.toString()),
|
|
onRetry: () => ref.invalidate(userDetailProvider(userId)),
|
|
),
|
|
data: (user) => Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
PageHeader(
|
|
title: user.fullName,
|
|
subtitle: user.employeeCode,
|
|
actions: [
|
|
OutlinedButton.icon(
|
|
onPressed: () => context.push('/users/$userId/edit'),
|
|
icon: const Icon(Icons.edit_outlined),
|
|
label: const Text('Edit'),
|
|
),
|
|
const SizedBox(width: 8),
|
|
AppButton(
|
|
label: 'Deactivate',
|
|
expand: false,
|
|
onPressed: () => _deactivate(context, ref),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 24),
|
|
AppCard(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(24),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
_DetailRow(label: 'Email', value: user.email),
|
|
_DetailRow(label: 'Mobile', value: user.mobile),
|
|
_DetailRow(label: 'Role', value: user.roleLabel),
|
|
_DetailRow(label: 'Department', value: user.departmentLabel),
|
|
_DetailRow(
|
|
label: 'Status',
|
|
valueWidget: AppStatusChip(status: user.status),
|
|
),
|
|
if (user.createdAt != null)
|
|
_DetailRow(
|
|
label: 'Created',
|
|
value: DateFormatter.displayDateTime(user.createdAt),
|
|
),
|
|
if (user.updatedAt != null)
|
|
_DetailRow(
|
|
label: 'Updated',
|
|
value: DateFormatter.displayDateTime(user.updatedAt),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<void> _deactivate(BuildContext context, WidgetRef ref) async {
|
|
final confirmed = await showAppConfirmationDialog(
|
|
context: context,
|
|
title: 'Deactivate user',
|
|
message: 'Are you sure you want to deactivate this user?',
|
|
confirmLabel: 'Deactivate',
|
|
isDestructive: true,
|
|
);
|
|
if (confirmed != true || !context.mounted) return;
|
|
|
|
final success = await ref.read(userDetailProvider(userId).notifier).deactivate();
|
|
if (!context.mounted) return;
|
|
|
|
showAppToastFromSnackBar(context,
|
|
SnackBar(content: Text(success ? 'User deactivated' : 'Failed to deactivate')),
|
|
);
|
|
if (success) context.pop();
|
|
}
|
|
}
|
|
|
|
class _DetailRow extends StatelessWidget {
|
|
const _DetailRow({
|
|
required this.label,
|
|
this.value,
|
|
this.valueWidget,
|
|
});
|
|
|
|
final String label;
|
|
final String? value;
|
|
final Widget? valueWidget;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
|
child: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
SizedBox(
|
|
width: 140,
|
|
child: Text(
|
|
label,
|
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
),
|
|
),
|
|
),
|
|
Expanded(
|
|
child: valueWidget ?? Text(value ?? '—'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|