Close chat #169

Closed
RecentRunner wants to merge 291 commits from close-chat into main
4 changed files with 82 additions and 6 deletions
Showing only changes of commit 1908558912 - Show all commits

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -48,11 +48,21 @@
<Insets bottom="18.0" left="18.0" right="18.0" top="18.0" />
</padding>
<children>
<Label fx:id="lblConversationTitle" text="Select a conversation" textFill="#0f172a">
<font>
<Font name="System Bold" size="24.0" />
</font>
</Label>
<HBox alignment="CENTER_LEFT" spacing="12.0">
<children>
<Label fx:id="lblConversationTitle" text="Select a conversation" textFill="#0f172a">
<font>
<Font name="System Bold" size="24.0" />
</font>
</Label>
<Region HBox.hgrow="ALWAYS" />
<Button fx:id="btnClose" mnemonicParsing="false" onAction="#btnCloseClicked" style="-fx-background-color: #dc2626; -fx-background-radius: 10; -fx-text-fill: white; -fx-cursor: hand;" text="Close Chat" visible="false" managed="false">
<padding>
<Insets bottom="8.0" left="14.0" right="14.0" top="8.0" />
</padding>
</Button>
</children>
</HBox>
<ScrollPane fx:id="spMessages" fitToWidth="true" hbarPolicy="NEVER" styleClass="chat-messages-scroll-pane" style="-fx-background-color: transparent; -fx-background: transparent;" VBox.vgrow="ALWAYS">
<content>
<VBox fx:id="vbMessages" spacing="10.0" style="-fx-background-color: rgba(255,255,255,0.72); -fx-background-radius: 18; -fx-padding: 18;" />