bharat_erp/lib/modules/roles/presentation/screens/permission_matrix_screen.dart

194 lines
6.1 KiB
Dart

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<PermissionMatrixScreen> createState() => _PermissionMatrixScreenState();
}
class _PermissionMatrixScreenState extends ConsumerState<PermissionMatrixScreen> {
bool _isSaving = false;
Future<void> _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<PermissionMatrixModuleRow>(
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<bool>? onChanged;
@override
Widget build(BuildContext context) {
return SwitchListTile(
title: Text(label),
value: value,
onChanged: onChanged,
);
}
}