Adjust product state actions and icon font loading

This commit is contained in:
2026-05-10 20:25:43 +07:00
parent d8a0b9b05e
commit 9fb4cd2aa7
6 changed files with 171 additions and 43 deletions

View File

@ -30,6 +30,8 @@ interface ProductRow {
totalStock: number;
}
type ProductState = "UNPUBLISHED" | "DELETED_BY_SELLER" | "DELETED_BY_ADMIN" | string;
function getToken() {
if (typeof window === "undefined") {
return "";
@ -184,6 +186,8 @@ function ProductsPageInner() {
const [unpublishTarget, setUnpublishTarget] = useState<ProductRow | null>(null);
const [deleting, setDeleting] = useState(false);
const [unpublishing, setUnpublishing] = useState(false);
const [publishingId, setPublishingId] = useState<string | null>(null);
const [restoringId, setRestoringId] = useState<string | null>(null);
// Reset to page 1 when tab changes
useEffect(() => {
@ -263,6 +267,7 @@ function ProductsPageInner() {
}
async function handleRestore(productId: string) {
setRestoringId(productId);
try {
const res = await fetch(`/api/products/${productId}?action=restore`, {
method: "PUT",
@ -277,9 +282,37 @@ function ProductsPageInner() {
window.location.reload();
} catch (err) {
alert(err instanceof Error ? err.message : p.restoreError);
} finally {
setRestoringId(null);
}
}
async function handlePublish(productId: string) {
setPublishingId(productId);
try {
const res = await fetch(`/api/products/${productId}?action=publish`, {
method: "PUT",
headers: { "x-auth-token": getToken() },
});
const result = await res.json().catch(() => ({}));
if (!res.ok) {
throw new Error(result?.responseDesc || p.publishError);
}
window.location.reload();
} catch (err) {
alert(err instanceof Error ? err.message : p.publishError);
}
finally {
setPublishingId(null);
}
}
function getProductState(product: ProductRow): ProductState {
return (product.state || product.status || product.reviewStatus || "").toUpperCase();
}
const internationalCount = rows.filter(
(row) => row.market === "International"
).length;
@ -411,6 +444,12 @@ function ProductsPageInner() {
</tr>
) : (
rows.map((product) => {
const productState = getProductState(product);
const isInactiveInAllTab =
activeTab === "All Product" &&
(productState === "UNPUBLISHED" ||
productState === "DELETED_BY_SELLER" ||
productState === "DELETED_BY_ADMIN");
const stockTone =
product.totalStock === 0
? "red"
@ -441,7 +480,7 @@ function ProductsPageInner() {
fill
sizes="44px"
className={`object-cover ${
stockTone === "red" ? "grayscale" : ""
stockTone === "red" || isInactiveInAllTab ? "grayscale" : ""
}`}
/>
) : (
@ -453,7 +492,7 @@ function ProductsPageInner() {
<div>
<p
className={`text-xs font-bold transition-colors group-hover:text-primary ${
stockTone === "red"
stockTone === "red" || isInactiveInAllTab
? "text-outline line-through"
: "text-on-surface"
}`}
@ -469,7 +508,7 @@ function ProductsPageInner() {
<td className="px-6 py-4 text-center">
<span
className={`text-xs font-bold ${
stockTone === "red"
stockTone === "red" || isInactiveInAllTab
? "text-outline"
: "text-on-surface"
}`}
@ -503,39 +542,64 @@ function ProductsPageInner() {
<span
className={`inline-flex items-center rounded-full px-2 py-0.5 text-[9px] font-black uppercase tracking-tight ${marketClasses(
product.market
)}`}
)} ${isInactiveInAllTab ? "opacity-60 line-through" : ""}`}
>
{product.market}
</span>
</td>
<td className="px-6 py-4 text-center">
<div className="flex items-center justify-center gap-3">
{!isInReviewTab ? (
!isDeletedTab ? (
<Link
href={`/products/${product.id}/edit${activeTab === "Draft" ? "?draft=1" : ""}`}
className="rounded-lg bg-primary px-3 py-1 text-[10px] font-bold text-white transition-colors hover:bg-primary/90"
>
{p.edit}
</Link>
) : null
) : null}
<Link
href={`/products/${product.id}/detail${activeTab === "Draft" ? "?draft=1" : isInReviewTab ? "?review=1" : ""}`}
className="text-[10px] font-bold text-primary transition-colors hover:underline"
>
{p.detail}
</Link>
{isDeletedTab ? (
productState === "DELETED_BY_ADMIN" ? (
<span className="rounded-full bg-error-container px-3 py-1 text-[10px] font-bold text-on-error-container">
{p.deletedByAdmin}
</span>
) : (
<button
type="button"
onClick={() => handleRestore(product.id)}
disabled={restoringId === product.id}
className="rounded-lg bg-tertiary px-3 py-1 text-[10px] font-bold text-white transition-colors hover:bg-tertiary/90 disabled:opacity-60"
>
{restoringId === product.id ? p.publishing : p.restore}
</button>
)
) : productState === "UNPUBLISHED" ? (
<button
type="button"
onClick={() => handlePublish(product.id)}
disabled={publishingId === product.id}
className="rounded-lg bg-tertiary px-3 py-1 text-[10px] font-bold text-white transition-colors hover:bg-tertiary/90 disabled:opacity-60"
>
{publishingId === product.id ? p.publishing : p.publish}
</button>
) : productState === "DELETED_BY_SELLER" ? (
<button
type="button"
onClick={() => handleRestore(product.id)}
className="rounded-lg bg-tertiary px-3 py-1 text-[10px] font-bold text-white transition-colors hover:bg-tertiary/90"
disabled={restoringId === product.id}
className="rounded-lg bg-tertiary px-3 py-1 text-[10px] font-bold text-white transition-colors hover:bg-tertiary/90 disabled:opacity-60"
>
{p.restore}
{restoringId === product.id ? p.publishing : p.restore}
</button>
) : (
<>
{!isInReviewTab ? (
!isDeletedTab ? (
<Link
href={`/products/${product.id}/edit${activeTab === "Draft" ? "?draft=1" : ""}`}
className="rounded-lg bg-primary px-3 py-1 text-[10px] font-bold text-white transition-colors hover:bg-primary/90"
>
{p.edit}
</Link>
) : null
) : null}
<Link
href={`/products/${product.id}/detail${activeTab === "Draft" ? "?draft=1" : isInReviewTab ? "?review=1" : ""}`}
className="text-[10px] font-bold text-primary transition-colors hover:underline"
>
{p.detail}
</Link>
{canUnpublish ? (
<button
type="button"