resolved directory data update issues #55

Merged
vaibhav.surve merged 2 commits from Vaibhav_Issue_17_07_2025 into main 2025-07-18 06:58:29 +00:00
5 changed files with 85 additions and 50 deletions

View File

@ -3,6 +3,7 @@ import 'package:marco/helpers/services/api_service.dart';
import 'package:marco/helpers/services/app_logger.dart';
import 'package:marco/helpers/widgets/my_snackbar.dart';
import 'package:marco/controller/directory/directory_controller.dart';
import 'package:marco/controller/directory/notes_controller.dart';
class AddCommentController extends GetxController {
final String contactId;
@ -39,6 +40,10 @@ class AddCommentController extends GetxController {
final directoryController = Get.find<DirectoryController>();
await directoryController.fetchCommentsForContact(contactId);
final notesController = Get.find<NotesController>();
await notesController.fetchNotes(
pageSize: 1000, pageNumber: 1); // Fixed here
Get.back(result: true);
showAppSnackbar(
@ -46,13 +51,6 @@ class AddCommentController extends GetxController {
message: "Your comment has been successfully added.",
type: SnackbarType.success,
);
} else {
logSafe("Comment submission failed", level: LogLevel.error);
showAppSnackbar(
title: "Submission Failed",
message: "Unable to add the comment. Please try again later.",
type: SnackbarType.error,
);
}
} catch (e) {
logSafe("Error while submitting comment: $e", level: LogLevel.error);

View File

@ -20,7 +20,8 @@ class ApiService {
final token = await LocalStorage.getJwtToken();
if (token == null) {
logSafe("No JWT token found.");
logSafe("No JWT token found. Logging out...");
await LocalStorage.logout();
return null;
}
@ -31,7 +32,7 @@ class ApiService {
if (refreshed) {
return await LocalStorage.getJwtToken();
} else {
logSafe("Token refresh failed. Logging out...");
logSafe("Token refresh failed. Logging out immediately...");
await LocalStorage.logout();
return null;
}
@ -47,10 +48,16 @@ class ApiService {
final refreshed = await AuthService.refreshToken();
if (refreshed) {
return await LocalStorage.getJwtToken();
} else {
logSafe("Token refresh failed (near expiry). Logging out...");
await LocalStorage.logout();
return null;
}
}
} catch (e) {
logSafe("Token decoding error: $e", level: LogLevel.error);
await LocalStorage.logout();
return null;
}
return token;
@ -107,8 +114,9 @@ class ApiService {
}) async {
String? token = await _getToken();
if (token == null) {
logSafe("Token is null. Cannot proceed with GET request.",
logSafe("Token is null. Forcing logout from GET request.",
level: LogLevel.error);
await LocalStorage.logout();
return null;
}
@ -141,8 +149,9 @@ class ApiService {
);
}
logSafe("Token refresh failed. Aborting request.",
logSafe("Token refresh failed. Logging out user.",
level: LogLevel.error);
await LocalStorage.logout();
}
return response;
@ -246,7 +255,7 @@ class ApiService {
}
/// Directory calling the API
static Future<bool> deleteBucket(String id) async {
final endpoint = "${ApiEndpoints.updateBucket}/$id";

View File

@ -11,7 +11,8 @@ import 'package:marco/helpers/widgets/my_snackbar.dart';
class EditBucketBottomSheet {
static void show(BuildContext context, ContactBucket bucket,
List<EmployeeModel> allEmployees) {
List<EmployeeModel> allEmployees,
{required String ownerId}) {
final ManageBucketController controller = Get.find();
final nameController = TextEditingController(text: bucket.name);
@ -192,13 +193,15 @@ class EditBucketBottomSheet {
controlAffinity:
ListTileControlAffinity.leading,
value: selectedIds.contains(emp.id),
onChanged: (val) {
if (val == true) {
selectedIds.add(emp.id);
} else {
selectedIds.remove(emp.id);
}
},
onChanged: emp.id == ownerId
? null
: (val) {
if (val == true) {
selectedIds.add(emp.id);
} else {
selectedIds.remove(emp.id);
}
},
title: Text(
fullName.isNotEmpty ? fullName : 'Unnamed',
style: const TextStyle(fontSize: 13),

View File

@ -77,18 +77,17 @@ String _convertDeltaToHtml(dynamic delta) {
class _ContactDetailScreenState extends State<ContactDetailScreen> {
late final DirectoryController directoryController;
late final ProjectController projectController;
late ContactModel contact;
@override
void initState() {
super.initState();
directoryController = Get.find<DirectoryController>();
projectController = Get.find<ProjectController>();
contact = widget.contact;
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!directoryController.contactCommentsMap
.containsKey(widget.contact.id)) {
directoryController.fetchCommentsForContact(widget.contact.id);
}
directoryController.fetchCommentsForContact(contact.id);
});
}
@ -133,7 +132,8 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
IconButton(
icon: const Icon(Icons.arrow_back_ios_new,
color: Colors.black, size: 20),
onPressed: () => Get.back(),
onPressed: () =>
Get.offAllNamed('/dashboard/directory-main-page'),
),
MySpacing.width(8),
Expanded(
@ -184,9 +184,9 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
Row(
children: [
Avatar(
firstName: widget.contact.name.split(" ").first,
lastName: widget.contact.name.split(" ").length > 1
? widget.contact.name.split(" ").last
firstName: contact.name.split(" ").first,
lastName: contact.name.split(" ").length > 1
? contact.name.split(" ").last
: "",
size: 35,
backgroundColor: Colors.indigo,
@ -195,10 +195,10 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MyText.titleSmall(widget.contact.name,
MyText.titleSmall(contact.name,
fontWeight: 600, color: Colors.black),
MySpacing.height(2),
MyText.bodySmall(widget.contact.organization,
MyText.bodySmall(contact.organization,
fontWeight: 500, color: Colors.grey[700]),
],
),
@ -226,24 +226,24 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
}
Widget _buildDetailsTab() {
final email = widget.contact.contactEmails.isNotEmpty
? widget.contact.contactEmails.first.emailAddress
final email = contact.contactEmails.isNotEmpty
? contact.contactEmails.first.emailAddress
: "-";
final phone = widget.contact.contactPhones.isNotEmpty
? widget.contact.contactPhones.first.phoneNumber
final phone = contact.contactPhones.isNotEmpty
? contact.contactPhones.first.phoneNumber
: "-";
final tags = widget.contact.tags.map((e) => e.name).join(", ");
final tags = contact.tags.map((e) => e.name).join(", ");
final bucketNames = widget.contact.bucketIds
final bucketNames = contact.bucketIds
.map((id) => directoryController.contactBuckets
.firstWhereOrNull((b) => b.id == id)
?.name)
.whereType<String>()
.join(", ");
final projectNames = widget.contact.projectIds
final projectNames = contact.projectIds
?.map((id) => projectController.projects
.firstWhereOrNull((p) => p.id == id)
?.name)
@ -251,7 +251,7 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
.join(", ") ??
"-";
final category = widget.contact.contactCategory?.name ?? "-";
final category = contact.contactCategory?.name ?? "-";
return Stack(
children: [
@ -270,12 +270,11 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
onTap: () => LauncherUtils.launchPhone(phone),
onLongPress: () => LauncherUtils.copyToClipboard(phone,
typeLabel: "Phone")),
_iconInfoRow(
Icons.location_on, "Address", widget.contact.address),
_iconInfoRow(Icons.location_on, "Address", contact.address),
]),
_infoCard("Organization", [
_iconInfoRow(Icons.business, "Organization",
widget.contact.organization),
_iconInfoRow(
Icons.business, "Organization", contact.organization),
_iconInfoRow(Icons.category, "Category", category),
]),
_infoCard("Meta Info", [
@ -289,7 +288,7 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
Align(
alignment: Alignment.topLeft,
child: MyText.bodyMedium(
widget.contact.description,
contact.description,
color: Colors.grey[800],
maxLines: 10,
textAlign: TextAlign.left,
@ -304,12 +303,25 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
right: 20,
child: FloatingActionButton.extended(
backgroundColor: Colors.red,
onPressed: () {
Get.bottomSheet(
AddContactBottomSheet(existingContact: widget.contact),
onPressed: () async {
final result = await Get.bottomSheet(
AddContactBottomSheet(existingContact: contact),
isScrollControlled: true,
backgroundColor: Colors.transparent,
);
if (result == true) {
await directoryController.fetchContacts();
final updated =
directoryController.allContacts.firstWhereOrNull(
(c) => c.id == contact.id,
);
if (updated != null) {
setState(() {
contact = updated;
});
}
}
},
icon: const Icon(Icons.edit, color: Colors.white),
label: const Text(
@ -324,7 +336,7 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
Widget _buildCommentsTab(BuildContext context) {
return Obx(() {
final contactId = widget.contact.id;
final contactId = contact.id;
if (!directoryController.contactCommentsMap.containsKey(contactId)) {
return const Center(child: CircularProgressIndicator());
@ -447,8 +459,15 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
final htmlOutput = _convertDeltaToHtml(delta);
final updated =
comment.copyWith(note: htmlOutput);
await directoryController
.updateComment(updated);
// Re-fetch comments to get updated list
await directoryController
.fetchCommentsForContact(contactId);
// Exit editing mode
directoryController.editingCommentId.value =
null;
},
@ -479,11 +498,16 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
right: 20,
child: FloatingActionButton.extended(
backgroundColor: Colors.red,
onPressed: () {
Get.bottomSheet(
onPressed: () async {
final result = await Get.bottomSheet(
AddCommentBottomSheet(contactId: contactId),
isScrollControlled: true,
);
if (result == true) {
await directoryController
.fetchCommentsForContact(contactId);
}
},
icon: const Icon(Icons.add_comment, color: Colors.white),
label: const Text(

View File

@ -194,7 +194,8 @@ class _ManageBucketsScreenState extends State<ManageBucketsScreen> {
Future.delayed(const Duration(milliseconds: 300),
() {
EditBucketBottomSheet.show(context, bucket,
manageBucketController.allEmployees);
manageBucketController.allEmployees,
ownerId: bucket.createdBy.id);
});
},
);