diff --git a/.gitignore b/.gitignore
index 7a998c43..bc434064 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,4 @@
commit-patches/
temp_photos/
uploads/
+.env
diff --git a/desktop/src/main/java/org/example/petshopdesktop/api/dto/chat/UpdateConversationRequest.java b/desktop/src/main/java/org/example/petshopdesktop/api/dto/chat/UpdateConversationRequest.java
new file mode 100644
index 00000000..3bacbca9
--- /dev/null
+++ b/desktop/src/main/java/org/example/petshopdesktop/api/dto/chat/UpdateConversationRequest.java
@@ -0,0 +1,13 @@
+package org.example.petshopdesktop.api.dto.chat;
+
+public class UpdateConversationRequest {
+ private String status;
+
+ public UpdateConversationRequest(String status) {
+ this.status = status;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+}
diff --git a/desktop/src/main/java/org/example/petshopdesktop/api/endpoints/ChatApi.java b/desktop/src/main/java/org/example/petshopdesktop/api/endpoints/ChatApi.java
index 3fcb7372..37d3e2a5 100644
--- a/desktop/src/main/java/org/example/petshopdesktop/api/endpoints/ChatApi.java
+++ b/desktop/src/main/java/org/example/petshopdesktop/api/endpoints/ChatApi.java
@@ -6,6 +6,7 @@ import org.example.petshopdesktop.api.dto.chat.ConversationRequest;
import org.example.petshopdesktop.api.dto.chat.ConversationResponse;
import org.example.petshopdesktop.api.dto.chat.MessageRequest;
import org.example.petshopdesktop.api.dto.chat.MessageResponse;
+import org.example.petshopdesktop.api.dto.chat.UpdateConversationRequest;
import java.util.List;
public class ChatApi {
@@ -41,4 +42,8 @@ public class ChatApi {
public MessageResponse sendMessage(Long conversationId, MessageRequest request) throws Exception {
return apiClient.post("/api/v1/chat/conversations/" + conversationId + "/messages", request, MessageResponse.class);
}
+
+ public ConversationResponse updateConversation(Long id, UpdateConversationRequest request) throws Exception {
+ return apiClient.put("/api/v1/chat/conversations/" + id, request, ConversationResponse.class);
+ }
}
diff --git a/desktop/src/main/java/org/example/petshopdesktop/controllers/ChatController.java b/desktop/src/main/java/org/example/petshopdesktop/controllers/ChatController.java
index 02d05a93..7b1ed333 100644
--- a/desktop/src/main/java/org/example/petshopdesktop/controllers/ChatController.java
+++ b/desktop/src/main/java/org/example/petshopdesktop/controllers/ChatController.java
@@ -23,6 +23,7 @@ import org.example.petshopdesktop.api.ChatRealtimeClient;
import org.example.petshopdesktop.api.dto.chat.ConversationResponse;
import org.example.petshopdesktop.api.dto.chat.MessageRequest;
import org.example.petshopdesktop.api.dto.chat.MessageResponse;
+import org.example.petshopdesktop.api.dto.chat.UpdateConversationRequest;
import org.example.petshopdesktop.api.dto.common.DropdownOption;
import org.example.petshopdesktop.api.endpoints.ChatApi;
import org.example.petshopdesktop.api.endpoints.DropdownApi;
@@ -61,6 +62,9 @@ public class ChatController {
@FXML
private Button btnAttachment;
+ @FXML
+ private Button btnClose;
+
@FXML
private Label lblConversationTitle;
@@ -116,6 +120,7 @@ public class ChatController {
lblConversationTitle.setText(getConversationTitle(newValue));
loadMessages(newValue.getId());
realtimeClient.subscribeToConversation(newValue.getId());
+ updateChatState(newValue);
}
});
@@ -137,6 +142,7 @@ public class ChatController {
loadCustomers();
loadConversations();
+ updateChatState(null);
}
@FXML
@@ -208,6 +214,42 @@ public class ChatController {
lblChatStatus.setText("Attachment selected");
}
+ @FXML
+ void btnCloseClicked() {
+ if (selectedConversation == null || "CLOSED".equals(selectedConversation.getStatus())) return;
+ Long convId = selectedConversation.getId();
+ btnClose.setDisable(true);
+ lblChatStatus.setText("Closing...");
+ new Thread(() -> {
+ try {
+ ConversationResponse updated = ChatApi.getInstance().updateConversation(convId, new UpdateConversationRequest("CLOSED"));
+ Platform.runLater(() -> {
+ upsertConversation(updated);
+ updateChatState(updated);
+ lblChatStatus.setText("Conversation closed");
+ });
+ } catch (Exception e) {
+ Platform.runLater(() -> {
+ btnClose.setDisable(false);
+ lblChatStatus.setText("Close failed");
+ ActivityLogger.getInstance().logException(
+ "ChatController.closeConversation",
+ e,
+ "Closing conversation " + convId);
+ });
+ }
+ }).start();
+ }
+
+ private void updateChatState(ConversationResponse conv) {
+ boolean closed = conv == null || "CLOSED".equals(conv.getStatus());
+ txtMessage.setDisable(closed);
+ btnSend.setDisable(closed);
+ btnAttachment.setDisable(closed);
+ btnClose.setVisible(!closed);
+ btnClose.setManaged(!closed);
+ }
+
private void clearLocalAttachment() {
selectedAttachmentFile = null;
btnAttachment.setText("📎");
@@ -323,6 +365,9 @@ public class ChatController {
conversations.sort(Comparator.comparing(ChatController::conversationSortTime, Comparator.nullsLast(Comparator.reverseOrder())));
restoreSelection();
lvConversations.refresh();
+ if (selectedConversation != null && selectedConversation.getId().equals(conversation.getId())) {
+ updateChatState(conversation);
+ }
}
private void upsertConversationForMessage(MessageResponse message) {
@@ -419,6 +464,10 @@ public class ChatController {
}
private String buildConversationMeta(ConversationResponse conversation) {
+ String updated = conversation.getUpdatedAt() == null ? "" : TIME_FORMATTER.format(conversation.getUpdatedAt());
+ if ("CLOSED".equals(conversation.getStatus())) {
+ return "Closed" + (updated.isBlank() ? "" : " · " + updated);
+ }
String assignee;
if (conversation.getStaffId() != null) {
assignee = "Assigned";
@@ -429,7 +478,6 @@ public class ChatController {
} else {
assignee = "Open";
}
- String updated = conversation.getUpdatedAt() == null ? "" : TIME_FORMATTER.format(conversation.getUpdatedAt());
return assignee + (updated.isBlank() ? "" : " · " + updated);
}
diff --git a/desktop/src/main/resources/org/example/petshopdesktop/modelviews/chat-view.fxml b/desktop/src/main/resources/org/example/petshopdesktop/modelviews/chat-view.fxml
index b55fd5c9..c1294eab 100644
--- a/desktop/src/main/resources/org/example/petshopdesktop/modelviews/chat-view.fxml
+++ b/desktop/src/main/resources/org/example/petshopdesktop/modelviews/chat-view.fxml
@@ -48,11 +48,21 @@
-
+
+
+
+
+
+
+