Ship the full Communication portal (real SMS API, feature-locked future channels), BFF proxy, docs, snapshot, and phase handover. Co-authored-by: Cursor <cursoragent@cursor.com>
141 lines
6.0 KiB
TypeScript
141 lines
6.0 KiB
TypeScript
"use client";
|
||
|
||
import Link from "next/link";
|
||
import {
|
||
Lock,
|
||
Rocket,
|
||
Sparkles,
|
||
Plug,
|
||
CheckCircle2,
|
||
} 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 type { FeatureLockConfig } from "@/modules/communication/types";
|
||
|
||
export function CommunicationFeatureLock({
|
||
config,
|
||
children,
|
||
}: {
|
||
config: FeatureLockConfig;
|
||
children?: React.ReactNode;
|
||
}) {
|
||
const caps = useCommunicationCapabilities();
|
||
|
||
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) return <>{children}</>;
|
||
|
||
return (
|
||
<div className="space-y-6">
|
||
<CommunicationBreadcrumbs current={config.title} />
|
||
<div className="grid gap-6 lg:grid-cols-2">
|
||
<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">
|
||
{config.bundle ? `نیازمند ${config.bundle}` : "بهزودی"}
|
||
</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="border-t border-[var(--border)] p-6">
|
||
<p className="mb-3 flex items-center gap-2 text-sm font-semibold text-secondary">
|
||
<Sparkles className="h-4 w-4 text-[var(--communication-accent)]" />
|
||
قابلیتهای برنامهریزیشده
|
||
</p>
|
||
<ul className="space-y-2">
|
||
{config.capabilities.map((c) => (
|
||
<li key={c} className="flex items-start gap-2 text-sm text-[var(--muted)]">
|
||
<CheckCircle2 className="mt-0.5 h-4 w-4 shrink-0 text-[var(--communication-success)]" />
|
||
{c}
|
||
</li>
|
||
))}
|
||
</ul>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
<Card>
|
||
<CardContent className="space-y-4 p-6">
|
||
<div className="aspect-video rounded-xl border border-dashed border-[var(--border)] bg-[var(--surface-muted)] p-4">
|
||
<div className="flex h-full flex-col items-center justify-center gap-2 text-center">
|
||
<div className="rounded-full bg-[var(--communication-accent-soft)] p-4 opacity-80">
|
||
<Sparkles className="h-10 w-10 text-[var(--communication-accent)]" />
|
||
</div>
|
||
<p className="text-sm font-medium text-secondary">پیشنمایش رابط کاربری</p>
|
||
<p className="max-w-xs text-xs text-[var(--muted)]">
|
||
این ماژول با طراحی production-ready آماده است و پس از فعالسازی backend بهصورت خودکار
|
||
باز میشود.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
{config.integrations?.length ? (
|
||
<div>
|
||
<p className="mb-2 flex items-center gap-2 text-sm font-semibold">
|
||
<Plug className="h-4 w-4" />
|
||
یکپارچهسازیها
|
||
</p>
|
||
<div className="flex flex-wrap gap-2">
|
||
{config.integrations.map((i) => (
|
||
<Badge key={i} tone="default">
|
||
{i}
|
||
</Badge>
|
||
))}
|
||
</div>
|
||
</div>
|
||
) : null}
|
||
<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> را فعال کند.
|
||
</p>
|
||
<div className="flex flex-wrap gap-2">
|
||
<Link href="/communication/capabilities">
|
||
<Button variant="outline">مشاهده قابلیتها</Button>
|
||
</Link>
|
||
<Link href="/communication">
|
||
<Button>بازگشت به داشبورد</Button>
|
||
</Link>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
</div>
|
||
</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>
|
||
);
|
||
};
|
||
}
|