Compare commits

..

No commits in common. "2cf1224107b7a4f8fe48a7c9d7f0ea7e5133eefd" and "d694040bf782e0df9c708496d377af0d3fc0e481" have entirely different histories.

8 changed files with 45 additions and 117 deletions

View File

@ -19,14 +19,11 @@ export const ContactButton = ({ product }: { product: ProductDetail }) => {
return;
}
if (!product.seller?.id) return;
createChatThread(
{ participantId: product.seller.id, productId: product.id },
{
onSuccess: (threadData) => {
navigate(`/profile/threads/${threadData.id}`);
},
}
);
createChatThread(product.seller.id, {
onSuccess: (threadData) => {
navigate(`/profile/threads/${threadData.id}`);
},
});
};
return (
<>

View File

@ -330,12 +330,9 @@ export const ProductDetailView = memo(function ProductDetailView({
return;
}
if (!product.seller?.id) return;
createChatThread(
{ participantId: product.seller.id, productId: product.id },
{
onSuccess: (t) => navigate(`/profile/threads/${t.id}`),
}
);
createChatThread(product.seller.id, {
onSuccess: (t) => navigate(`/profile/threads/${t.id}`),
});
};
return (

View File

@ -24,14 +24,11 @@ export const ProductHeader = ({ product }: ProductHeaderProps) => {
return;
}
if (!product.seller?.id) return;
createChatThread(
{ participantId: product.seller.id, productId: product.id },
{
onSuccess: (threadData) => {
navigate(`/profile/threads/${threadData.id}`);
},
}
);
createChatThread(product.seller.id, {
onSuccess: (threadData) => {
navigate(`/profile/threads/${threadData.id}`);
},
});
};
return (

View File

@ -20,6 +20,11 @@ interface SizeChartSectionProps {
showError?: boolean;
}
const UNITS: { value: string; label: string }[] = [
{ value: "cm", label: "سانتی‌متر" },
{ value: "inch", label: "اینچ" },
];
export function SizeChartSection({
value,
onChange,
@ -166,8 +171,26 @@ export function SizeChartSection({
</p>
)}
<div className="flex items-center justify-end">
<span className="text-R12 text-GRAY">اندازهها به سانتیمتر</span>
{/* Unit toggle */}
<div className="flex items-center justify-between">
<span className="text-R12 text-GRAY">واحد اندازهگیری</span>
<div className="flex overflow-hidden rounded-lg border border-GRAY3">
{UNITS.map((u) => (
<button
key={u.value}
type="button"
onClick={() => emit({ unit: u.value })}
className={cn(
"px-3 py-1 text-R12 transition-colors cursor-pointer",
unit === u.value
? "bg-BLACK text-WHITE"
: "bg-WHITE text-BLACK2 hover:bg-WHITE3"
)}
>
{u.label}
</button>
))}
</div>
</div>
{/* Grid */}

View File

@ -1,5 +1,4 @@
import { Reply, MoreVertical, Trash2, Check } from "lucide-react";
import { Link } from "@remix-run/react";
import {
DropdownMenu,
@ -82,34 +81,7 @@ export const MessageBubble = ({
{replyToMessageContent || "پاسخ به پیام"}
</div>
)}
{message.product && (
<Link
to={`/product/${message.product.id}`}
className="flex items-center gap-2 mb-1 w-[210px] max-w-full rounded-lg bg-WHITE border border-GRAY3 p-2"
>
{message.product.primaryImage ? (
<img
src={message.product.primaryImage}
alt=""
className="w-12 h-12 rounded-md object-cover shrink-0"
/>
) : (
<div className="w-12 h-12 rounded-md bg-WHITE3 shrink-0" />
)}
<div className="min-w-0 text-right">
<p className="text-R12 text-BLACK truncate">
{message.product.title}
</p>
{message.product.basePrice && (
<p className="text-R10 text-GRAY mt-0.5">
{parseInt(message.product.basePrice).toLocaleString("fa-IR")}{" "}
تومان
</p>
)}
</div>
</Link>
)}
{message.content && <p>{message.content}</p>}
<p>{message.content}</p>
</div>
<div className="flex flex-col justify-between items-start">
<div className="flex flex-col gap-1 items-start">

View File

@ -156,10 +156,7 @@ const ThreadsList = ({
<p
className={`text-R14 ${hasUnread ? "text-BLACK font-bold" : "text-GRAY"}`}
>
{thread.lastMessage?.content ||
(thread.lastMessage?.product
? `🛍 ${thread.lastMessage.product.title || "محصول"}`
: "بدون پیام")}
{thread.lastMessage?.content || "بدون پیام"}
</p>
</div>
</div>

View File

@ -206,21 +206,15 @@ export function useGetThreadMessages(threadId: string) {
export function useCreateChatThread() {
const token = useAuthToken();
return useMutation({
mutationFn: async (
input: string | { participantId: string; productId?: string }
) => {
mutationFn: async (participantId: 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,
...(productId ? { productId } : {}),
participantId: participantId,
},
});
} catch (error) {

View File

@ -2336,51 +2336,14 @@ 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
*/
@ -2516,17 +2479,11 @@ 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;
}
/**
*
@ -6507,12 +6464,6 @@ 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;
}
/**
*