Unify commercial runtime ownership across backend and frontend so platform, experience, and hospitality modules use the shared commercial source of truth. Co-authored-by: Cursor <cursoragent@cursor.com>
115 lines
4.7 KiB
TypeScript
115 lines
4.7 KiB
TypeScript
"use client";
|
||
|
||
import Link from "next/link";
|
||
import { Lock, Rocket, Sparkles } from "lucide-react";
|
||
import { Button, Card, CardContent, Badge } from "@/components/ds";
|
||
import { CommunicationBreadcrumbs } from "@/modules/communication/components/CommunicationBreadcrumbs";
|
||
import {
|
||
useCommunicationCapabilities,
|
||
isFeatureEnabled,
|
||
} from "@/modules/communication/hooks/useCommunicationCapabilities";
|
||
import { CommunicationPageLoader, CommunicationPageError } from "@/modules/communication/pages/shared";
|
||
import { FeatureLock } from "@/modules/commercial/components";
|
||
import { useTenantId } from "@/hooks/useTenantId";
|
||
import type { FeatureLockConfig } from "@/modules/communication/types";
|
||
|
||
/**
|
||
* L2 capability gate + Commercial Runtime entitlement.
|
||
* UI copy only (title/description/phase) — no commercial catalogs.
|
||
*/
|
||
export function CommunicationFeatureLock({
|
||
config,
|
||
children,
|
||
}: {
|
||
config: FeatureLockConfig;
|
||
children?: React.ReactNode;
|
||
}) {
|
||
const caps = useCommunicationCapabilities();
|
||
const { tenantId } = useTenantId();
|
||
|
||
if (caps.isLoading) return <CommunicationPageLoader label="بررسی قابلیت…" />;
|
||
if (caps.error) return <CommunicationPageError error={caps.error} onRetry={() => caps.refetch()} />;
|
||
|
||
const enabled = isFeatureEnabled(caps.data?.features, config.feature);
|
||
if (enabled && children) {
|
||
if (!tenantId) return <>{children}</>;
|
||
const commercialKey = config.feature.startsWith("communication.")
|
||
? config.feature
|
||
: `communication.${config.feature}`;
|
||
return (
|
||
<FeatureLock featureKey={commercialKey} tenantId={tenantId} requiredCapability={commercialKey}>
|
||
{children}
|
||
</FeatureLock>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<div className="space-y-6">
|
||
<CommunicationBreadcrumbs current={config.title} />
|
||
<Card className="overflow-hidden border-[var(--communication-accent)]/20">
|
||
<CardContent className="p-0">
|
||
<div className="bg-gradient-to-br from-[var(--communication-accent-soft)] to-transparent p-8">
|
||
<div className="mb-4 flex items-center gap-3">
|
||
<div className="rounded-2xl bg-[var(--communication-accent)]/15 p-3">
|
||
<Lock className="h-8 w-8 text-[var(--communication-accent)]" />
|
||
</div>
|
||
<div className="text-right">
|
||
<Badge tone="warning" className="mb-1">
|
||
بهزودی
|
||
</Badge>
|
||
<h2 className="text-xl font-bold text-secondary">{config.title}</h2>
|
||
</div>
|
||
</div>
|
||
<p className="text-sm leading-relaxed text-[var(--muted)]">{config.description}</p>
|
||
{config.phase ? (
|
||
<p className="mt-3 text-xs text-[var(--muted)]">
|
||
<Rocket className="mr-1 inline h-3.5 w-3.5" />
|
||
فاز برنامهریزی: {config.phase}
|
||
</p>
|
||
) : null}
|
||
</div>
|
||
<div className="space-y-4 border-t border-[var(--border)] p-6">
|
||
<div className="flex items-center gap-2 text-sm text-[var(--muted)]">
|
||
<Sparkles className="h-4 w-4 text-[var(--communication-accent)]" />
|
||
entitlement و قابلیتها فقط از Commercial Runtime / L2 capabilities.
|
||
</div>
|
||
<p className="text-xs text-[var(--muted)]">
|
||
باز شدن: وقتی{" "}
|
||
<code className="rounded bg-[var(--surface-muted)] px-1">/capabilities</code> ویژگی{" "}
|
||
<code className="font-mono">{config.feature}</code> را فعال کند و Commercial Runtime مجوز
|
||
دهد.
|
||
</p>
|
||
<div className="flex flex-wrap gap-2">
|
||
<Link href="/communication/capabilities">
|
||
<Button variant="outline">مشاهده قابلیتها</Button>
|
||
</Link>
|
||
<Link href="/admin/commercial/capabilities">
|
||
<Button variant="outline">Commercial Capabilities</Button>
|
||
</Link>
|
||
<Link href="/communication">
|
||
<Button>بازگشت به داشبورد</Button>
|
||
</Link>
|
||
</div>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export function createCommunicationLockedPage(config: FeatureLockConfig) {
|
||
return function CommunicationLockedPage() {
|
||
return <CommunicationFeatureLock config={config} />;
|
||
};
|
||
}
|
||
|
||
export function createCommunicationFeatureGate(config: FeatureLockConfig, Page: React.ComponentType) {
|
||
return function CommunicationGatedPage() {
|
||
return (
|
||
<CommunicationFeatureLock config={config}>
|
||
<Page />
|
||
</CommunicationFeatureLock>
|
||
);
|
||
};
|
||
}
|