feat(contact): implement contact editing functionality and update API integration
This commit is contained in:
parent
5fb18a13d2
commit
445cd75e03
@ -82,6 +82,7 @@ class AddContactController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> submitContact({
|
Future<void> submitContact({
|
||||||
|
String? id,
|
||||||
required String name,
|
required String name,
|
||||||
required String organization,
|
required String organization,
|
||||||
required List<Map<String, String>> emails,
|
required List<Map<String, String>> emails,
|
||||||
@ -96,10 +97,13 @@ class AddContactController extends GetxController {
|
|||||||
|
|
||||||
final tagObjects = enteredTags.map((tagName) {
|
final tagObjects = enteredTags.map((tagName) {
|
||||||
final tagId = tagsMap[tagName];
|
final tagId = tagsMap[tagName];
|
||||||
return tagId != null ? {"id": tagId, "name": tagName} : {"name": tagName};
|
return tagId != null
|
||||||
|
? {"id": tagId, "name": tagName}
|
||||||
|
: {"name": tagName};
|
||||||
}).toList();
|
}).toList();
|
||||||
|
|
||||||
final body = {
|
final body = {
|
||||||
|
if (id != null) "id": id,
|
||||||
"name": name,
|
"name": name,
|
||||||
"organization": organization,
|
"organization": organization,
|
||||||
"contactCategoryId": categoryId,
|
"contactCategoryId": categoryId,
|
||||||
@ -112,27 +116,30 @@ class AddContactController extends GetxController {
|
|||||||
"description": description,
|
"description": description,
|
||||||
};
|
};
|
||||||
|
|
||||||
logSafe("Submitting contact", sensitive: true);
|
logSafe("${id != null ? 'Updating' : 'Creating'} contact");
|
||||||
|
|
||||||
|
final response = id != null
|
||||||
|
? await ApiService.updateContact(id, body)
|
||||||
|
: await ApiService.createContact(body);
|
||||||
|
|
||||||
final response = await ApiService.createContact(body);
|
|
||||||
if (response == true) {
|
if (response == true) {
|
||||||
logSafe("Contact creation succeeded");
|
|
||||||
Get.back(result: true);
|
Get.back(result: true);
|
||||||
showAppSnackbar(
|
showAppSnackbar(
|
||||||
title: "Success",
|
title: "Success",
|
||||||
message: "Contact created successfully",
|
message: id != null
|
||||||
|
? "Contact updated successfully"
|
||||||
|
: "Contact created successfully",
|
||||||
type: SnackbarType.success,
|
type: SnackbarType.success,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
logSafe("Contact creation failed", level: LogLevel.error);
|
|
||||||
showAppSnackbar(
|
showAppSnackbar(
|
||||||
title: "Error",
|
title: "Error",
|
||||||
message: "Failed to create contact",
|
message: "Failed to ${id != null ? 'update' : 'create'} contact",
|
||||||
type: SnackbarType.error,
|
type: SnackbarType.error,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logSafe("Contact creation error: \$e", level: LogLevel.error);
|
logSafe("Submit contact error: $e", level: LogLevel.error);
|
||||||
showAppSnackbar(
|
showAppSnackbar(
|
||||||
title: "Error",
|
title: "Error",
|
||||||
message: "Something went wrong",
|
message: "Something went wrong",
|
||||||
@ -149,9 +156,12 @@ class AddContactController extends GetxController {
|
|||||||
|
|
||||||
final lower = query.toLowerCase();
|
final lower = query.toLowerCase();
|
||||||
filteredOrgSuggestions.assignAll(
|
filteredOrgSuggestions.assignAll(
|
||||||
organizationNames.where((name) => name.toLowerCase().contains(lower)).toList(),
|
organizationNames
|
||||||
|
.where((name) => name.toLowerCase().contains(lower))
|
||||||
|
.toList(),
|
||||||
);
|
);
|
||||||
logSafe("Filtered organization suggestions for: \$query", level: LogLevel.debug);
|
logSafe("Filtered organization suggestions for: \$query",
|
||||||
|
level: LogLevel.debug);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> fetchGlobalProjects() async {
|
Future<void> fetchGlobalProjects() async {
|
||||||
@ -197,7 +207,10 @@ class AddContactController extends GetxController {
|
|||||||
|
|
||||||
final lower = query.toLowerCase();
|
final lower = query.toLowerCase();
|
||||||
filteredSuggestions.assignAll(
|
filteredSuggestions.assignAll(
|
||||||
tags.where((tag) => tag.toLowerCase().contains(lower) && !enteredTags.contains(tag)).toList(),
|
tags
|
||||||
|
.where((tag) =>
|
||||||
|
tag.toLowerCase().contains(lower) && !enteredTags.contains(tag))
|
||||||
|
.toList(),
|
||||||
);
|
);
|
||||||
logSafe("Filtered tag suggestions for: \$query", level: LogLevel.debug);
|
logSafe("Filtered tag suggestions for: \$query", level: LogLevel.debug);
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ class ApiEndpoints {
|
|||||||
static const String getDirectoryContactTags = "/master/contact-tags";
|
static const String getDirectoryContactTags = "/master/contact-tags";
|
||||||
static const String getDirectoryOrganization = "/directory/organization";
|
static const String getDirectoryOrganization = "/directory/organization";
|
||||||
static const String createContact = "/directory";
|
static const String createContact = "/directory";
|
||||||
|
static const String updateContact = "/directory";
|
||||||
static const String getDirectoryNotes = "/directory/notes";
|
static const String getDirectoryNotes = "/directory/notes";
|
||||||
static const String updateDirectoryNotes = "/directory/note";
|
static const String updateDirectoryNotes = "/directory/note";
|
||||||
}
|
}
|
||||||
|
@ -365,6 +365,30 @@ class ApiService {
|
|||||||
return data is List ? data : null;
|
return data is List ? data : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<bool> updateContact(
|
||||||
|
String contactId, Map<String, dynamic> payload) async {
|
||||||
|
try {
|
||||||
|
final endpoint = "${ApiEndpoints.updateContact}/$contactId";
|
||||||
|
|
||||||
|
logSafe("Updating contact [$contactId] with payload: $payload");
|
||||||
|
|
||||||
|
final response = await _putRequest(endpoint, payload);
|
||||||
|
if (response != null) {
|
||||||
|
final json = jsonDecode(response.body);
|
||||||
|
if (json['success'] == true) {
|
||||||
|
logSafe("Contact updated successfully.");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
logSafe("Update contact failed: ${json['message']}",
|
||||||
|
level: LogLevel.warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logSafe("Error updating contact: $e", level: LogLevel.error);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static Future<bool> createContact(Map<String, dynamic> payload) async {
|
static Future<bool> createContact(Map<String, dynamic> payload) async {
|
||||||
try {
|
try {
|
||||||
logSafe("Submitting contact payload: $payload");
|
logSafe("Submitting contact payload: $payload");
|
||||||
|
@ -1,26 +1,80 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:marco/controller/directory/add_contact_controller.dart';
|
import 'package:marco/controller/directory/add_contact_controller.dart';
|
||||||
import 'package:marco/helpers/widgets/my_spacing.dart';
|
import 'package:marco/helpers/widgets/my_spacing.dart';
|
||||||
import 'package:marco/helpers/widgets/my_text.dart';
|
import 'package:marco/helpers/widgets/my_text.dart';
|
||||||
import 'package:marco/helpers/widgets/my_text_style.dart';
|
import 'package:marco/helpers/widgets/my_text_style.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:marco/model/directory/contact_model.dart';
|
||||||
|
|
||||||
class AddContactBottomSheet extends StatelessWidget {
|
class AddContactBottomSheet extends StatelessWidget {
|
||||||
AddContactBottomSheet({super.key}) {
|
final ContactModel? existingContact;
|
||||||
|
|
||||||
|
AddContactBottomSheet({super.key, this.existingContact}) {
|
||||||
controller.resetForm();
|
controller.resetForm();
|
||||||
|
|
||||||
nameController.clear();
|
nameController.text = existingContact?.name ?? '';
|
||||||
orgController.clear();
|
orgController.text = existingContact?.organization ?? '';
|
||||||
tagTextController.clear();
|
tagTextController.clear();
|
||||||
addressController.clear();
|
addressController.text = existingContact?.address ?? '';
|
||||||
descriptionController.clear();
|
descriptionController.text = existingContact?.description ?? '';
|
||||||
|
|
||||||
emailControllers.add(TextEditingController());
|
if (existingContact != null) {
|
||||||
emailLabels.add('Office'.obs);
|
emailControllers.clear();
|
||||||
|
emailLabels.clear();
|
||||||
|
for (var email in existingContact!.contactEmails) {
|
||||||
|
emailControllers.add(TextEditingController(text: email.emailAddress));
|
||||||
|
emailLabels.add((email.label ?? 'Office').obs);
|
||||||
|
}
|
||||||
|
|
||||||
phoneControllers.add(TextEditingController());
|
if (emailControllers.isEmpty) {
|
||||||
phoneLabels.add('Work'.obs);
|
emailControllers.add(TextEditingController());
|
||||||
|
emailLabels.add('Office'.obs);
|
||||||
|
}
|
||||||
|
|
||||||
|
phoneControllers.clear();
|
||||||
|
phoneLabels.clear();
|
||||||
|
for (var phone in existingContact!.contactPhones) {
|
||||||
|
phoneControllers.add(TextEditingController(text: phone.phoneNumber));
|
||||||
|
phoneLabels.add((phone.label ?? 'Work').obs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (phoneControllers.isEmpty) {
|
||||||
|
phoneControllers.add(TextEditingController());
|
||||||
|
phoneLabels.add('Work'.obs);
|
||||||
|
}
|
||||||
|
|
||||||
|
controller.selectedCategory.value =
|
||||||
|
existingContact!.contactCategory?.name ?? '';
|
||||||
|
|
||||||
|
if (existingContact!.projectIds?.isNotEmpty == true) {
|
||||||
|
controller.selectedProject.value = controller.globalProjects
|
||||||
|
.firstWhereOrNull(
|
||||||
|
(e) => e == existingContact!.projectIds!.first,
|
||||||
|
)
|
||||||
|
?.toString() ??
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existingContact!.bucketIds.isNotEmpty) {
|
||||||
|
controller.selectedBucket.value = controller.buckets
|
||||||
|
.firstWhereOrNull(
|
||||||
|
(b) => b == existingContact!.bucketIds.first,
|
||||||
|
)
|
||||||
|
?.toString() ??
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
|
||||||
|
controller.enteredTags.assignAll(
|
||||||
|
existingContact!.tags.map((tag) => tag.name).toList(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
emailControllers.add(TextEditingController());
|
||||||
|
emailLabels.add('Office'.obs);
|
||||||
|
|
||||||
|
phoneControllers.add(TextEditingController());
|
||||||
|
phoneLabels.add('Work'.obs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final controller = Get.put(AddContactController());
|
final controller = Get.put(AddContactController());
|
||||||
@ -113,10 +167,9 @@ class AddContactBottomSheet extends StatelessWidget {
|
|||||||
MyText.labelMedium(label),
|
MyText.labelMedium(label),
|
||||||
MySpacing.height(8),
|
MySpacing.height(8),
|
||||||
_popupSelector(
|
_popupSelector(
|
||||||
hint: "Label",
|
hint: "Label",
|
||||||
selectedValue: selectedLabel,
|
selectedValue: selectedLabel,
|
||||||
options: options,
|
options: options),
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -128,40 +181,35 @@ class AddContactBottomSheet extends StatelessWidget {
|
|||||||
MyText.labelMedium(inputLabel),
|
MyText.labelMedium(inputLabel),
|
||||||
MySpacing.height(8),
|
MySpacing.height(8),
|
||||||
TextFormField(
|
TextFormField(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
keyboardType: inputType,
|
keyboardType: inputType,
|
||||||
maxLength: inputType == TextInputType.phone ? 10 : null,
|
maxLength: inputType == TextInputType.phone ? 10 : null,
|
||||||
inputFormatters: inputType == TextInputType.phone
|
inputFormatters: inputType == TextInputType.phone
|
||||||
? [FilteringTextInputFormatter.digitsOnly]
|
? [FilteringTextInputFormatter.digitsOnly]
|
||||||
: [],
|
: [],
|
||||||
decoration: _inputDecoration("Enter $inputLabel").copyWith(
|
decoration: _inputDecoration("Enter $inputLabel")
|
||||||
counterText: "", // hides length indicator
|
.copyWith(counterText: ""),
|
||||||
),
|
validator: (value) {
|
||||||
validator: (value) {
|
if (value == null || value.trim().isEmpty) {
|
||||||
if (value == null || value.trim().isEmpty) {
|
return "$inputLabel is required";
|
||||||
return "$inputLabel is required";
|
}
|
||||||
}
|
final trimmed = value.trim();
|
||||||
|
if (inputType == TextInputType.phone) {
|
||||||
final trimmed = value.trim();
|
if (!RegExp(r'^\d{10}$').hasMatch(trimmed)) {
|
||||||
|
return "Enter a valid 10-digit phone number";
|
||||||
if (inputType == TextInputType.phone) {
|
}
|
||||||
if (!RegExp(r'^\d{10}$').hasMatch(trimmed)) {
|
if (RegExp(r'^0+$').hasMatch(trimmed)) {
|
||||||
return "Enter a valid 10-digit phone number";
|
return "Phone number cannot be all zeroes";
|
||||||
}
|
}
|
||||||
if (RegExp(r'^0+$').hasMatch(trimmed)) {
|
}
|
||||||
return "Phone number cannot be all zeroes";
|
if (inputType == TextInputType.emailAddress &&
|
||||||
}
|
!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$')
|
||||||
}
|
.hasMatch(trimmed)) {
|
||||||
|
return "Enter a valid email address";
|
||||||
if (inputType == TextInputType.emailAddress &&
|
}
|
||||||
!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(trimmed)) {
|
return null;
|
||||||
return "Enter a valid email address";
|
},
|
||||||
}
|
),
|
||||||
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -177,53 +225,49 @@ class AddContactBottomSheet extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildEmailList() {
|
Widget _buildEmailList() => Column(
|
||||||
return Column(
|
children: List.generate(emailControllers.length, (index) {
|
||||||
children: List.generate(emailControllers.length, (index) {
|
return Padding(
|
||||||
return Padding(
|
padding: const EdgeInsets.only(bottom: 12),
|
||||||
padding: const EdgeInsets.only(bottom: 12),
|
child: _buildLabeledRow(
|
||||||
child: _buildLabeledRow(
|
"Email Label",
|
||||||
"Email Label",
|
emailLabels[index],
|
||||||
emailLabels[index],
|
["Office", "Personal", "Other"],
|
||||||
["Office", "Personal", "Other"],
|
"Email",
|
||||||
"Email",
|
emailControllers[index],
|
||||||
emailControllers[index],
|
TextInputType.emailAddress,
|
||||||
TextInputType.emailAddress,
|
onRemove: emailControllers.length > 1
|
||||||
onRemove: emailControllers.length > 1
|
? () {
|
||||||
? () {
|
emailControllers.removeAt(index);
|
||||||
emailControllers.removeAt(index);
|
emailLabels.removeAt(index);
|
||||||
emailLabels.removeAt(index);
|
}
|
||||||
}
|
: null,
|
||||||
: null,
|
),
|
||||||
),
|
);
|
||||||
);
|
}),
|
||||||
}),
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildPhoneList() {
|
Widget _buildPhoneList() => Column(
|
||||||
return Column(
|
children: List.generate(phoneControllers.length, (index) {
|
||||||
children: List.generate(phoneControllers.length, (index) {
|
return Padding(
|
||||||
return Padding(
|
padding: const EdgeInsets.only(bottom: 12),
|
||||||
padding: const EdgeInsets.only(bottom: 12),
|
child: _buildLabeledRow(
|
||||||
child: _buildLabeledRow(
|
"Phone Label",
|
||||||
"Phone Label",
|
phoneLabels[index],
|
||||||
phoneLabels[index],
|
["Work", "Mobile", "Other"],
|
||||||
["Work", "Mobile", "Other"],
|
"Phone",
|
||||||
"Phone",
|
phoneControllers[index],
|
||||||
phoneControllers[index],
|
TextInputType.phone,
|
||||||
TextInputType.phone,
|
onRemove: phoneControllers.length > 1
|
||||||
onRemove: phoneControllers.length > 1
|
? () {
|
||||||
? () {
|
phoneControllers.removeAt(index);
|
||||||
phoneControllers.removeAt(index);
|
phoneLabels.removeAt(index);
|
||||||
phoneLabels.removeAt(index);
|
}
|
||||||
}
|
: null,
|
||||||
: null,
|
),
|
||||||
),
|
);
|
||||||
);
|
}),
|
||||||
}),
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _dropdownField({
|
Widget _dropdownField({
|
||||||
required String label,
|
required String label,
|
||||||
@ -350,8 +394,13 @@ class AddContactBottomSheet extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Center(
|
Center(
|
||||||
child: MyText.titleMedium("Create New Contact",
|
child: MyText.titleMedium(
|
||||||
fontWeight: 700)),
|
existingContact != null
|
||||||
|
? "Edit Contact"
|
||||||
|
: "Create New Contact",
|
||||||
|
fontWeight: 700,
|
||||||
|
),
|
||||||
|
),
|
||||||
MySpacing.height(24),
|
MySpacing.height(24),
|
||||||
_sectionLabel("Basic Info"),
|
_sectionLabel("Basic Info"),
|
||||||
MySpacing.height(16),
|
MySpacing.height(16),
|
||||||
@ -519,8 +568,9 @@ class AddContactBottomSheet extends StatelessWidget {
|
|||||||
"phoneNumber": entry.value.text.trim(),
|
"phoneNumber": entry.value.text.trim(),
|
||||||
})
|
})
|
||||||
.toList();
|
.toList();
|
||||||
|
print("Submitting contact payload , id: ${existingContact?.id}");
|
||||||
controller.submitContact(
|
controller.submitContact(
|
||||||
|
id: existingContact?.id,
|
||||||
name: nameController.text.trim(),
|
name: nameController.text.trim(),
|
||||||
organization: orgController.text.trim(),
|
organization: orgController.text.trim(),
|
||||||
emails: emails,
|
emails: emails,
|
||||||
|
@ -14,6 +14,7 @@ import 'package:marco/helpers/widgets/Directory/comment_editor_card.dart';
|
|||||||
import 'package:flutter_quill_delta_from_html/flutter_quill_delta_from_html.dart';
|
import 'package:flutter_quill_delta_from_html/flutter_quill_delta_from_html.dart';
|
||||||
import 'package:marco/model/directory/add_comment_bottom_sheet.dart';
|
import 'package:marco/model/directory/add_comment_bottom_sheet.dart';
|
||||||
import 'package:marco/helpers/utils/date_time_utils.dart';
|
import 'package:marco/helpers/utils/date_time_utils.dart';
|
||||||
|
import 'package:marco/model/directory/add_contact_bottom_sheet.dart';
|
||||||
|
|
||||||
class ContactDetailScreen extends StatefulWidget {
|
class ContactDetailScreen extends StatefulWidget {
|
||||||
final ContactModel contact;
|
final ContactModel contact;
|
||||||
@ -252,47 +253,72 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
|||||||
|
|
||||||
final category = widget.contact.contactCategory?.name ?? "-";
|
final category = widget.contact.contactCategory?.name ?? "-";
|
||||||
|
|
||||||
return SingleChildScrollView(
|
return Stack(
|
||||||
padding: MySpacing.xy(8, 8),
|
children: [
|
||||||
child: Column(
|
SingleChildScrollView(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
padding: MySpacing.fromLTRB(8, 8, 8, 80),
|
||||||
children: [
|
child: Column(
|
||||||
_infoCard("Basic Info", [
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
_iconInfoRow(Icons.email, "Email", email,
|
children: [
|
||||||
onTap: () => LauncherUtils.launchEmail(email),
|
MySpacing.height(12),
|
||||||
onLongPress: () =>
|
_infoCard("Basic Info", [
|
||||||
LauncherUtils.copyToClipboard(email, typeLabel: "Email")),
|
_iconInfoRow(Icons.email, "Email", email,
|
||||||
_iconInfoRow(Icons.phone, "Phone", phone,
|
onTap: () => LauncherUtils.launchEmail(email),
|
||||||
onTap: () => LauncherUtils.launchPhone(phone),
|
onLongPress: () => LauncherUtils.copyToClipboard(email,
|
||||||
onLongPress: () =>
|
typeLabel: "Email")),
|
||||||
LauncherUtils.copyToClipboard(phone, typeLabel: "Phone")),
|
_iconInfoRow(Icons.phone, "Phone", phone,
|
||||||
_iconInfoRow(Icons.location_on, "Address", widget.contact.address),
|
onTap: () => LauncherUtils.launchPhone(phone),
|
||||||
]),
|
onLongPress: () => LauncherUtils.copyToClipboard(phone,
|
||||||
_infoCard("Organization", [
|
typeLabel: "Phone")),
|
||||||
_iconInfoRow(
|
_iconInfoRow(
|
||||||
Icons.business, "Organization", widget.contact.organization),
|
Icons.location_on, "Address", widget.contact.address),
|
||||||
_iconInfoRow(Icons.category, "Category", category),
|
]),
|
||||||
]),
|
_infoCard("Organization", [
|
||||||
_infoCard("Meta Info", [
|
_iconInfoRow(Icons.business, "Organization",
|
||||||
_iconInfoRow(Icons.label, "Tags", tags.isNotEmpty ? tags : "-"),
|
widget.contact.organization),
|
||||||
_iconInfoRow(Icons.folder_shared, "Contact Buckets",
|
_iconInfoRow(Icons.category, "Category", category),
|
||||||
bucketNames.isNotEmpty ? bucketNames : "-"),
|
]),
|
||||||
_iconInfoRow(Icons.work_outline, "Projects", projectNames),
|
_infoCard("Meta Info", [
|
||||||
]),
|
_iconInfoRow(Icons.label, "Tags", tags.isNotEmpty ? tags : "-"),
|
||||||
_infoCard("Description", [
|
_iconInfoRow(Icons.folder_shared, "Contact Buckets",
|
||||||
MySpacing.height(6),
|
bucketNames.isNotEmpty ? bucketNames : "-"),
|
||||||
Align(
|
_iconInfoRow(Icons.work_outline, "Projects", projectNames),
|
||||||
alignment: Alignment.topLeft,
|
]),
|
||||||
child: MyText.bodyMedium(
|
_infoCard("Description", [
|
||||||
widget.contact.description,
|
MySpacing.height(6),
|
||||||
color: Colors.grey[800],
|
Align(
|
||||||
maxLines: 10,
|
alignment: Alignment.topLeft,
|
||||||
textAlign: TextAlign.left,
|
child: MyText.bodyMedium(
|
||||||
),
|
widget.contact.description,
|
||||||
|
color: Colors.grey[800],
|
||||||
|
maxLines: 10,
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
])
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
bottom: 20,
|
||||||
|
right: 20,
|
||||||
|
child: FloatingActionButton.extended(
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
onPressed: () {
|
||||||
|
Get.bottomSheet(
|
||||||
|
AddContactBottomSheet(existingContact: widget.contact),
|
||||||
|
isScrollControlled: true,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.edit, color: Colors.white),
|
||||||
|
label: const Text(
|
||||||
|
"Edit Contact",
|
||||||
|
style: TextStyle(color: Colors.white),
|
||||||
),
|
),
|
||||||
])
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,7 +434,6 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
MySpacing.height(12),
|
|
||||||
// Comment Content
|
// Comment Content
|
||||||
if (isEditing && quillController != null)
|
if (isEditing && quillController != null)
|
||||||
CommentEditorCard(
|
CommentEditorCard(
|
||||||
@ -453,7 +478,7 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
|||||||
bottom: 20,
|
bottom: 20,
|
||||||
right: 20,
|
right: 20,
|
||||||
child: FloatingActionButton.extended(
|
child: FloatingActionButton.extended(
|
||||||
backgroundColor: Colors.indigo,
|
backgroundColor: Colors.red,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Get.bottomSheet(
|
Get.bottomSheet(
|
||||||
AddCommentBottomSheet(contactId: contactId),
|
AddCommentBottomSheet(contactId: contactId),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user