Add close chat and closed status
This commit is contained in:
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.ConversationResponse;
|
||||||
import org.example.petshopdesktop.api.dto.chat.MessageRequest;
|
import org.example.petshopdesktop.api.dto.chat.MessageRequest;
|
||||||
import org.example.petshopdesktop.api.dto.chat.MessageResponse;
|
import org.example.petshopdesktop.api.dto.chat.MessageResponse;
|
||||||
|
import org.example.petshopdesktop.api.dto.chat.UpdateConversationRequest;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class ChatApi {
|
public class ChatApi {
|
||||||
@@ -41,4 +42,8 @@ public class ChatApi {
|
|||||||
public MessageResponse sendMessage(Long conversationId, MessageRequest request) throws Exception {
|
public MessageResponse sendMessage(Long conversationId, MessageRequest request) throws Exception {
|
||||||
return apiClient.post("/api/v1/chat/conversations/" + conversationId + "/messages", request, MessageResponse.class);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.ConversationResponse;
|
||||||
import org.example.petshopdesktop.api.dto.chat.MessageRequest;
|
import org.example.petshopdesktop.api.dto.chat.MessageRequest;
|
||||||
import org.example.petshopdesktop.api.dto.chat.MessageResponse;
|
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.dto.common.DropdownOption;
|
||||||
import org.example.petshopdesktop.api.endpoints.ChatApi;
|
import org.example.petshopdesktop.api.endpoints.ChatApi;
|
||||||
import org.example.petshopdesktop.api.endpoints.DropdownApi;
|
import org.example.petshopdesktop.api.endpoints.DropdownApi;
|
||||||
@@ -61,6 +62,9 @@ public class ChatController {
|
|||||||
@FXML
|
@FXML
|
||||||
private Button btnAttachment;
|
private Button btnAttachment;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Button btnClose;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Label lblConversationTitle;
|
private Label lblConversationTitle;
|
||||||
|
|
||||||
@@ -116,6 +120,7 @@ public class ChatController {
|
|||||||
lblConversationTitle.setText(getConversationTitle(newValue));
|
lblConversationTitle.setText(getConversationTitle(newValue));
|
||||||
loadMessages(newValue.getId());
|
loadMessages(newValue.getId());
|
||||||
realtimeClient.subscribeToConversation(newValue.getId());
|
realtimeClient.subscribeToConversation(newValue.getId());
|
||||||
|
updateChatState(newValue);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -137,6 +142,7 @@ public class ChatController {
|
|||||||
|
|
||||||
loadCustomers();
|
loadCustomers();
|
||||||
loadConversations();
|
loadConversations();
|
||||||
|
updateChatState(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@@ -208,6 +214,42 @@ public class ChatController {
|
|||||||
lblChatStatus.setText("Attachment selected");
|
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() {
|
private void clearLocalAttachment() {
|
||||||
selectedAttachmentFile = null;
|
selectedAttachmentFile = null;
|
||||||
btnAttachment.setText("📎");
|
btnAttachment.setText("📎");
|
||||||
@@ -323,6 +365,9 @@ public class ChatController {
|
|||||||
conversations.sort(Comparator.comparing(ChatController::conversationSortTime, Comparator.nullsLast(Comparator.reverseOrder())));
|
conversations.sort(Comparator.comparing(ChatController::conversationSortTime, Comparator.nullsLast(Comparator.reverseOrder())));
|
||||||
restoreSelection();
|
restoreSelection();
|
||||||
lvConversations.refresh();
|
lvConversations.refresh();
|
||||||
|
if (selectedConversation != null && selectedConversation.getId().equals(conversation.getId())) {
|
||||||
|
updateChatState(conversation);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void upsertConversationForMessage(MessageResponse message) {
|
private void upsertConversationForMessage(MessageResponse message) {
|
||||||
@@ -424,6 +469,10 @@ public class ChatController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String buildConversationMeta(ConversationResponse conversation) {
|
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;
|
String assignee;
|
||||||
if (conversation.getStaffId() != null) {
|
if (conversation.getStaffId() != null) {
|
||||||
assignee = "Assigned";
|
assignee = "Assigned";
|
||||||
@@ -434,7 +483,6 @@ public class ChatController {
|
|||||||
} else {
|
} else {
|
||||||
assignee = "Open";
|
assignee = "Open";
|
||||||
}
|
}
|
||||||
String updated = conversation.getUpdatedAt() == null ? "" : TIME_FORMATTER.format(conversation.getUpdatedAt());
|
|
||||||
return assignee + (updated.isBlank() ? "" : " · " + updated);
|
return assignee + (updated.isBlank() ? "" : " · " + updated);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,11 +48,21 @@
|
|||||||
<Insets bottom="18.0" left="18.0" right="18.0" top="18.0" />
|
<Insets bottom="18.0" left="18.0" right="18.0" top="18.0" />
|
||||||
</padding>
|
</padding>
|
||||||
<children>
|
<children>
|
||||||
<Label fx:id="lblConversationTitle" text="Select a conversation" textFill="#0f172a">
|
<HBox alignment="CENTER_LEFT" spacing="12.0">
|
||||||
<font>
|
<children>
|
||||||
<Font name="System Bold" size="24.0" />
|
<Label fx:id="lblConversationTitle" text="Select a conversation" textFill="#0f172a">
|
||||||
</font>
|
<font>
|
||||||
</Label>
|
<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">
|
<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>
|
<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;" />
|
<VBox fx:id="vbMessages" spacing="10.0" style="-fx-background-color: rgba(255,255,255,0.72); -fx-background-radius: 18; -fx-padding: 18;" />
|
||||||
|
|||||||
Reference in New Issue
Block a user