Refactor code for improved readability and consistency in multiple files
This commit is contained in:
@@ -18,7 +18,9 @@ class PermissionBloc extends Bloc<PermissionEvent, PermissionState> {
|
|||||||
) async {
|
) async {
|
||||||
emit(PermissionLoading());
|
emit(PermissionLoading());
|
||||||
try {
|
try {
|
||||||
final response = await apiClient.getRaw('/orgs/${event.orgId}/permissions');
|
final response = await apiClient.getRaw(
|
||||||
|
'/orgs/${event.orgId}/permissions',
|
||||||
|
);
|
||||||
final capabilities = Capabilities(
|
final capabilities = Capabilities(
|
||||||
canRead: response['canRead'] ?? false,
|
canRead: response['canRead'] ?? false,
|
||||||
canWrite: response['canWrite'] ?? false,
|
canWrite: response['canWrite'] ?? false,
|
||||||
|
|||||||
@@ -56,7 +56,9 @@ class Invitation {
|
|||||||
role: json['role'],
|
role: json['role'],
|
||||||
createdAt: DateTime.parse(json['createdAt']),
|
createdAt: DateTime.parse(json['createdAt']),
|
||||||
expiresAt: DateTime.parse(json['expiresAt']),
|
expiresAt: DateTime.parse(json['expiresAt']),
|
||||||
acceptedAt: json['acceptedAt'] != null ? DateTime.parse(json['acceptedAt']) : null,
|
acceptedAt: json['acceptedAt'] != null
|
||||||
|
? DateTime.parse(json['acceptedAt'])
|
||||||
|
: null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -370,13 +370,20 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildNavButton(String label, IconData icon, {bool isAvatar = false, VoidCallback? onTap}) {
|
Widget _buildNavButton(
|
||||||
|
String label,
|
||||||
|
IconData icon, {
|
||||||
|
bool isAvatar = false,
|
||||||
|
VoidCallback? onTap,
|
||||||
|
}) {
|
||||||
final isSelected = _selectedTab == label;
|
final isSelected = _selectedTab == label;
|
||||||
final highlightColor = const Color.fromARGB(255, 100, 200, 255);
|
final highlightColor = const Color.fromARGB(255, 100, 200, 255);
|
||||||
final defaultColor = AppTheme.secondaryText;
|
final defaultColor = AppTheme.secondaryText;
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: onTap ?? () {
|
onTap:
|
||||||
|
onTap ??
|
||||||
|
() {
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectedTab = label;
|
_selectedTab = label;
|
||||||
});
|
});
|
||||||
@@ -495,7 +502,12 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
|
|||||||
const SizedBox(width: 16),
|
const SizedBox(width: 16),
|
||||||
_buildNavButton('Add', Icons.add),
|
_buildNavButton('Add', Icons.add),
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: 16),
|
||||||
_buildNavButton('Settings', Icons.settings, onTap: () => _showOrganizationSettings(context)),
|
_buildNavButton(
|
||||||
|
'Settings',
|
||||||
|
Icons.settings,
|
||||||
|
onTap: () =>
|
||||||
|
_showOrganizationSettings(context),
|
||||||
|
),
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: 16),
|
||||||
_buildNavButton(
|
_buildNavButton(
|
||||||
'Profile',
|
'Profile',
|
||||||
|
|||||||
@@ -71,7 +71,10 @@ class ApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, dynamic>> getRaw(String path, {Map<String, dynamic>? queryParameters}) async {
|
Future<Map<String, dynamic>> getRaw(
|
||||||
|
String path, {
|
||||||
|
Map<String, dynamic>? queryParameters,
|
||||||
|
}) async {
|
||||||
try {
|
try {
|
||||||
final response = await _dio.get(path, queryParameters: queryParameters);
|
final response = await _dio.get(path, queryParameters: queryParameters);
|
||||||
return response.data;
|
return response.data;
|
||||||
|
|||||||
@@ -39,7 +39,11 @@ class OrgApi {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> updateMemberRole(String orgId, String userId, String role) async {
|
Future<void> updateMemberRole(
|
||||||
|
String orgId,
|
||||||
|
String userId,
|
||||||
|
String role,
|
||||||
|
) async {
|
||||||
await _apiClient.patch(
|
await _apiClient.patch(
|
||||||
'/orgs/$orgId/members/$userId',
|
'/orgs/$orgId/members/$userId',
|
||||||
data: {'role': role},
|
data: {'role': role},
|
||||||
@@ -58,7 +62,11 @@ class OrgApi {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Invitation> createInvitation(String orgId, String username, String role) async {
|
Future<Invitation> createInvitation(
|
||||||
|
String orgId,
|
||||||
|
String username,
|
||||||
|
String role,
|
||||||
|
) async {
|
||||||
final result = await _apiClient.post(
|
final result = await _apiClient.post(
|
||||||
'/orgs/$orgId/invitations',
|
'/orgs/$orgId/invitations',
|
||||||
data: {'username': username, 'role': role},
|
data: {'username': username, 'role': role},
|
||||||
@@ -78,7 +86,10 @@ class OrgApi {
|
|||||||
await _apiClient.delete('/orgs/$orgId/invitations/$invitationId');
|
await _apiClient.delete('/orgs/$orgId/invitations/$invitationId');
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<JoinRequest> createJoinRequest(String orgId, {String? inviteToken}) async {
|
Future<JoinRequest> createJoinRequest(
|
||||||
|
String orgId, {
|
||||||
|
String? inviteToken,
|
||||||
|
}) async {
|
||||||
final data = {'orgId': orgId};
|
final data = {'orgId': orgId};
|
||||||
if (inviteToken != null) {
|
if (inviteToken != null) {
|
||||||
data['inviteToken'] = inviteToken;
|
data['inviteToken'] = inviteToken;
|
||||||
@@ -98,7 +109,11 @@ class OrgApi {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> acceptJoinRequest(String orgId, String requestId, String role) async {
|
Future<void> acceptJoinRequest(
|
||||||
|
String orgId,
|
||||||
|
String requestId,
|
||||||
|
String role,
|
||||||
|
) async {
|
||||||
await _apiClient.post(
|
await _apiClient.post(
|
||||||
'/orgs/$orgId/join-requests/$requestId/accept',
|
'/orgs/$orgId/join-requests/$requestId/accept',
|
||||||
data: {'role': role},
|
data: {'role': role},
|
||||||
|
|||||||
@@ -20,10 +20,12 @@ class OrganizationSettingsDialog extends StatefulWidget {
|
|||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<OrganizationSettingsDialog> createState() => _OrganizationSettingsDialogState();
|
State<OrganizationSettingsDialog> createState() =>
|
||||||
|
_OrganizationSettingsDialogState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _OrganizationSettingsDialogState extends State<OrganizationSettingsDialog> with TickerProviderStateMixin {
|
class _OrganizationSettingsDialogState extends State<OrganizationSettingsDialog>
|
||||||
|
with TickerProviderStateMixin {
|
||||||
late TabController _tabController;
|
late TabController _tabController;
|
||||||
List<Member> _members = [];
|
List<Member> _members = [];
|
||||||
List<Invitation> _invitations = [];
|
List<Invitation> _invitations = [];
|
||||||
@@ -77,46 +79,54 @@ class _OrganizationSettingsDialogState extends State<OrganizationSettingsDialog>
|
|||||||
|
|
||||||
Future<void> _updateMemberRole(String userId, String newRole) async {
|
Future<void> _updateMemberRole(String userId, String newRole) async {
|
||||||
try {
|
try {
|
||||||
await widget.orgApi.updateMemberRole(widget.organization.id, userId, newRole);
|
await widget.orgApi.updateMemberRole(
|
||||||
|
widget.organization.id,
|
||||||
|
userId,
|
||||||
|
newRole,
|
||||||
|
);
|
||||||
await _loadData(); // Refresh
|
await _loadData(); // Refresh
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(
|
||||||
SnackBar(content: Text('Failed to update role: $e')),
|
context,
|
||||||
);
|
).showSnackBar(SnackBar(content: Text('Failed to update role: $e')));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _removeMember(String userId) async {
|
Future<void> _removeMember(String userId) async {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
await widget.orgApi.removeMember(widget.organization.id, userId);
|
await widget.orgApi.removeMember(widget.organization.id, userId);
|
||||||
await _loadData(); // Refresh
|
await _loadData(); // Refresh
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(
|
||||||
SnackBar(content: Text('Failed to remove member: $e')),
|
context,
|
||||||
);
|
).showSnackBar(SnackBar(content: Text('Failed to remove member: $e')));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _inviteUser(String username, String role) async {
|
Future<void> _inviteUser(String username, String role) async {
|
||||||
try {
|
try {
|
||||||
|
await widget.orgApi.createInvitation(
|
||||||
await widget.orgApi.createInvitation(widget.organization.id, username, role);
|
widget.organization.id,
|
||||||
|
username,
|
||||||
|
role,
|
||||||
|
);
|
||||||
await _loadData(); // Refresh
|
await _loadData(); // Refresh
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(
|
||||||
SnackBar(content: Text('Failed to send invitation: $e')),
|
context,
|
||||||
);
|
).showSnackBar(SnackBar(content: Text('Failed to send invitation: $e')));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _cancelInvitation(String invitationId) async {
|
Future<void> _cancelInvitation(String invitationId) async {
|
||||||
try {
|
try {
|
||||||
|
await widget.orgApi.cancelInvitation(
|
||||||
await widget.orgApi.cancelInvitation(widget.organization.id, invitationId);
|
widget.organization.id,
|
||||||
|
invitationId,
|
||||||
|
);
|
||||||
await _loadData(); // Refresh
|
await _loadData(); // Refresh
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
@@ -128,40 +138,43 @@ class _OrganizationSettingsDialogState extends State<OrganizationSettingsDialog>
|
|||||||
|
|
||||||
Future<void> _acceptJoinRequest(String requestId, String role) async {
|
Future<void> _acceptJoinRequest(String requestId, String role) async {
|
||||||
try {
|
try {
|
||||||
|
await widget.orgApi.acceptJoinRequest(
|
||||||
await widget.orgApi.acceptJoinRequest(widget.organization.id, requestId, role);
|
widget.organization.id,
|
||||||
|
requestId,
|
||||||
|
role,
|
||||||
|
);
|
||||||
await _loadData(); // Refresh
|
await _loadData(); // Refresh
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(
|
||||||
SnackBar(content: Text('Failed to accept request: $e')),
|
context,
|
||||||
);
|
).showSnackBar(SnackBar(content: Text('Failed to accept request: $e')));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _rejectJoinRequest(String requestId) async {
|
Future<void> _rejectJoinRequest(String requestId) async {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
await widget.orgApi.rejectJoinRequest(widget.organization.id, requestId);
|
await widget.orgApi.rejectJoinRequest(widget.organization.id, requestId);
|
||||||
await _loadData(); // Refresh
|
await _loadData(); // Refresh
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(
|
||||||
SnackBar(content: Text('Failed to reject request: $e')),
|
context,
|
||||||
);
|
).showSnackBar(SnackBar(content: Text('Failed to reject request: $e')));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _regenerateInviteLink() async {
|
Future<void> _regenerateInviteLink() async {
|
||||||
try {
|
try {
|
||||||
|
final newLink = await widget.orgApi.regenerateInviteLink(
|
||||||
final newLink = await widget.orgApi.regenerateInviteLink(widget.organization.id);
|
widget.organization.id,
|
||||||
|
);
|
||||||
setState(() => _inviteLink = newLink);
|
setState(() => _inviteLink = newLink);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(
|
||||||
SnackBar(content: Text('Failed to regenerate link: $e')),
|
context,
|
||||||
);
|
).showSnackBar(SnackBar(content: Text('Failed to regenerate link: $e')));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,7 +187,8 @@ class _OrganizationSettingsDialogState extends State<OrganizationSettingsDialog>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get _canManage => widget.permissionState is PermissionLoaded &&
|
bool get _canManage =>
|
||||||
|
widget.permissionState is PermissionLoaded &&
|
||||||
(widget.permissionState as PermissionLoaded).capabilities.canAdmin;
|
(widget.permissionState as PermissionLoaded).capabilities.canAdmin;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -232,7 +246,10 @@ class _OrganizationSettingsDialogState extends State<OrganizationSettingsDialog>
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Text(_error!, style: TextStyle(color: AppTheme.errorColor)),
|
Text(
|
||||||
|
_error!,
|
||||||
|
style: TextStyle(color: AppTheme.errorColor),
|
||||||
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
ModernGlassButton(
|
ModernGlassButton(
|
||||||
onPressed: _loadData,
|
onPressed: _loadData,
|
||||||
@@ -271,7 +288,8 @@ class _OrganizationSettingsDialogState extends State<OrganizationSettingsDialog>
|
|||||||
member.role,
|
member.role,
|
||||||
style: TextStyle(color: AppTheme.secondaryText),
|
style: TextStyle(color: AppTheme.secondaryText),
|
||||||
),
|
),
|
||||||
trailing: _canManage ? Row(
|
trailing: _canManage
|
||||||
|
? Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
if (member.role != 'owner')
|
if (member.role != 'owner')
|
||||||
@@ -291,11 +309,15 @@ class _OrganizationSettingsDialogState extends State<OrganizationSettingsDialog>
|
|||||||
),
|
),
|
||||||
if (member.role != 'owner')
|
if (member.role != 'owner')
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.remove_circle, color: AppTheme.errorColor),
|
icon: Icon(
|
||||||
|
Icons.remove_circle,
|
||||||
|
color: AppTheme.errorColor,
|
||||||
|
),
|
||||||
onPressed: () => _removeMember(member.userId),
|
onPressed: () => _removeMember(member.userId),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
) : null,
|
)
|
||||||
|
: null,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -323,8 +345,14 @@ class _OrganizationSettingsDialogState extends State<OrganizationSettingsDialog>
|
|||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final inv = _invitations[index];
|
final inv = _invitations[index];
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(inv.username, style: TextStyle(color: AppTheme.primaryText)),
|
title: Text(
|
||||||
subtitle: Text('Role: ${inv.role}', style: TextStyle(color: AppTheme.secondaryText)),
|
inv.username,
|
||||||
|
style: TextStyle(color: AppTheme.primaryText),
|
||||||
|
),
|
||||||
|
subtitle: Text(
|
||||||
|
'Role: ${inv.role}',
|
||||||
|
style: TextStyle(color: AppTheme.secondaryText),
|
||||||
|
),
|
||||||
trailing: IconButton(
|
trailing: IconButton(
|
||||||
icon: Icon(Icons.cancel, color: AppTheme.errorColor),
|
icon: Icon(Icons.cancel, color: AppTheme.errorColor),
|
||||||
onPressed: () => _cancelInvitation(inv.id),
|
onPressed: () => _cancelInvitation(inv.id),
|
||||||
@@ -358,10 +386,7 @@ class _OrganizationSettingsDialogState extends State<OrganizationSettingsDialog>
|
|||||||
DropdownButtonFormField<String>(
|
DropdownButtonFormField<String>(
|
||||||
initialValue: selectedRole,
|
initialValue: selectedRole,
|
||||||
items: ['admin', 'member'].map((role) {
|
items: ['admin', 'member'].map((role) {
|
||||||
return DropdownMenuItem(
|
return DropdownMenuItem(value: role, child: Text(role));
|
||||||
value: role,
|
|
||||||
child: Text(role),
|
|
||||||
);
|
|
||||||
}).toList(),
|
}).toList(),
|
||||||
onChanged: (value) => selectedRole = value ?? 'member',
|
onChanged: (value) => selectedRole = value ?? 'member',
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
@@ -399,7 +424,8 @@ class _OrganizationSettingsDialogState extends State<OrganizationSettingsDialog>
|
|||||||
'Requested to join',
|
'Requested to join',
|
||||||
style: TextStyle(color: AppTheme.secondaryText),
|
style: TextStyle(color: AppTheme.secondaryText),
|
||||||
),
|
),
|
||||||
trailing: _canManage ? Row(
|
trailing: _canManage
|
||||||
|
? Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
TextButton(
|
TextButton(
|
||||||
@@ -408,10 +434,14 @@ class _OrganizationSettingsDialogState extends State<OrganizationSettingsDialog>
|
|||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => _rejectJoinRequest(req.id),
|
onPressed: () => _rejectJoinRequest(req.id),
|
||||||
child: Text('Reject', style: TextStyle(color: AppTheme.errorColor)),
|
child: Text(
|
||||||
|
'Reject',
|
||||||
|
style: TextStyle(color: AppTheme.errorColor),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
) : null,
|
)
|
||||||
|
: null,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -429,10 +459,7 @@ class _OrganizationSettingsDialogState extends State<OrganizationSettingsDialog>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text(
|
Text(_inviteLink!, style: TextStyle(color: AppTheme.secondaryText)),
|
||||||
_inviteLink!,
|
|
||||||
style: TextStyle(color: AppTheme.secondaryText),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
Reference in New Issue
Block a user