TorbatYar/frontend/modules/communication/components/CommunicationFeatureLock.tsx
Mortezakoohjani 10c3c43a75 feat(communication): add SMS MVP frontend with governance artifacts.
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>
2026-07-27 11:11:25 +03:30

141 lines
6.0 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 {
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>
);
};
}