Compare commits
2 Commits
1fafe77211
...
47666c7897
Author | SHA1 | Date | |
---|---|---|---|
47666c7897 | |||
e6f028d129 |
@ -209,10 +209,11 @@ class NotificationActionHandler {
|
||||
|
||||
/// ---------------------- DOCUMENT HANDLER ----------------------
|
||||
static void _handleDocumentModified(Map<String, dynamic> data) {
|
||||
late String entityTypeId;
|
||||
late String entityId;
|
||||
String entityTypeId;
|
||||
String entityId;
|
||||
String? documentId = data['DocumentId'];
|
||||
|
||||
// Determine entity type and ID
|
||||
if (data['Keyword'] == 'Employee_Document_Modified') {
|
||||
entityTypeId = Permissions.employeeEntity;
|
||||
entityId = data['EmployeeId'] ?? '';
|
||||
@ -229,63 +230,81 @@ class NotificationActionHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
_safeControllerUpdate<DocumentController>(
|
||||
onFound: (controller) async {
|
||||
await controller.fetchDocuments(
|
||||
entityTypeId: entityTypeId,
|
||||
entityId: entityId,
|
||||
reset: true,
|
||||
);
|
||||
},
|
||||
notFoundMessage: '⚠️ DocumentController not found, cannot refresh list.',
|
||||
successMessage: '✅ DocumentController refreshed from notification.',
|
||||
);
|
||||
_logger.i(
|
||||
"🔔 Document notification received: keyword=${data['Keyword']}, entityTypeId=$entityTypeId, entityId=$entityId, documentId=$documentId");
|
||||
|
||||
if (documentId != null) {
|
||||
// Refresh Document List
|
||||
if (Get.isRegistered<DocumentController>()) {
|
||||
_safeControllerUpdate<DocumentController>(
|
||||
onFound: (controller) async {
|
||||
await controller.fetchDocuments(
|
||||
entityTypeId: entityTypeId,
|
||||
entityId: entityId,
|
||||
reset: true,
|
||||
);
|
||||
},
|
||||
notFoundMessage:
|
||||
'⚠️ DocumentController not found, cannot refresh list.',
|
||||
successMessage: '✅ DocumentController refreshed from notification.',
|
||||
);
|
||||
} else {
|
||||
_logger.w('⚠️ DocumentController not registered, skipping list refresh.');
|
||||
}
|
||||
|
||||
// Refresh Document Details (if open)
|
||||
if (documentId != null && Get.isRegistered<DocumentDetailsController>()) {
|
||||
_safeControllerUpdate<DocumentDetailsController>(
|
||||
onFound: (controller) async {
|
||||
if (controller.documentDetails.value?.data?.id == documentId) {
|
||||
await controller.fetchDocumentDetails(documentId);
|
||||
_logger.i(
|
||||
"✅ DocumentDetailsController refreshed for Document $documentId");
|
||||
}
|
||||
// Refresh details regardless of current document
|
||||
await controller.fetchDocumentDetails(documentId);
|
||||
_logger.i(
|
||||
"✅ DocumentDetailsController refreshed for Document $documentId");
|
||||
},
|
||||
notFoundMessage: 'ℹ️ DocumentDetailsController not active, skipping.',
|
||||
notFoundMessage:
|
||||
'ℹ️ DocumentDetailsController not active, skipping details refresh.',
|
||||
successMessage: '✅ DocumentDetailsController checked for refresh.',
|
||||
);
|
||||
} else if (documentId != null) {
|
||||
_logger.w(
|
||||
'⚠️ DocumentDetailsController not registered, cannot refresh document details.');
|
||||
}
|
||||
}
|
||||
|
||||
/// ---------------------- DIRECTORY HANDLERS ----------------------
|
||||
/// ---------------------- DIRECTORY HANDLERS ----------------------
|
||||
static void _handleContactModified(Map<String, dynamic> data) {
|
||||
_safeControllerUpdate<DirectoryController>(
|
||||
onFound: (controller) => controller.fetchContacts(),
|
||||
notFoundMessage: '⚠️ DirectoryController not found, cannot refresh.',
|
||||
successMessage: '✅ Directory contacts refreshed from notification.',
|
||||
);
|
||||
}
|
||||
|
||||
static void _handleContactNoteModified(Map<String, dynamic> data) {
|
||||
final contactId = data['contactId'];
|
||||
final contactId = data['ContactId'];
|
||||
|
||||
// Always refresh the contact list
|
||||
_safeControllerUpdate<DirectoryController>(
|
||||
onFound: (controller) {
|
||||
controller.fetchContacts();
|
||||
// If a specific contact is provided, refresh its notes as well
|
||||
if (contactId != null) {
|
||||
controller.fetchCommentsForContact(contactId);
|
||||
}
|
||||
},
|
||||
notFoundMessage:
|
||||
'⚠️ DirectoryController not found, cannot refresh notes.',
|
||||
successMessage: '✅ Directory comments refreshed from notification.',
|
||||
'⚠️ DirectoryController not found, cannot refresh contacts.',
|
||||
successMessage:
|
||||
'✅ Directory contacts (and notes if applicable) refreshed from notification.',
|
||||
);
|
||||
|
||||
// Refresh notes globally as well
|
||||
_safeControllerUpdate<NotesController>(
|
||||
onFound: (controller) => controller.fetchNotes(),
|
||||
notFoundMessage: '⚠️ NotesController not found, cannot refresh.',
|
||||
notFoundMessage: '⚠️ NotesController not found, cannot refresh notes.',
|
||||
successMessage: '✅ Notes refreshed from notification.',
|
||||
);
|
||||
}
|
||||
|
||||
static void _handleContactNoteModified(Map<String, dynamic> data) {
|
||||
final contactId = data['ContactId'];
|
||||
|
||||
// Refresh both contacts and notes when a note is modified
|
||||
_handleContactModified(data);
|
||||
}
|
||||
|
||||
static void _handleBucketModified(Map<String, dynamic> data) {
|
||||
_safeControllerUpdate<DirectoryController>(
|
||||
onFound: (controller) => controller.fetchBuckets(),
|
||||
@ -313,7 +332,8 @@ class NotificationActionHandler {
|
||||
break;
|
||||
case 'task_updated':
|
||||
await controller.fetchDashboardTasks(
|
||||
projectId: controller.projectController.selectedProjectId.value);
|
||||
projectId:
|
||||
controller.projectController.selectedProjectId.value);
|
||||
break;
|
||||
case 'project_progress_update':
|
||||
await controller.fetchProjectProgress();
|
||||
|
@ -63,6 +63,7 @@ String _convertDeltaToHtml(dynamic delta) {
|
||||
class ContactDetailScreen extends StatefulWidget {
|
||||
final ContactModel contact;
|
||||
const ContactDetailScreen({super.key, required this.contact});
|
||||
|
||||
@override
|
||||
State<ContactDetailScreen> createState() => _ContactDetailScreenState();
|
||||
}
|
||||
@ -70,16 +71,25 @@ class ContactDetailScreen extends StatefulWidget {
|
||||
class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
||||
late final DirectoryController directoryController;
|
||||
late final ProjectController projectController;
|
||||
late ContactModel contact;
|
||||
|
||||
late Rx<ContactModel> contactRx;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
directoryController = Get.find<DirectoryController>();
|
||||
projectController = Get.find<ProjectController>();
|
||||
contact = widget.contact;
|
||||
contactRx = widget.contact.obs;
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
directoryController.fetchCommentsForContact(contact.id);
|
||||
directoryController.fetchCommentsForContact(contactRx.value.id);
|
||||
});
|
||||
|
||||
// Listen to controller's allContacts and update contact if changed
|
||||
ever(directoryController.allContacts, (_) {
|
||||
final updated = directoryController.allContacts
|
||||
.firstWhereOrNull((c) => c.id == contactRx.value.id);
|
||||
if (updated != null) contactRx.value = updated;
|
||||
});
|
||||
}
|
||||
|
||||
@ -94,12 +104,12 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildSubHeader(),
|
||||
Obx(() => _buildSubHeader(contactRx.value)),
|
||||
const Divider(height: 1, thickness: 0.5, color: Colors.grey),
|
||||
Expanded(
|
||||
child: TabBarView(children: [
|
||||
_buildDetailsTab(),
|
||||
_buildCommentsTab(context),
|
||||
Obx(() => _buildDetailsTab(contactRx.value)),
|
||||
_buildCommentsTab(),
|
||||
]),
|
||||
),
|
||||
],
|
||||
@ -135,9 +145,9 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
||||
MyText.titleLarge('Contact Profile',
|
||||
fontWeight: 700, color: Colors.black),
|
||||
MySpacing.height(2),
|
||||
GetBuilder<ProjectController>(
|
||||
builder: (p) => ProjectLabel(p.selectedProject?.name),
|
||||
),
|
||||
GetBuilder<ProjectController>(builder: (p) {
|
||||
return ProjectLabel(p.selectedProject?.name);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -147,7 +157,7 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSubHeader() {
|
||||
Widget _buildSubHeader(ContactModel contact) {
|
||||
final firstName = contact.name.split(" ").first;
|
||||
final lastName =
|
||||
contact.name.split(" ").length > 1 ? contact.name.split(" ").last : "";
|
||||
@ -196,7 +206,7 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDetailsTab() {
|
||||
Widget _buildDetailsTab(ContactModel contact) {
|
||||
final tags = contact.tags.map((e) => e.name).join(", ");
|
||||
final bucketNames = contact.bucketIds
|
||||
.map((id) => directoryController.contactBuckets
|
||||
@ -249,7 +259,6 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
MySpacing.height(12),
|
||||
// BASIC INFO CARD
|
||||
_infoCard("Basic Info", [
|
||||
multiRows(
|
||||
items:
|
||||
@ -273,20 +282,17 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
||||
),
|
||||
_iconInfoRow(Icons.location_on, "Address", contact.address),
|
||||
]),
|
||||
// ORGANIZATION CARD
|
||||
_infoCard("Organization", [
|
||||
_iconInfoRow(
|
||||
Icons.business, "Organization", contact.organization),
|
||||
_iconInfoRow(Icons.category, "Category", category),
|
||||
]),
|
||||
// META INFO CARD
|
||||
_infoCard("Meta Info", [
|
||||
_iconInfoRow(Icons.label, "Tags", tags.isNotEmpty ? tags : "-"),
|
||||
_iconInfoRow(Icons.folder_shared, "Contact Buckets",
|
||||
bucketNames.isNotEmpty ? bucketNames : "-"),
|
||||
_iconInfoRow(Icons.work_outline, "Projects", projectNames),
|
||||
]),
|
||||
// DESCRIPTION CARD
|
||||
_infoCard("Description", [
|
||||
MySpacing.height(6),
|
||||
Align(
|
||||
@ -318,7 +324,7 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
||||
final updated = directoryController.allContacts
|
||||
.firstWhereOrNull((c) => c.id == contact.id);
|
||||
if (updated != null) {
|
||||
setState(() => contact = updated);
|
||||
contactRx.value = updated;
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -331,9 +337,9 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCommentsTab(BuildContext context) {
|
||||
Widget _buildCommentsTab() {
|
||||
return Obx(() {
|
||||
final contactId = contact.id;
|
||||
final contactId = contactRx.value.id;
|
||||
if (!directoryController.contactCommentsMap.containsKey(contactId)) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
@ -355,7 +361,7 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
children: [
|
||||
SizedBox(
|
||||
height: MediaQuery.of(context).size.height * 0.6,
|
||||
height: Get.height * 0.6,
|
||||
child: Center(
|
||||
child: MyText.bodyLarge(
|
||||
"No comments yet.",
|
||||
@ -375,7 +381,7 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
||||
itemBuilder: (_, index) => _buildCommentItem(
|
||||
comments[index],
|
||||
editingId,
|
||||
contact.id,
|
||||
contactId,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -438,7 +444,6 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Header Row
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -474,7 +479,6 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
||||
),
|
||||
],
|
||||
),
|
||||
// Comment Content
|
||||
if (isEditing && quillController != null)
|
||||
CommentEditorCard(
|
||||
controller: quillController,
|
||||
|
@ -25,7 +25,8 @@ class DocumentDetailsPage extends StatefulWidget {
|
||||
|
||||
class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
||||
final DocumentDetailsController controller =
|
||||
Get.put(DocumentDetailsController());
|
||||
Get.find<DocumentDetailsController>();
|
||||
|
||||
final PermissionController permissionController =
|
||||
Get.find<PermissionController>();
|
||||
@override
|
||||
|
@ -17,9 +17,9 @@ import 'package:marco/helpers/widgets/custom_app_bar.dart';
|
||||
import 'package:marco/helpers/widgets/my_confirmation_dialog.dart';
|
||||
import 'package:marco/helpers/widgets/my_snackbar.dart';
|
||||
import 'package:marco/controller/permission_controller.dart';
|
||||
import 'package:marco/controller/document/document_details_controller.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
|
||||
class UserDocumentsPage extends StatefulWidget {
|
||||
final String? entityId;
|
||||
final bool isEmployee;
|
||||
@ -38,7 +38,8 @@ class _UserDocumentsPageState extends State<UserDocumentsPage> {
|
||||
final DocumentController docController = Get.put(DocumentController());
|
||||
final PermissionController permissionController =
|
||||
Get.find<PermissionController>();
|
||||
|
||||
final DocumentDetailsController controller =
|
||||
Get.put(DocumentDetailsController());
|
||||
String get entityTypeId => widget.isEmployee
|
||||
? Permissions.employeeEntity
|
||||
: Permissions.projectEntity;
|
||||
@ -586,8 +587,7 @@ class _UserDocumentsPageState extends State<UserDocumentsPage> {
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (_) => DocumentUploadBottomSheet(
|
||||
isEmployee:
|
||||
widget.isEmployee,
|
||||
isEmployee: widget.isEmployee,
|
||||
onSubmit: (data) async {
|
||||
final success = await uploadController.uploadDocument(
|
||||
name: data["name"],
|
||||
|
Loading…
x
Reference in New Issue
Block a user