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>
161 lines
5.9 KiB
JavaScript
161 lines
5.9 KiB
JavaScript
#!/usr/bin/env node
|
|
/** Scaffolds communication app routes + locked feature pages */
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const ROOT = path.join(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
const APP = path.join(ROOT, "app", "communication");
|
|
const FEATURES = path.join(ROOT, "modules", "communication", "features");
|
|
|
|
const lockKeyMap = {
|
|
email: "email",
|
|
whatsapp: "whatsapp",
|
|
telegram: "telegram",
|
|
rubika: "rubika",
|
|
push: "push",
|
|
voice: "voice",
|
|
video: "video",
|
|
liveChat: "live_chat",
|
|
omnichannelInbox: "omnichannel_inbox",
|
|
marketingAutomation: "marketing_automation",
|
|
automationTriggers: "automation_triggers",
|
|
journeyBuilder: "journey_builder",
|
|
aiCampaigns: "ai_campaigns",
|
|
webhookCenter: "webhook_center",
|
|
connectors: "connectors",
|
|
conversations: "conversations",
|
|
campaignOrchestrator: "campaign_orchestrator",
|
|
notificationCenter: "notification_center",
|
|
templateCategories: "template_categories",
|
|
roles: "roles",
|
|
audit: "audit",
|
|
apiKeys: "api_keys",
|
|
integrationsHub: "integrations_hub",
|
|
};
|
|
|
|
const PAGES = [
|
|
["", "ExecutiveDashboard", "executiveDashboard"],
|
|
["dashboard", "CommunicationDashboard", "dashboard"],
|
|
["providers/dashboard", "ProviderDashboard", "providerDashboard"],
|
|
["sms", "SmsDashboard", "smsDashboard"],
|
|
["sms/providers", "ProvidersPage", "providers"],
|
|
["sms/templates", "TemplatesPage", "templates"],
|
|
["sms/template-categories", "TemplateCategoriesPage", "templateCategories"],
|
|
["sms/senders", "SendersPage", "senders"],
|
|
["sms/campaigns", "CampaignsPage", "campaigns"],
|
|
["sms/send", "SendMessagePage", "sendMessage"],
|
|
["sms/queue", "QueuePage", "queue"],
|
|
["sms/delivery-reports", "DeliveryReportsPage", "deliveryReports"],
|
|
["sms/messages", "MessagesPage", "messages"],
|
|
["contacts/recipients", "RecipientsPage", "recipients"],
|
|
["contacts/sources", "ContactSourcesPage", "contactSources"],
|
|
["contacts/address-books", "AddressBooksPage", "recipients"],
|
|
["email", "EmailPage", "email"],
|
|
["whatsapp", "WhatsappPage", "whatsapp"],
|
|
["telegram", "TelegramPage", "telegram"],
|
|
["rubika", "RubikaPage", "rubika"],
|
|
["push", "PushPage", "push"],
|
|
["voice", "VoicePage", "voice"],
|
|
["video", "VideoPage", "video"],
|
|
["live-chat", "LiveChatPage", "liveChat"],
|
|
["inbox", "OmnichannelInboxPage", "omnichannelInbox"],
|
|
["automation", "MarketingAutomationPage", "marketingAutomation"],
|
|
["automation/triggers", "AutomationTriggersPage", "automationTriggers"],
|
|
["journey-builder", "JourneyBuilderPage", "journeyBuilder"],
|
|
["ai-campaigns", "AiCampaignsPage", "aiCampaigns"],
|
|
["webhooks", "WebhookCenterPage", "webhookCenter"],
|
|
["connectors", "ConnectorsPage", "connectors"],
|
|
["conversations", "ConversationsPage", "conversations"],
|
|
["campaign-orchestrator", "CampaignOrchestratorPage", "campaignOrchestrator"],
|
|
["notifications", "NotificationCenterPage", "notificationCenter"],
|
|
["monitoring", "MonitoringPage", "monitoring"],
|
|
["analytics", "AnalyticsPage", "monitoring"],
|
|
["reports", "ReportsPage", "monitoring"],
|
|
["health", "HealthPage", "health"],
|
|
["capabilities", "CapabilitiesPage", "capabilities"],
|
|
["settings", "SettingsPage", "settings"],
|
|
["permissions", "PermissionsPage", "permissions"],
|
|
["roles", "RolesPage", "roles"],
|
|
["audit", "AuditPage", "audit"],
|
|
["api-keys", "ApiKeysPage", "apiKeys"],
|
|
["integrations", "IntegrationsHubPage", "integrationsHub"],
|
|
["otp", "OtpPage", "otp"],
|
|
];
|
|
|
|
function ensureDir(d) {
|
|
fs.mkdirSync(d, { recursive: true });
|
|
}
|
|
|
|
function writeLockedFeature(featureFile, lockKey, exportName) {
|
|
const file = path.join(FEATURES, `${featureFile}.tsx`);
|
|
if (fs.existsSync(file)) return;
|
|
fs.writeFileSync(
|
|
file,
|
|
`import { createCommunicationLockedPage } from "@/modules/communication/components/CommunicationFeatureLock";
|
|
import { FUTURE_MODULE_REGISTRY } from "@/modules/communication/constants/featureRegistry";
|
|
|
|
const config = FUTURE_MODULE_REGISTRY.${lockKey}!;
|
|
export const ${exportName} = createCommunicationLockedPage(config);
|
|
export default ${exportName};
|
|
`
|
|
);
|
|
}
|
|
|
|
function writeRoute(routePath, exportName, featureFile) {
|
|
const dir = path.join(APP, routePath);
|
|
ensureDir(dir);
|
|
fs.writeFileSync(
|
|
path.join(dir, "page.tsx"),
|
|
`"use client";
|
|
export { ${exportName} as default } from "@/modules/communication/features/${featureFile}";
|
|
`
|
|
);
|
|
fs.writeFileSync(
|
|
path.join(dir, "loading.tsx"),
|
|
`import { LoadingState } from "@/components/ds";
|
|
export default function Loading() { return <LoadingState label="بارگذاری…" />; }
|
|
`
|
|
);
|
|
fs.writeFileSync(
|
|
path.join(dir, "error.tsx"),
|
|
`"use client";
|
|
import { ErrorState } from "@/components/ds";
|
|
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
|
return <ErrorState message={error.message} onRetry={reset} />;
|
|
}
|
|
`
|
|
);
|
|
}
|
|
|
|
ensureDir(APP);
|
|
fs.writeFileSync(
|
|
path.join(APP, "layout.tsx"),
|
|
`"use client";
|
|
import { CommunicationRootLayout } from "@/modules/communication/components/createPortalLayout";
|
|
export default CommunicationRootLayout;
|
|
`
|
|
);
|
|
fs.writeFileSync(
|
|
path.join(APP, "not-found.tsx"),
|
|
`import Link from "next/link";
|
|
import { EmptyState, Button } from "@/components/ds";
|
|
export default function NotFound() {
|
|
return (
|
|
<EmptyState title="صفحه یافت نشد" description="مسیر ارتباطات نامعتبر است." action={<Link href="/communication"><Button>داشبورد</Button></Link>} />
|
|
);
|
|
}
|
|
`
|
|
);
|
|
|
|
for (const [file, key] of Object.entries(lockKeyMap)) {
|
|
const exportName = file.charAt(0).toUpperCase() + file.slice(1) + "Page";
|
|
writeLockedFeature(file, key, exportName);
|
|
}
|
|
|
|
for (const [route, exportName, featureFile] of PAGES) {
|
|
writeRoute(route, exportName, featureFile);
|
|
}
|
|
|
|
console.log(`Scaffolded ${PAGES.length} communication routes`);
|