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; return;
} }
if (!product.seller?.id) return; if (!product.seller?.id) return;
createChatThread( createChatThread(product.seller.id, {
{ participantId: product.seller.id, productId: product.id },
{
onSuccess: (threadData) => { onSuccess: (threadData) => {
navigate(`/profile/threads/${threadData.id}`); navigate(`/profile/threads/${threadData.id}`);
}, },
} });
);
}; };
return ( return (
<> <>

View File

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

View File

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

View File

@ -20,6 +20,11 @@ interface SizeChartSectionProps {
showError?: boolean; showError?: boolean;
} }
const UNITS: { value: string; label: string }[] = [
{ value: "cm", label: "سانتی‌متر" },
{ value: "inch", label: "اینچ" },
];
export function SizeChartSection({ export function SizeChartSection({
value, value,
onChange, onChange,
@ -166,8 +171,26 @@ export function SizeChartSection({
</p> </p>
)} )}
<div className="flex items-center justify-end"> {/* Unit toggle */}
<span className="text-R12 text-GRAY">اندازهها به سانتیمتر</span> <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> </div>
{/* Grid */} {/* Grid */}

View File

@ -1,5 +1,4 @@
import { Reply, MoreVertical, Trash2, Check } from "lucide-react"; import { Reply, MoreVertical, Trash2, Check } from "lucide-react";
import { Link } from "@remix-run/react";
import { import {
DropdownMenu, DropdownMenu,
@ -82,34 +81,7 @@ export const MessageBubble = ({
{replyToMessageContent || "پاسخ به پیام"} {replyToMessageContent || "پاسخ به پیام"}
</div> </div>
)} )}
{message.product && ( <p>{message.content}</p>
<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>}
</div> </div>
<div className="flex flex-col justify-between items-start"> <div className="flex flex-col justify-between items-start">
<div className="flex flex-col gap-1 items-start"> <div className="flex flex-col gap-1 items-start">

View File

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

View File

@ -206,21 +206,15 @@ export function useGetThreadMessages(threadId: string) {
export function useCreateChatThread() { export function useCreateChatThread() {
const token = useAuthToken(); const token = useAuthToken();
return useMutation({ return useMutation({
mutationFn: async ( mutationFn: async (participantId: string) => {
input: string | { participantId: string; productId?: string }
) => {
if (!token) { if (!token) {
throw new Error("Authentication token and user ID are required"); 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 { try {
const chatApi = createChatApi(token); const chatApi = createChatApi(token);
return await chatApi.apiChatV1ThreadsCreateCreate({ return await chatApi.apiChatV1ThreadsCreateCreate({
data: { data: {
participantId, participantId: participantId,
...(productId ? { productId } : {}),
}, },
}); });
} catch (error) { } catch (error) {

View File

@ -2341,43 +2341,6 @@ export interface LastMessage {
* @memberof LastMessage * @memberof LastMessage
*/ */
readonly createdAt?: string; 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;
} }
/** /**
* *
@ -2521,12 +2484,6 @@ export interface MessageList {
* @memberof MessageList * @memberof MessageList
*/ */
readonly isThreadCreator?: boolean; 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 * @memberof ThreadCreate
*/ */
participantId: string; participantId: string;
/**
* Optional product the chat is about; shared into the thread as a card
* @type {string}
* @memberof ThreadCreate
*/
productId?: string | null;
} }
/** /**
* *