import '../../../../shared/widgets/app_card.dart'; import '../../../../shared/widgets/app_data_table.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../../core/errors/failure.dart'; import '../../../../core/utils/responsive_utils.dart'; import '../../../../shared/models/permission_matrix_models.dart'; import '../../../../shared/widgets/app_button.dart'; import '../../../../shared/widgets/app_loading_view.dart'; import '../../../../shared/widgets/error_view.dart'; import '../../../../shared/widgets/page_header.dart'; import '../providers/roles_provider.dart'; import '../../../../shared/widgets/app_toast.dart'; class PermissionMatrixScreen extends ConsumerStatefulWidget { const PermissionMatrixScreen({super.key, required this.roleId}); final String roleId; @override ConsumerState createState() => _PermissionMatrixScreenState(); } class _PermissionMatrixScreenState extends ConsumerState { bool _isSaving = false; Future _save() async { setState(() => _isSaving = true); final success = await ref.read(permissionMatrixProvider(widget.roleId).notifier).save(); if (!mounted) return; setState(() => _isSaving = false); showAppToastFromSnackBar(context, SnackBar( content: Text(success ? 'Permissions saved' : 'Failed to save permissions'), ), ); } @override Widget build(BuildContext context) { final matrixAsync = ref.watch(permissionMatrixProvider(widget.roleId)); return Padding( padding: const EdgeInsets.all(24), child: matrixAsync.when( loading: () => const AppLoadingView(message: 'Loading permission matrix...'), error: (error, _) => ErrorView.fromFailure( error is Failure ? error : Failure.unknown(message: error.toString()), onRetry: () => ref.invalidate(permissionMatrixProvider(widget.roleId)), ), data: (matrix) => Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ PageHeader( title: 'Permission Matrix', subtitle: matrix.roleName, actions: [ AppButton( label: 'Save Changes', expand: false, isLoading: _isSaving, onPressed: _save, ), ], ), Expanded( child: context.isMobile ? _MatrixCardList(roleId: widget.roleId, matrix: matrix) : _MatrixGrid(roleId: widget.roleId, matrix: matrix), ), ], ), ), ); } } class _MatrixGrid extends ConsumerWidget { const _MatrixGrid({required this.roleId, required this.matrix}); final String roleId; final RolePermissionMatrix matrix; @override Widget build(BuildContext context, WidgetRef ref) { return AppDataTable( wrapInCard: true, columns: [ AppDataColumn( id: 'module', label: 'Module', sortKey: 'module', flex: 3, enableSearch: false, searchText: (row) => row.name, cellBuilder: (_, row) => AppTableCell.text(row.name), ), ...matrix.actionColumns.map( (action) => AppDataColumn( label: permissionActionLabel(action), flex: 1, alignment: Alignment.center, enableSearch: false, cellBuilder: (_, row) { final applicable = isPermissionActionApplicable( row.code, action, columnActions: matrix.actionColumns, ); return Checkbox( value: applicable ? (row.granted[action] ?? false) : false, visualDensity: VisualDensity.compact, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, onChanged: applicable ? (checked) => ref .read(permissionMatrixProvider(roleId).notifier) .toggleAction(row.moduleId, action, checked ?? false) : null, ); }, ), ), ], rows: matrix.modules, ); } } class _MatrixCardList extends ConsumerWidget { const _MatrixCardList({required this.roleId, required this.matrix}); final String roleId; final RolePermissionMatrix matrix; @override Widget build(BuildContext context, WidgetRef ref) { return ListView.separated( itemCount: matrix.modules.length, separatorBuilder: (_, __) => const SizedBox(height: 12), itemBuilder: (context, index) { final row = matrix.modules[index]; return AppCard( child: ExpansionTile( title: Text(row.name), children: matrix.actionColumns .map( (action) { final applicable = isPermissionActionApplicable( row.code, action, columnActions: matrix.actionColumns, ); return _PermissionSwitch( label: permissionActionLabel(action), value: applicable ? (row.granted[action] ?? false) : false, onChanged: applicable ? (v) => ref .read(permissionMatrixProvider(roleId).notifier) .toggleAction(row.moduleId, action, v) : null, ); }, ) .toList(), ), ); }, ); } } class _PermissionSwitch extends StatelessWidget { const _PermissionSwitch({ required this.label, required this.value, required this.onChanged, }); final String label; final bool value; final ValueChanged? onChanged; @override Widget build(BuildContext context) { return SwitchListTile( title: Text(label), value: value, onChanged: onChanged, ); } }