diff --git a/app/components/product/ContactButton.tsx b/app/components/product/ContactButton.tsx
index 4099cb9..a2478ac 100644
--- a/app/components/product/ContactButton.tsx
+++ b/app/components/product/ContactButton.tsx
@@ -19,11 +19,14 @@ export const ContactButton = ({ product }: { product: ProductDetail }) => {
return;
}
if (!product.seller?.id) return;
- createChatThread(product.seller.id, {
- onSuccess: (threadData) => {
- navigate(`/profile/threads/${threadData.id}`);
- },
- });
+ createChatThread(
+ { participantId: product.seller.id, productId: product.id },
+ {
+ onSuccess: (threadData) => {
+ navigate(`/profile/threads/${threadData.id}`);
+ },
+ }
+ );
};
return (
<>
diff --git a/app/components/product/ProductDetailView.tsx b/app/components/product/ProductDetailView.tsx
index 6a28cc5..e0db283 100644
--- a/app/components/product/ProductDetailView.tsx
+++ b/app/components/product/ProductDetailView.tsx
@@ -330,9 +330,12 @@ export const ProductDetailView = memo(function ProductDetailView({
return;
}
if (!product.seller?.id) return;
- createChatThread(product.seller.id, {
- onSuccess: (t) => navigate(`/profile/threads/${t.id}`),
- });
+ createChatThread(
+ { participantId: product.seller.id, productId: product.id },
+ {
+ onSuccess: (t) => navigate(`/profile/threads/${t.id}`),
+ }
+ );
};
return (
diff --git a/app/components/product/ProductHeader.tsx b/app/components/product/ProductHeader.tsx
index 63313af..9ec1956 100644
--- a/app/components/product/ProductHeader.tsx
+++ b/app/components/product/ProductHeader.tsx
@@ -24,11 +24,14 @@ export const ProductHeader = ({ product }: ProductHeaderProps) => {
return;
}
if (!product.seller?.id) return;
- createChatThread(product.seller.id, {
- onSuccess: (threadData) => {
- navigate(`/profile/threads/${threadData.id}`);
- },
- });
+ createChatThread(
+ { participantId: product.seller.id, productId: product.id },
+ {
+ onSuccess: (threadData) => {
+ navigate(`/profile/threads/${threadData.id}`);
+ },
+ }
+ );
};
return (
diff --git a/app/components/thread/MessageBubble.tsx b/app/components/thread/MessageBubble.tsx
index 8d17670..2f12749 100644
--- a/app/components/thread/MessageBubble.tsx
+++ b/app/components/thread/MessageBubble.tsx
@@ -1,4 +1,5 @@
import { Reply, MoreVertical, Trash2, Check } from "lucide-react";
+import { Link } from "@remix-run/react";
import {
DropdownMenu,
@@ -81,7 +82,34 @@ export const MessageBubble = ({
{replyToMessageContent || "پاسخ به پیام"}
)}
-
{message.content}
+ {message.product && (
+
+ {message.product.primaryImage ? (
+
+ ) : (
+
+ )}
+
+
+ {message.product.title}
+
+ {message.product.basePrice && (
+
+ {parseInt(message.product.basePrice).toLocaleString("fa-IR")}{" "}
+ تومان
+
+ )}
+
+
+ )}
+ {message.content && {message.content}
}
diff --git a/app/components/thread/ThreadsPage.tsx b/app/components/thread/ThreadsPage.tsx
index eb949c3..ff310bb 100644
--- a/app/components/thread/ThreadsPage.tsx
+++ b/app/components/thread/ThreadsPage.tsx
@@ -156,7 +156,10 @@ const ThreadsList = ({
- {thread.lastMessage?.content || "بدون پیام"}
+ {thread.lastMessage?.content ||
+ (thread.lastMessage?.product
+ ? `🛍 ${thread.lastMessage.product.title || "محصول"}`
+ : "بدون پیام")}
diff --git a/app/requestHandler/use-chat-hooks.tsx b/app/requestHandler/use-chat-hooks.tsx
index a92b739..4c01ae5 100644
--- a/app/requestHandler/use-chat-hooks.tsx
+++ b/app/requestHandler/use-chat-hooks.tsx
@@ -206,15 +206,21 @@ export function useGetThreadMessages(threadId: string) {
export function useCreateChatThread() {
const token = useAuthToken();
return useMutation({
- mutationFn: async (participantId: string) => {
+ mutationFn: async (
+ input: string | { participantId: string; productId?: string }
+ ) => {
if (!token) {
throw new Error("Authentication token and user ID are required");
}
+ // Back-compat: callers may pass a bare participantId string.
+ const { participantId, productId } =
+ typeof input === "string" ? { participantId: input, productId: undefined } : input;
try {
const chatApi = createChatApi(token);
return await chatApi.apiChatV1ThreadsCreateCreate({
data: {
- participantId: participantId,
+ participantId,
+ ...(productId ? { productId } : {}),
},
});
} catch (error) {
diff --git a/src/api/types/models/index.ts b/src/api/types/models/index.ts
index 6487d61..9835651 100644
--- a/src/api/types/models/index.ts
+++ b/src/api/types/models/index.ts
@@ -2336,14 +2336,51 @@ export interface LastMessage {
*/
readonly senderUsername?: string;
/**
- *
+ *
* @type {string}
* @memberof LastMessage
*/
readonly createdAt?: string;
+ /**
+ * Product shared into the chat, if this message is a product card.
+ * @type {ChatProductCard}
+ * @memberof LastMessage
+ */
+ readonly product?: ChatProductCard | null;
}
/**
- *
+ * Minimal product info rendered as a card inside a chat.
+ * @export
+ * @interface ChatProductCard
+ */
+export interface ChatProductCard {
+ /**
+ *
+ * @type {string}
+ * @memberof ChatProductCard
+ */
+ readonly id?: string;
+ /**
+ *
+ * @type {string}
+ * @memberof ChatProductCard
+ */
+ readonly title?: string;
+ /**
+ *
+ * @type {string}
+ * @memberof ChatProductCard
+ */
+ readonly basePrice?: string;
+ /**
+ *
+ * @type {string}
+ * @memberof ChatProductCard
+ */
+ readonly primaryImage?: string | null;
+}
+/**
+ *
* @export
* @interface Login
*/
@@ -2479,11 +2516,17 @@ export interface MessageList {
*/
readonly isDeleted?: boolean;
/**
- *
+ *
* @type {boolean}
* @memberof MessageList
*/
readonly isThreadCreator?: boolean;
+ /**
+ * Product shared into the chat, if this message is a product card.
+ * @type {ChatProductCard}
+ * @memberof MessageList
+ */
+ readonly product?: ChatProductCard | null;
}
/**
*
@@ -6464,6 +6507,12 @@ export interface ThreadCreate {
* @memberof ThreadCreate
*/
participantId: string;
+ /**
+ * Optional product the chat is about; shared into the thread as a card
+ * @type {string}
+ * @memberof ThreadCreate
+ */
+ productId?: string | null;
}
/**
*