diff --git a/frontend/pweb/lib/pages/roles/page.dart b/frontend/pweb/lib/pages/roles/page.dart deleted file mode 100644 index 54f587b0..00000000 --- a/frontend/pweb/lib/pages/roles/page.dart +++ /dev/null @@ -1,162 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:go_router/go_router.dart'; - -import 'package:provider/provider.dart'; - -import 'package:pshared/models/permissions/data/policy.dart'; -import 'package:pshared/models/permissions/descriptions/role.dart'; -import 'package:pshared/models/resources.dart'; -import 'package:pshared/provider/permissions.dart'; - -import 'package:pweb/app/router/payout_routes.dart'; -import 'package:pweb/app/router/page_params.dart'; -import 'package:pweb/app/router/pages.dart'; -import 'package:pweb/pages/loader.dart'; -import 'package:pweb/pages/roles/widgets/actions.dart'; -import 'package:pweb/pages/roles/widgets/header.dart'; -import 'package:pweb/pages/roles/widgets/list.dart'; -import 'package:pweb/models/state/visibility.dart'; -import 'package:pweb/utils/roles/is_owner_role.dart'; -import 'package:pweb/widgets/sidebar/destinations.dart'; -import 'package:pweb/widgets/dialogs/confirmation_dialog.dart'; -import 'package:pweb/utils/error/snackbar.dart'; -import 'package:pweb/widgets/roles/create_role_dialog.dart'; - -import 'package:pweb/generated/i18n/app_localizations.dart'; - - -class RolesSettingsPage extends StatelessWidget { - const RolesSettingsPage({super.key}); - - Future _createRole(BuildContext context) async { - final loc = AppLocalizations.of(context)!; - final draft = await showCreateRoleDialog( - context, - // title: loc.rolesCreateTitle, - // confirmLabel: loc.rolesCreateAction, - ); - if (draft == null) return; - - final permissions = context.read(); - await executeActionWithNotification( - context: context, - action: () => permissions.createRoleDescription( - name: draft.name, - description: draft.description.isEmpty ? null : draft.description, - ), - successMessage: loc.rolesCreateSuccess, - errorMessage: loc.rolesCreateFailed, - ); - } - - Future _copyRole(BuildContext context, RoleDescription role) async { - final loc = AppLocalizations.of(context)!; - final permissions = context.read(); - final sourcePolicies = permissions.getRolePolicies(role.id); - final draft = await showCreateRoleDialog( - context, - // initialDraft: RoleDraft( - // name: copyName, - // description: role.description ?? '', - // ), - // title: loc.rolesCopyTitle, - // confirmLabel: loc.rolesCopyAction, - ); - if (draft == null) return; - - await executeActionWithNotification( - context: context, - action: () async { - final createdRole = await permissions.createRoleDescription( - name: draft.name, - description: draft.description.isEmpty ? null : draft.description, - ); - if (createdRole == null || sourcePolicies.isEmpty) return createdRole; - final copiedPolicies = sourcePolicies.map((policy) => Policy( - roleDescriptionRef: createdRole.id, - organizationRef: policy.organizationRef, - descriptionRef: policy.descriptionRef, - objectRef: policy.objectRef, - effect: policy.effect, - )).toList(); - await permissions.createPermissions(copiedPolicies); - return createdRole; - }, - successMessage: loc.rolesCopySuccess, - errorMessage: loc.rolesCopyFailed, - ); - } - - Future _deleteRole(BuildContext context, RoleDescription role) async { - final loc = AppLocalizations.of(context)!; - final confirmed = await showConfirmationDialog( - context: context, - title: loc.rolesDeleteConfirmTitle, - message: loc.rolesDeleteConfirmMessage(role.name), - confirmLabel: loc.delete, - ); - if (!confirmed) return; - - final permissions = context.read(); - await executeActionWithNotification( - context: context, - action: () => permissions.deleteRoleDescription(role.id), - successMessage: loc.rolesDeleteSuccess, - errorMessage: loc.rolesDeleteFailed, - ); - } - - @override - Widget build(BuildContext context) { - final loc = AppLocalizations.of(context)!; - final permissions = context.watch(); - final canCreate = permissions.canCreate(ResourceType.roles); - final canDelete = permissions.canDelete(ResourceType.roles); - final roles = permissions.roleDescriptions; - bool isPermanentRole(RoleDescription role) => isOwnerRole(role, loc); - VisibilityState hiddenIf(bool isHidden) => - isHidden ? VisibilityState.hidden : VisibilityState.visible; - - return PageViewLoader( - child: SafeArea( - child: SingleChildScrollView( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - RolesHeader( - title: loc.rolesPageTitle, - subtitle: loc.rolesPageSubtitle, - onBack: () => context.goToPayout(PayoutDestination.invitations), - ), - const SizedBox(height: 16), - RolesActions( - canCreate: canCreate, - onCreateRole: () => _createRole(context), - createLabel: loc.rolesCreateAction, - ), - const SizedBox(height: 16), - RolesList( - roles: roles, - canCopy: (role) => hiddenIf(!canCreate || isPermanentRole(role)), - canDelete: (role) => hiddenIf(!canDelete || isPermanentRole(role)), - canManagePolicies: (role) => hiddenIf(isPermanentRole(role)), - emptyLabel: loc.rolesEmpty, - policiesCount: (role) => permissions.getRolePolicies(role.id).length, - onCopy: (role) => _copyRole(context, role), - onDelete: (role) => _deleteRole(context, role), - onManagePolicies: (role) => context.pushNamed( - Pages.permissions.name, - pathParameters: { - PageParams.roleRef.name: role.id, - }, - ), - ), - ], - ), - ), - ), - ); - } -} diff --git a/frontend/pweb/lib/pages/roles/widgets/actions.dart b/frontend/pweb/lib/pages/roles/widgets/actions.dart deleted file mode 100644 index e7ab053b..00000000 --- a/frontend/pweb/lib/pages/roles/widgets/actions.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'package:flutter/material.dart'; - - -class RolesActions extends StatelessWidget { - final bool canCreate; - final VoidCallback onCreateRole; - final String createLabel; - - const RolesActions({ - super.key, - required this.canCreate, - required this.onCreateRole, - required this.createLabel, - }); - - @override - Widget build(BuildContext context) { - if (!canCreate) return const SizedBox.shrink(); - return Align( - alignment: Alignment.centerLeft, - child: ElevatedButton.icon( - onPressed: onCreateRole, - icon: const Icon(Icons.add), - label: Text(createLabel), - ), - ); - } -} diff --git a/frontend/pweb/lib/pages/roles/widgets/empty_state.dart b/frontend/pweb/lib/pages/roles/widgets/empty_state.dart deleted file mode 100644 index 0588029b..00000000 --- a/frontend/pweb/lib/pages/roles/widgets/empty_state.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:flutter/material.dart'; - - -class RolesEmptyState extends StatelessWidget { - final String label; - - const RolesEmptyState({ - super.key, - required this.label, - }); - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - return Center( - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 32), - child: Text( - label, - style: theme.textTheme.bodyMedium, - ), - ), - ); - } -} diff --git a/frontend/pweb/lib/pages/roles/widgets/header.dart b/frontend/pweb/lib/pages/roles/widgets/header.dart deleted file mode 100644 index 4b158e25..00000000 --- a/frontend/pweb/lib/pages/roles/widgets/header.dart +++ /dev/null @@ -1,55 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:pweb/generated/i18n/app_localizations.dart'; - - -class RolesHeader extends StatelessWidget { - final String title; - final String subtitle; - final VoidCallback? onBack; - - const RolesHeader({ - super.key, - required this.title, - required this.subtitle, - this.onBack, - }); - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - final loc = AppLocalizations.of(context)!; - return Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (onBack != null) ...[ - IconButton( - icon: const Icon(Icons.arrow_back), - onPressed: onBack, - tooltip: loc.back, - color: theme.colorScheme.primary, - ), - const SizedBox(width: 8), - ], - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - title, - style: theme.textTheme.headlineSmall?.copyWith(fontWeight: FontWeight.w600), - ), - const SizedBox(height: 6), - Text( - subtitle, - style: theme.textTheme.bodyMedium?.copyWith( - color: theme.colorScheme.onSurfaceVariant, - ), - ), - ], - ), - ), - ], - ); - } -} diff --git a/frontend/pweb/lib/pages/roles/widgets/list.dart b/frontend/pweb/lib/pages/roles/widgets/list.dart deleted file mode 100644 index dbecd006..00000000 --- a/frontend/pweb/lib/pages/roles/widgets/list.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:pshared/models/permissions/descriptions/role.dart'; - -import 'package:pweb/models/state/visibility.dart'; -import 'package:pweb/pages/roles/widgets/empty_state.dart'; -import 'package:pweb/pages/roles/widgets/role_card.dart'; - - -class RolesList extends StatelessWidget { - final List roles; - final VisibilityState Function(RoleDescription role) canCopy; - final VisibilityState Function(RoleDescription role) canDelete; - final VisibilityState Function(RoleDescription role) canManagePolicies; - final String emptyLabel; - final int Function(RoleDescription role) policiesCount; - final ValueChanged onCopy; - final ValueChanged onDelete; - final ValueChanged onManagePolicies; - - const RolesList({ - super.key, - required this.roles, - required this.canCopy, - required this.canDelete, - required this.canManagePolicies, - required this.emptyLabel, - required this.policiesCount, - required this.onCopy, - required this.onDelete, - required this.onManagePolicies, - }); - - @override - Widget build(BuildContext context) { - if (roles.isEmpty) { - return RolesEmptyState(label: emptyLabel); - } - - return Column( - children: roles.map((role) { - return Padding( - padding: const EdgeInsets.only(bottom: 12), - child: RoleCard( - role: role, - policyCount: policiesCount(role), - canManagePolicies: canManagePolicies(role), - canCopy: canCopy(role), - canDelete: canDelete(role), - onCopy: () => onCopy(role), - onDelete: () => onDelete(role), - onManagePolicies: () => onManagePolicies(role), - ), - ); - }).toList(), - ); - } -} diff --git a/frontend/pweb/lib/pages/roles/widgets/role_card.dart b/frontend/pweb/lib/pages/roles/widgets/role_card.dart deleted file mode 100644 index ecfdeb2b..00000000 --- a/frontend/pweb/lib/pages/roles/widgets/role_card.dart +++ /dev/null @@ -1,108 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:pshared/models/permissions/descriptions/role.dart'; - -import 'package:pweb/models/state/visibility.dart'; -import 'package:pweb/generated/i18n/app_localizations.dart'; - - -class RoleCard extends StatelessWidget { - final RoleDescription role; - final int policyCount; - final VisibilityState canManagePolicies; - final VisibilityState canCopy; - final VisibilityState canDelete; - final VoidCallback onCopy; - final VoidCallback onDelete; - final VoidCallback onManagePolicies; - - const RoleCard({ - super.key, - required this.role, - required this.policyCount, - required this.canManagePolicies, - required this.canCopy, - required this.canDelete, - required this.onCopy, - required this.onDelete, - required this.onManagePolicies, - }); - - @override - Widget build(BuildContext context) { - final loc = AppLocalizations.of(context)!; - final theme = Theme.of(context); - - return Card( - elevation: 0, - color: theme.colorScheme.surfaceContainerHighest.withAlpha(40), - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Icon(Icons.security_outlined), - const SizedBox(width: 12), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - role.name, - style: theme.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w600), - ), - if ((role.description ?? '').isNotEmpty) ...[ - const SizedBox(height: 4), - Text( - role.description!, - style: theme.textTheme.bodyMedium, - ), - ], - const SizedBox(height: 6), - Text( - loc.rolesPoliciesCount(policyCount), - style: theme.textTheme.bodySmall?.copyWith( - color: theme.colorScheme.onSurfaceVariant, - ), - ), - ], - ), - ), - ], - ), - const SizedBox(height: 12), - Wrap( - spacing: 12, - children: [ - if (canManagePolicies == VisibilityState.visible) - TextButton.icon( - onPressed: onManagePolicies, - icon: const Icon(Icons.tune, size: 18), - label: Text(loc.rolesPoliciesAction), - ), - if (canCopy == VisibilityState.visible) - TextButton.icon( - onPressed: onCopy, - icon: const Icon(Icons.copy_outlined, size: 18), - label: Text(loc.rolesCopyAction), - ), - if (canDelete == VisibilityState.visible) - TextButton.icon( - onPressed: onDelete, - icon: Icon(Icons.delete_outline, size: 18, color: theme.colorScheme.error), - label: Text(loc.delete), - style: TextButton.styleFrom( - foregroundColor: theme.colorScheme.error, - ), - ), - ], - ), - ], - ), - ), - ); - } -}