Merge pull request 'resolved directory data update issues' (#55) from Vaibhav_Issue_17_07_2025 into main
Reviewed-on: #55
This commit is contained in:
commit
60060eaa5e
@ -3,6 +3,7 @@ import 'package:marco/helpers/services/api_service.dart';
|
|||||||
import 'package:marco/helpers/services/app_logger.dart';
|
import 'package:marco/helpers/services/app_logger.dart';
|
||||||
import 'package:marco/helpers/widgets/my_snackbar.dart';
|
import 'package:marco/helpers/widgets/my_snackbar.dart';
|
||||||
import 'package:marco/controller/directory/directory_controller.dart';
|
import 'package:marco/controller/directory/directory_controller.dart';
|
||||||
|
import 'package:marco/controller/directory/notes_controller.dart';
|
||||||
|
|
||||||
class AddCommentController extends GetxController {
|
class AddCommentController extends GetxController {
|
||||||
final String contactId;
|
final String contactId;
|
||||||
@ -39,6 +40,10 @@ class AddCommentController extends GetxController {
|
|||||||
final directoryController = Get.find<DirectoryController>();
|
final directoryController = Get.find<DirectoryController>();
|
||||||
await directoryController.fetchCommentsForContact(contactId);
|
await directoryController.fetchCommentsForContact(contactId);
|
||||||
|
|
||||||
|
final notesController = Get.find<NotesController>();
|
||||||
|
await notesController.fetchNotes(
|
||||||
|
pageSize: 1000, pageNumber: 1); // ✅ Fixed here
|
||||||
|
|
||||||
Get.back(result: true);
|
Get.back(result: true);
|
||||||
|
|
||||||
showAppSnackbar(
|
showAppSnackbar(
|
||||||
@ -46,13 +51,6 @@ class AddCommentController extends GetxController {
|
|||||||
message: "Your comment has been successfully added.",
|
message: "Your comment has been successfully added.",
|
||||||
type: SnackbarType.success,
|
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) {
|
} catch (e) {
|
||||||
logSafe("Error while submitting comment: $e", level: LogLevel.error);
|
logSafe("Error while submitting comment: $e", level: LogLevel.error);
|
||||||
|
@ -20,7 +20,8 @@ class ApiService {
|
|||||||
final token = await LocalStorage.getJwtToken();
|
final token = await LocalStorage.getJwtToken();
|
||||||
|
|
||||||
if (token == null) {
|
if (token == null) {
|
||||||
logSafe("No JWT token found.");
|
logSafe("No JWT token found. Logging out...");
|
||||||
|
await LocalStorage.logout();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,7 +32,7 @@ class ApiService {
|
|||||||
if (refreshed) {
|
if (refreshed) {
|
||||||
return await LocalStorage.getJwtToken();
|
return await LocalStorage.getJwtToken();
|
||||||
} else {
|
} else {
|
||||||
logSafe("Token refresh failed. Logging out...");
|
logSafe("Token refresh failed. Logging out immediately...");
|
||||||
await LocalStorage.logout();
|
await LocalStorage.logout();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -47,10 +48,16 @@ class ApiService {
|
|||||||
final refreshed = await AuthService.refreshToken();
|
final refreshed = await AuthService.refreshToken();
|
||||||
if (refreshed) {
|
if (refreshed) {
|
||||||
return await LocalStorage.getJwtToken();
|
return await LocalStorage.getJwtToken();
|
||||||
|
} else {
|
||||||
|
logSafe("Token refresh failed (near expiry). Logging out...");
|
||||||
|
await LocalStorage.logout();
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logSafe("Token decoding error: $e", level: LogLevel.error);
|
logSafe("Token decoding error: $e", level: LogLevel.error);
|
||||||
|
await LocalStorage.logout();
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return token;
|
return token;
|
||||||
@ -107,8 +114,9 @@ class ApiService {
|
|||||||
}) async {
|
}) async {
|
||||||
String? token = await _getToken();
|
String? token = await _getToken();
|
||||||
if (token == null) {
|
if (token == null) {
|
||||||
logSafe("Token is null. Cannot proceed with GET request.",
|
logSafe("Token is null. Forcing logout from GET request.",
|
||||||
level: LogLevel.error);
|
level: LogLevel.error);
|
||||||
|
await LocalStorage.logout();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,8 +149,9 @@ class ApiService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
logSafe("Token refresh failed. Aborting request.",
|
logSafe("Token refresh failed. Logging out user.",
|
||||||
level: LogLevel.error);
|
level: LogLevel.error);
|
||||||
|
await LocalStorage.logout();
|
||||||
}
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
@ -246,7 +255,7 @@ class ApiService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Directory calling the API
|
/// Directory calling the API
|
||||||
|
|
||||||
static Future<bool> deleteBucket(String id) async {
|
static Future<bool> deleteBucket(String id) async {
|
||||||
final endpoint = "${ApiEndpoints.updateBucket}/$id";
|
final endpoint = "${ApiEndpoints.updateBucket}/$id";
|
||||||
|
|
||||||
|
@ -11,7 +11,8 @@ import 'package:marco/helpers/widgets/my_snackbar.dart';
|
|||||||
|
|
||||||
class EditBucketBottomSheet {
|
class EditBucketBottomSheet {
|
||||||
static void show(BuildContext context, ContactBucket bucket,
|
static void show(BuildContext context, ContactBucket bucket,
|
||||||
List<EmployeeModel> allEmployees) {
|
List<EmployeeModel> allEmployees,
|
||||||
|
{required String ownerId}) {
|
||||||
final ManageBucketController controller = Get.find();
|
final ManageBucketController controller = Get.find();
|
||||||
|
|
||||||
final nameController = TextEditingController(text: bucket.name);
|
final nameController = TextEditingController(text: bucket.name);
|
||||||
@ -192,13 +193,15 @@ class EditBucketBottomSheet {
|
|||||||
controlAffinity:
|
controlAffinity:
|
||||||
ListTileControlAffinity.leading,
|
ListTileControlAffinity.leading,
|
||||||
value: selectedIds.contains(emp.id),
|
value: selectedIds.contains(emp.id),
|
||||||
onChanged: (val) {
|
onChanged: emp.id == ownerId
|
||||||
if (val == true) {
|
? null
|
||||||
selectedIds.add(emp.id);
|
: (val) {
|
||||||
} else {
|
if (val == true) {
|
||||||
selectedIds.remove(emp.id);
|
selectedIds.add(emp.id);
|
||||||
}
|
} else {
|
||||||
},
|
selectedIds.remove(emp.id);
|
||||||
|
}
|
||||||
|
},
|
||||||
title: Text(
|
title: Text(
|
||||||
fullName.isNotEmpty ? fullName : 'Unnamed',
|
fullName.isNotEmpty ? fullName : 'Unnamed',
|
||||||
style: const TextStyle(fontSize: 13),
|
style: const TextStyle(fontSize: 13),
|
||||||
|
@ -77,18 +77,17 @@ String _convertDeltaToHtml(dynamic delta) {
|
|||||||
class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
||||||
late final DirectoryController directoryController;
|
late final DirectoryController directoryController;
|
||||||
late final ProjectController projectController;
|
late final ProjectController projectController;
|
||||||
|
late ContactModel contact;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
directoryController = Get.find<DirectoryController>();
|
directoryController = Get.find<DirectoryController>();
|
||||||
projectController = Get.find<ProjectController>();
|
projectController = Get.find<ProjectController>();
|
||||||
|
contact = widget.contact;
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (!directoryController.contactCommentsMap
|
directoryController.fetchCommentsForContact(contact.id);
|
||||||
.containsKey(widget.contact.id)) {
|
|
||||||
directoryController.fetchCommentsForContact(widget.contact.id);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,7 +132,8 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
|||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.arrow_back_ios_new,
|
icon: const Icon(Icons.arrow_back_ios_new,
|
||||||
color: Colors.black, size: 20),
|
color: Colors.black, size: 20),
|
||||||
onPressed: () => Get.back(),
|
onPressed: () =>
|
||||||
|
Get.offAllNamed('/dashboard/directory-main-page'),
|
||||||
),
|
),
|
||||||
MySpacing.width(8),
|
MySpacing.width(8),
|
||||||
Expanded(
|
Expanded(
|
||||||
@ -184,9 +184,9 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
|||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Avatar(
|
Avatar(
|
||||||
firstName: widget.contact.name.split(" ").first,
|
firstName: contact.name.split(" ").first,
|
||||||
lastName: widget.contact.name.split(" ").length > 1
|
lastName: contact.name.split(" ").length > 1
|
||||||
? widget.contact.name.split(" ").last
|
? contact.name.split(" ").last
|
||||||
: "",
|
: "",
|
||||||
size: 35,
|
size: 35,
|
||||||
backgroundColor: Colors.indigo,
|
backgroundColor: Colors.indigo,
|
||||||
@ -195,10 +195,10 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
|||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
MyText.titleSmall(widget.contact.name,
|
MyText.titleSmall(contact.name,
|
||||||
fontWeight: 600, color: Colors.black),
|
fontWeight: 600, color: Colors.black),
|
||||||
MySpacing.height(2),
|
MySpacing.height(2),
|
||||||
MyText.bodySmall(widget.contact.organization,
|
MyText.bodySmall(contact.organization,
|
||||||
fontWeight: 500, color: Colors.grey[700]),
|
fontWeight: 500, color: Colors.grey[700]),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -226,24 +226,24 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDetailsTab() {
|
Widget _buildDetailsTab() {
|
||||||
final email = widget.contact.contactEmails.isNotEmpty
|
final email = contact.contactEmails.isNotEmpty
|
||||||
? widget.contact.contactEmails.first.emailAddress
|
? contact.contactEmails.first.emailAddress
|
||||||
: "-";
|
: "-";
|
||||||
|
|
||||||
final phone = widget.contact.contactPhones.isNotEmpty
|
final phone = contact.contactPhones.isNotEmpty
|
||||||
? widget.contact.contactPhones.first.phoneNumber
|
? 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
|
.map((id) => directoryController.contactBuckets
|
||||||
.firstWhereOrNull((b) => b.id == id)
|
.firstWhereOrNull((b) => b.id == id)
|
||||||
?.name)
|
?.name)
|
||||||
.whereType<String>()
|
.whereType<String>()
|
||||||
.join(", ");
|
.join(", ");
|
||||||
|
|
||||||
final projectNames = widget.contact.projectIds
|
final projectNames = contact.projectIds
|
||||||
?.map((id) => projectController.projects
|
?.map((id) => projectController.projects
|
||||||
.firstWhereOrNull((p) => p.id == id)
|
.firstWhereOrNull((p) => p.id == id)
|
||||||
?.name)
|
?.name)
|
||||||
@ -251,7 +251,7 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
|||||||
.join(", ") ??
|
.join(", ") ??
|
||||||
"-";
|
"-";
|
||||||
|
|
||||||
final category = widget.contact.contactCategory?.name ?? "-";
|
final category = contact.contactCategory?.name ?? "-";
|
||||||
|
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
@ -270,12 +270,11 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
|||||||
onTap: () => LauncherUtils.launchPhone(phone),
|
onTap: () => LauncherUtils.launchPhone(phone),
|
||||||
onLongPress: () => LauncherUtils.copyToClipboard(phone,
|
onLongPress: () => LauncherUtils.copyToClipboard(phone,
|
||||||
typeLabel: "Phone")),
|
typeLabel: "Phone")),
|
||||||
_iconInfoRow(
|
_iconInfoRow(Icons.location_on, "Address", contact.address),
|
||||||
Icons.location_on, "Address", widget.contact.address),
|
|
||||||
]),
|
]),
|
||||||
_infoCard("Organization", [
|
_infoCard("Organization", [
|
||||||
_iconInfoRow(Icons.business, "Organization",
|
_iconInfoRow(
|
||||||
widget.contact.organization),
|
Icons.business, "Organization", contact.organization),
|
||||||
_iconInfoRow(Icons.category, "Category", category),
|
_iconInfoRow(Icons.category, "Category", category),
|
||||||
]),
|
]),
|
||||||
_infoCard("Meta Info", [
|
_infoCard("Meta Info", [
|
||||||
@ -289,7 +288,7 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
|||||||
Align(
|
Align(
|
||||||
alignment: Alignment.topLeft,
|
alignment: Alignment.topLeft,
|
||||||
child: MyText.bodyMedium(
|
child: MyText.bodyMedium(
|
||||||
widget.contact.description,
|
contact.description,
|
||||||
color: Colors.grey[800],
|
color: Colors.grey[800],
|
||||||
maxLines: 10,
|
maxLines: 10,
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
@ -304,12 +303,25 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
|||||||
right: 20,
|
right: 20,
|
||||||
child: FloatingActionButton.extended(
|
child: FloatingActionButton.extended(
|
||||||
backgroundColor: Colors.red,
|
backgroundColor: Colors.red,
|
||||||
onPressed: () {
|
onPressed: () async {
|
||||||
Get.bottomSheet(
|
final result = await Get.bottomSheet(
|
||||||
AddContactBottomSheet(existingContact: widget.contact),
|
AddContactBottomSheet(existingContact: contact),
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
backgroundColor: Colors.transparent,
|
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),
|
icon: const Icon(Icons.edit, color: Colors.white),
|
||||||
label: const Text(
|
label: const Text(
|
||||||
@ -324,7 +336,7 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
|||||||
|
|
||||||
Widget _buildCommentsTab(BuildContext context) {
|
Widget _buildCommentsTab(BuildContext context) {
|
||||||
return Obx(() {
|
return Obx(() {
|
||||||
final contactId = widget.contact.id;
|
final contactId = contact.id;
|
||||||
|
|
||||||
if (!directoryController.contactCommentsMap.containsKey(contactId)) {
|
if (!directoryController.contactCommentsMap.containsKey(contactId)) {
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
@ -447,8 +459,15 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
|||||||
final htmlOutput = _convertDeltaToHtml(delta);
|
final htmlOutput = _convertDeltaToHtml(delta);
|
||||||
final updated =
|
final updated =
|
||||||
comment.copyWith(note: htmlOutput);
|
comment.copyWith(note: htmlOutput);
|
||||||
|
|
||||||
await directoryController
|
await directoryController
|
||||||
.updateComment(updated);
|
.updateComment(updated);
|
||||||
|
|
||||||
|
// ✅ Re-fetch comments to get updated list
|
||||||
|
await directoryController
|
||||||
|
.fetchCommentsForContact(contactId);
|
||||||
|
|
||||||
|
// ✅ Exit editing mode
|
||||||
directoryController.editingCommentId.value =
|
directoryController.editingCommentId.value =
|
||||||
null;
|
null;
|
||||||
},
|
},
|
||||||
@ -479,11 +498,16 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
|||||||
right: 20,
|
right: 20,
|
||||||
child: FloatingActionButton.extended(
|
child: FloatingActionButton.extended(
|
||||||
backgroundColor: Colors.red,
|
backgroundColor: Colors.red,
|
||||||
onPressed: () {
|
onPressed: () async {
|
||||||
Get.bottomSheet(
|
final result = await Get.bottomSheet(
|
||||||
AddCommentBottomSheet(contactId: contactId),
|
AddCommentBottomSheet(contactId: contactId),
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (result == true) {
|
||||||
|
await directoryController
|
||||||
|
.fetchCommentsForContact(contactId);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.add_comment, color: Colors.white),
|
icon: const Icon(Icons.add_comment, color: Colors.white),
|
||||||
label: const Text(
|
label: const Text(
|
||||||
|
@ -194,7 +194,8 @@ class _ManageBucketsScreenState extends State<ManageBucketsScreen> {
|
|||||||
Future.delayed(const Duration(milliseconds: 300),
|
Future.delayed(const Duration(milliseconds: 300),
|
||||||
() {
|
() {
|
||||||
EditBucketBottomSheet.show(context, bucket,
|
EditBucketBottomSheet.show(context, bucket,
|
||||||
manageBucketController.allEmployees);
|
manageBucketController.allEmployees,
|
||||||
|
ownerId: bucket.createdBy.id);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user