Create Glassmond storefront with Meshkee integration
Build the RTL Next.js experience with live catalog, journal, customer interactions, and SEO-ready canonical routes. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
"use client";
|
||||
|
||||
import { FormEvent, useState } from "react";
|
||||
import { MessageSquare, Send, UserRound } from "lucide-react";
|
||||
import { useAuth } from "@/components/auth-provider";
|
||||
import {
|
||||
CUSTOMER_URL,
|
||||
MESHKEE_API_BASE,
|
||||
WEBSITE_DOMAIN,
|
||||
type PublicComment,
|
||||
} from "@/lib/meshkee";
|
||||
|
||||
export function CommentsPanel({
|
||||
entityType,
|
||||
entityId,
|
||||
comments,
|
||||
}: {
|
||||
entityType: "product" | "blog";
|
||||
entityId: string;
|
||||
comments: PublicComment[];
|
||||
}) {
|
||||
const { user, loading } = useAuth();
|
||||
const [status, setStatus] = useState<"idle" | "sending" | "sent" | "error">("idle");
|
||||
|
||||
async function submit(event: FormEvent<HTMLFormElement>) {
|
||||
event.preventDefault();
|
||||
if (!user) return;
|
||||
|
||||
const form = event.currentTarget;
|
||||
const data = new FormData(form);
|
||||
const text = String(data.get("text") ?? "").trim();
|
||||
if (!text) return;
|
||||
|
||||
setStatus("sending");
|
||||
try {
|
||||
const authorName =
|
||||
[user.firstName, user.lastName].filter(Boolean).join(" ") ||
|
||||
user.name ||
|
||||
"کاربر گلسموند";
|
||||
const response = await fetch(
|
||||
`${MESHKEE_API_BASE}/tenants/${WEBSITE_DOMAIN}/comments`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
entityType,
|
||||
entityId,
|
||||
authorName,
|
||||
authorEmail: user.email || undefined,
|
||||
text,
|
||||
}),
|
||||
},
|
||||
);
|
||||
if (!response.ok) throw new Error("Comment failed");
|
||||
setStatus("sent");
|
||||
form.reset();
|
||||
} catch {
|
||||
setStatus("error");
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="comments-panel">
|
||||
<div className="comments-list">
|
||||
{comments.length > 0 ? (
|
||||
comments.map((comment) => (
|
||||
<article key={comment.id}>
|
||||
<span><UserRound /></span>
|
||||
<div>
|
||||
<h3>{comment.authorName}</h3>
|
||||
<p>{comment.text}</p>
|
||||
</div>
|
||||
</article>
|
||||
))
|
||||
) : (
|
||||
<div className="comments-empty">
|
||||
<MessageSquare />
|
||||
<p>هنوز دیدگاهی ثبت نشده است. اولین نفر باشید.</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{!loading && (
|
||||
user ? (
|
||||
<form className="comment-form" onSubmit={submit}>
|
||||
<label htmlFor={`comment-${entityId}`}>دیدگاه شما</label>
|
||||
<textarea
|
||||
id={`comment-${entityId}`}
|
||||
name="text"
|
||||
rows={5}
|
||||
placeholder="نظر خود را بنویسید..."
|
||||
required
|
||||
/>
|
||||
<button className="primary-button" type="submit" disabled={status === "sending"}>
|
||||
{status === "sending" ? "در حال ارسال..." : "ثبت دیدگاه"}
|
||||
<Send />
|
||||
</button>
|
||||
{status === "sent" && <p className="comment-success">دیدگاه شما ثبت شد و پس از تأیید نمایش داده میشود.</p>}
|
||||
{status === "error" && <p className="form-error">ارسال دیدگاه انجام نشد. دوباره تلاش کنید.</p>}
|
||||
</form>
|
||||
) : (
|
||||
<div className="comment-login-box">
|
||||
<UserRound />
|
||||
<div>
|
||||
<h3>برای ثبت دیدگاه وارد شوید</h3>
|
||||
<p>ثبت نظر برای کاربران واردشده امکانپذیر است.</p>
|
||||
</div>
|
||||
<a className="primary-button" href={CUSTOMER_URL}>ورود به حساب</a>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function ProductTabs({
|
||||
descriptionHtml,
|
||||
entityId,
|
||||
comments,
|
||||
}: {
|
||||
descriptionHtml: string;
|
||||
entityId: string;
|
||||
comments: PublicComment[];
|
||||
}) {
|
||||
const [tab, setTab] = useState<"description" | "comments">("description");
|
||||
|
||||
return (
|
||||
<section className="entity-tabs">
|
||||
<div className="tab-buttons" role="tablist" aria-label="اطلاعات محصول">
|
||||
<button
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-selected={tab === "description"}
|
||||
className={tab === "description" ? "active" : ""}
|
||||
onClick={() => setTab("description")}
|
||||
>
|
||||
توضیحات
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-selected={tab === "comments"}
|
||||
className={tab === "comments" ? "active" : ""}
|
||||
onClick={() => setTab("comments")}
|
||||
>
|
||||
دیدگاهها
|
||||
<span>{comments.length.toLocaleString("fa-IR")}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="tab-content">
|
||||
{tab === "description" ? (
|
||||
<div
|
||||
className="cms-content"
|
||||
dangerouslySetInnerHTML={{ __html: descriptionHtml }}
|
||||
/>
|
||||
) : (
|
||||
<CommentsPanel entityType="product" entityId={entityId} comments={comments} />
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user