215 lines
6.7 KiB
Dart
215 lines
6.7 KiB
Dart
import 'package:flutter/material.dart';
|
||
import 'package:marco/helpers/widgets/my_text.dart';
|
||
import 'package:marco/helpers/widgets/avatar.dart';
|
||
import 'package:marco/helpers/widgets/my_spacing.dart';
|
||
import 'package:marco/model/directory/contact_bucket_list_model.dart';
|
||
|
||
class TeamMembersBottomSheet {
|
||
static void show(
|
||
BuildContext context,
|
||
ContactBucket bucket,
|
||
List<dynamic> members, {
|
||
bool canEdit = false,
|
||
VoidCallback? onEdit,
|
||
}) {
|
||
final ownerId = bucket.createdBy.id;
|
||
|
||
// Ensure owner is first
|
||
members.sort((a, b) {
|
||
if (a.id == ownerId) return -1;
|
||
if (b.id == ownerId) return 1;
|
||
return 0;
|
||
});
|
||
|
||
showModalBottomSheet(
|
||
context: context,
|
||
isScrollControlled: true,
|
||
backgroundColor: Colors.transparent,
|
||
isDismissible: true,
|
||
enableDrag: true,
|
||
builder: (context) {
|
||
return SafeArea(
|
||
child: Container(
|
||
decoration: const BoxDecoration(
|
||
color: Colors.white,
|
||
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
|
||
),
|
||
child: DraggableScrollableSheet(
|
||
expand: false,
|
||
initialChildSize: 0.75,
|
||
minChildSize: 0.55,
|
||
maxChildSize: 0.95,
|
||
builder: (context, scrollController) {
|
||
return Column(
|
||
children: [
|
||
MySpacing.height(8),
|
||
_buildGrabHandle(),
|
||
MySpacing.height(10),
|
||
MyText.titleMedium('Bucket Details', fontWeight: 700),
|
||
MySpacing.height(12),
|
||
_buildHeader(bucket, canEdit, onEdit),
|
||
_buildInfo(bucket, members.length, canEdit),
|
||
MySpacing.height(6),
|
||
_buildMembersTitle(),
|
||
MySpacing.height(4),
|
||
Expanded(child: _buildMemberList(members, ownerId, scrollController)),
|
||
MySpacing.height(8),
|
||
],
|
||
);
|
||
},
|
||
),
|
||
),
|
||
);
|
||
},
|
||
);
|
||
}
|
||
|
||
static Widget _buildGrabHandle() {
|
||
return Container(
|
||
width: 36,
|
||
height: 4,
|
||
decoration: BoxDecoration(
|
||
color: Colors.grey.shade300,
|
||
borderRadius: BorderRadius.circular(2),
|
||
),
|
||
);
|
||
}
|
||
|
||
static Widget _buildHeader(ContactBucket bucket, bool canEdit, VoidCallback? onEdit) {
|
||
return Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||
child: Row(
|
||
children: [
|
||
Expanded(
|
||
child: MyText.titleMedium(bucket.name, fontWeight: 700),
|
||
),
|
||
if (canEdit)
|
||
IconButton(
|
||
onPressed: onEdit,
|
||
icon: const Icon(Icons.edit, color: Colors.red),
|
||
tooltip: 'Edit Bucket',
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
static Widget _buildInfo(ContactBucket bucket, int totalMembers, bool canEdit) {
|
||
return Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
if (bucket.description.isNotEmpty)
|
||
Padding(
|
||
padding: const EdgeInsets.only(bottom: 6),
|
||
child: MyText.bodySmall(
|
||
bucket.description,
|
||
color: Colors.grey[700],
|
||
),
|
||
),
|
||
Row(
|
||
children: [
|
||
const Icon(Icons.contacts_outlined, size: 14, color: Colors.grey),
|
||
const SizedBox(width: 4),
|
||
MyText.labelSmall(
|
||
'${bucket.numberOfContacts} contact(s)',
|
||
fontWeight: 600,
|
||
color: Colors.red,
|
||
),
|
||
const SizedBox(width: 12),
|
||
const Icon(Icons.ios_share_outlined, size: 14, color: Colors.grey),
|
||
const SizedBox(width: 4),
|
||
MyText.labelSmall(
|
||
'Shared with ($totalMembers)',
|
||
fontWeight: 600,
|
||
color: Colors.indigo,
|
||
),
|
||
],
|
||
),
|
||
MySpacing.height(8),
|
||
Row(
|
||
children: [
|
||
const Icon(Icons.edit_outlined, size: 14, color: Colors.grey),
|
||
const SizedBox(width: 4),
|
||
MyText.labelSmall(
|
||
canEdit ? 'Can be edited by you' : 'You don’t have edit access',
|
||
fontWeight: 600,
|
||
color: canEdit ? Colors.green : Colors.grey,
|
||
),
|
||
],
|
||
),
|
||
MySpacing.height(8),
|
||
const Divider(thickness: 1),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
static Widget _buildMembersTitle() {
|
||
return Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||
child: MyText.labelLarge('Shared with', fontWeight: 700, color: Colors.black),
|
||
);
|
||
}
|
||
|
||
static Widget _buildMemberList(List<dynamic> members, String ownerId, ScrollController scrollController) {
|
||
if (members.isEmpty) {
|
||
return Center(
|
||
child: MyText.bodySmall(
|
||
"No team members found.",
|
||
fontWeight: 600,
|
||
color: Colors.grey,
|
||
),
|
||
);
|
||
}
|
||
|
||
return ListView.separated(
|
||
controller: scrollController,
|
||
itemCount: members.length,
|
||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||
separatorBuilder: (_, __) => const SizedBox(height: 4),
|
||
itemBuilder: (context, index) {
|
||
final member = members[index];
|
||
final firstName = member.firstName ?? '';
|
||
final lastName = member.lastName ?? '';
|
||
final isOwner = member.id == ownerId;
|
||
|
||
return ListTile(
|
||
dense: true,
|
||
contentPadding: EdgeInsets.zero,
|
||
leading: Avatar(firstName: firstName, lastName: lastName, size: 32),
|
||
title: Row(
|
||
children: [
|
||
Expanded(
|
||
child: MyText.bodyMedium(
|
||
'${firstName.isNotEmpty ? firstName : 'Unnamed'} ${lastName.isNotEmpty ? lastName : ''}',
|
||
fontWeight: 600,
|
||
),
|
||
),
|
||
if (isOwner)
|
||
Container(
|
||
margin: const EdgeInsets.only(left: 6),
|
||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||
decoration: BoxDecoration(
|
||
color: Colors.red.shade50,
|
||
borderRadius: BorderRadius.circular(4),
|
||
),
|
||
child: MyText.labelSmall(
|
||
"Owner",
|
||
fontWeight: 600,
|
||
color: Colors.red,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
subtitle: MyText.bodySmall(
|
||
member.jobRole ?? '',
|
||
color: Colors.grey.shade600,
|
||
),
|
||
);
|
||
},
|
||
);
|
||
}
|
||
}
|