TorbatYar/frontend/modules/commercial/components/ProductCard.tsx

149 lines
5.3 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import Link from "next/link";
import type { CommercialProduct } from "../types";
import { resolveProductLaunchUrl } from "../types";
import { Button } from "@/components/ui";
import { FeatureLock } from "./FeatureLock";
import { pushRecent } from "../launcher-prefs";
import { enumLabel } from "../presentation";
export function ProductCard({
product,
recommendationScore,
tenantId,
favorite,
pinned,
onToggleFavorite,
onTogglePinned,
}: {
product: CommercialProduct;
recommendationScore?: number;
tenantId?: string;
favorite?: boolean;
pinned?: boolean;
onToggleFavorite?: () => void;
onTogglePinned?: () => void;
}) {
const visibilityLocked = product.visibility === "hidden" || product.status === "inactive";
const launchUrl = resolveProductLaunchUrl(product);
const strategy = product.launch_strategy || "route";
const requiredCap = product.required_capabilities?.[0];
const setupHref = `/setup/${encodeURIComponent(product.product_code)}`;
const markRecent = () => {
pushRecent(product.product_code);
};
const launchButton =
launchUrl && !visibilityLocked && strategy !== "disabled" ? (
strategy === "external" ? (
<a href={launchUrl} target="_blank" rel="noreferrer" onClick={markRecent}>
<Button size="sm" variant="outline" className="w-full">
باز کردن
</Button>
</a>
) : (
<Link href={launchUrl} onClick={markRecent}>
<Button size="sm" variant="outline" className="w-full">
باز کردن
</Button>
</Link>
)
) : null;
return (
<div
className={`rounded-2xl border p-4 transition hover:shadow-md ${
visibilityLocked
? "border-gray-100 bg-gray-50 opacity-70 dark:border-gray-800 dark:bg-gray-900"
: "border-gray-200 bg-white shadow-sm dark:border-gray-800 dark:bg-gray-900"
}`}
>
<div className="flex items-start gap-3">
<div
className="flex h-11 w-11 shrink-0 items-center justify-center rounded-xl text-lg font-bold text-white"
style={{ background: product.color || "var(--color-primary)" }}
>
{product.icon || (product.display_name?.charAt(0) ?? "P")}
</div>
<div className="min-w-0 flex-1">
<div className="flex flex-wrap items-center gap-2">
<h3 className="font-semibold text-secondary dark:text-gray-100">{product.display_name}</h3>
{product.category ? (
<span className="rounded-full bg-gray-100 px-2 py-0.5 text-[10px] text-gray-600">
{enumLabel(product.category)}
</span>
) : null}
{typeof recommendationScore === "number" ? (
<span className="text-[10px] text-primary">پیشنهاد مناسب شما</span>
) : null}
</div>
{product.description ? (
<p className="mt-1 text-xs leading-5 text-gray-500">{product.description}</p>
) : null}
{product.trial_eligible ? <p className="mt-2 text-xs font-medium text-emerald-700">امکان استفاده آزمایشی</p> : null}
</div>
</div>
<div className="mt-3 flex flex-wrap gap-2">
{onToggleFavorite ? (
<button
type="button"
aria-pressed={favorite}
aria-label={favorite ? "حذف از علاقه‌مندی" : "افزودن به علاقه‌مندی"}
className={`rounded-lg px-2 py-1 text-xs ${
favorite ? "bg-amber-100 text-amber-800" : "bg-gray-100 text-gray-600 dark:bg-gray-800"
}`}
onClick={onToggleFavorite}
>
{favorite ? "★" : "☆"}
</button>
) : null}
{onTogglePinned ? (
<button
type="button"
aria-pressed={pinned}
aria-label={pinned ? "برداشتن پین" : "پین کردن"}
className={`rounded-lg px-2 py-1 text-xs ${
pinned ? "bg-sky-100 text-sky-800" : "bg-gray-100 text-gray-600 dark:bg-gray-800"
}`}
onClick={onTogglePinned}
>
{pinned ? "📌" : "سنجاق"}
</button>
) : null}
<Link href={setupHref} className="rounded-lg bg-gray-100 px-2 py-1 text-xs text-gray-700 dark:bg-gray-800 dark:text-gray-300">
راهاندازی
</Link>
</div>
{launchButton ? (
<div className="mt-3">
{tenantId && requiredCap ? (
<FeatureLock
tenantId={tenantId}
featureKey={requiredCap}
fallback={
<Button size="sm" variant="outline" className="w-full">
قفل ارتقا لازم
</Button>
}
>
{launchButton}
</FeatureLock>
) : (
launchButton
)}
</div>
) : (
<div className="mt-3">
<Link href={setupHref}>
<Button size="sm" variant="outline" className="w-full">
شروع راهاندازی
</Button>
</Link>
</div>
)}
</div>
);
}