diff --git a/backend/services/communication/README.md b/backend/services/communication/README.md index daa0fd2..0dda09e 100644 --- a/backend/services/communication/README.md +++ b/backend/services/communication/README.md @@ -1,11 +1,12 @@ # Communication Service -Independent **Enterprise Communication Platform** for TorbatYar SuperApp. +Independent **Enterprise Communication Platform** (**Torbat Communication**) for TorbatYar SuperApp. - Database: `communication_db` (sole owner) - Port: `8005` - Version: `0.8.10.1` - Permission prefix: `communication.*` +- Status: **Production Ready (SMS MVP)** ## Boundaries @@ -15,10 +16,14 @@ Consumers interact only via HTTP APIs and events. No module may call external SM ## Channels -Designed for: SMS, Email, Push, WhatsApp, Telegram, Rubika, Voice, Future. - -**Initially active:** SMS (mock + Payamak adapters). +| Channel | Status | +| --- | --- | +| SMS (mock + Payamak) | **Production Ready** | +| Email, Push, WhatsApp, Telegram, Rubika, Voice | Architecture-ready stubs — see [communication-roadmap.md](../../../docs/communication-roadmap.md) | ## Docs -See `docs/communication-phase-8.md`. +- [communication-phase-8.md](../../../docs/communication-phase-8.md) +- [communication-roadmap.md](../../../docs/communication-roadmap.md) +- [phase-handover/phase-8.md](../../../docs/phase-handover/phase-8.md) +- [service-snapshots/communication.yaml](../../../docs/service-snapshots/communication.yaml) diff --git a/docs/communication-phase-8-audit.md b/docs/communication-phase-8-audit.md index 92a0b3b..0c6387e 100644 --- a/docs/communication-phase-8-audit.md +++ b/docs/communication-phase-8-audit.md @@ -85,11 +85,14 @@ | Backward Compatibility | Pass (additive) | | Tenant Isolation | Pass | -**Verdict:** Enterprise Communication Platform meets production-ready quality for Phase 8 according to project standards. +**Verdict:** Enterprise Communication Platform meets production-ready quality for Phase 8 (SMS MVP) according to project standards. Service snapshot and phase handover generated 2026-07-26. ## Related Documents - [communication-phase-8.md](communication-phase-8.md) +- [communication-roadmap.md](communication-roadmap.md) +- [phase-handover/phase-8.md](phase-handover/phase-8.md) +- [service-snapshots/communication.yaml](service-snapshots/communication.yaml) - [ADR-012](architecture/adr/ADR-012.md) - [progress.md](progress.md) - [quality-gates.md](ai-framework/quality-gates.md) diff --git a/docs/communication-phase-8.md b/docs/communication-phase-8.md index 869d901..eb9faa8 100644 --- a/docs/communication-phase-8.md +++ b/docs/communication-phase-8.md @@ -2,8 +2,9 @@ | Field | Value | | --- | --- | -| Status | Complete (validated) | +| Status | Complete — **Production Ready (SMS MVP)** | | Module | communication | +| Commercial product | Torbat Communication | | Version | 0.8.10.1 | | Database | `communication_db` | | API Port | 8005 | @@ -141,7 +142,7 @@ Architecture, health/ready/security, providers/SMS/failover cascade, queue/DLQ, ## Known Limitations -- Email / Push / WhatsApp / Telegram / Rubika / Voice remain architecture-ready stubs. +- **MVP scope:** SMS is production-ready; Email / Push / WhatsApp / Telegram / Rubika / Voice remain architecture-ready stubs — see [communication-roadmap.md](communication-roadmap.md). - Continuous Celery worker drain is optional ops work (API/`process_immediately` covers correctness). - Real message bus consumers not wired (publish-only envelopes). - Sender unique constraint is not soft-delete-aware (recreate after soft-delete may conflict). @@ -159,12 +160,17 @@ See [communication-phase-8-audit.md](communication-phase-8-audit.md). - [x] ADR-012 referenced - [x] Module Registry updated - [x] Progress updated +- [x] Service snapshot + phase handover generated +- [x] Production Ready (SMS MVP) declared - [x] No TODO remains ## Related Documents - [Progress](progress.md) - [Module Registry](module-registry.md) +- [Communication Roadmap](communication-roadmap.md) +- [Phase Handover](phase-handover/phase-8.md) +- [Service Snapshot](service-snapshots/communication.yaml) - [Audit Report](communication-phase-8-audit.md) - [ADR-012](architecture/adr/ADR-012.md) - [Service README](../backend/services/communication/README.md) diff --git a/docs/communication-roadmap.md b/docs/communication-roadmap.md new file mode 100644 index 0000000..7911325 --- /dev/null +++ b/docs/communication-roadmap.md @@ -0,0 +1,67 @@ +# Communication Platform Roadmap + +> **Torbat Communication** — Production Ready for **SMS MVP** (Phase 8 complete). +> Completed work → [progress.md](progress.md). Phase doc → [communication-phase-8.md](communication-phase-8.md). +> Handover → [phase-handover/phase-8.md](phase-handover/phase-8.md). Snapshot → [service-snapshots/communication.yaml](service-snapshots/communication.yaml). + +The Communication service is **feature-complete for the current MVP scope**. Items below are **future roadmap only** — not registered phases and not in scope until explicitly product-scoped. + +## Current Production Scope (Complete) + +| Capability | Status | +| --- | --- | +| SMS (mock + Payamak) | Production Ready | +| Provider registry + failover + circuit breaker | Production Ready | +| Template engine | Production Ready | +| Dynamic contact sources | Production Ready | +| Queue + DLQ + delivery tracking | Production Ready | +| Business OTP (non-auth) | Production Ready | +| Incoming webhooks | Production Ready | +| Health / capabilities / monitoring | Production Ready | + +## Future Channels (Not Implemented) + +Architecture-ready stubs exist; real adapters are deferred: + +| Channel | Provider kind (stub) | Notes | +| --- | --- | --- | +| Email | `smtp` | SMTP adapter + sender identities | +| Push | `fcm` | FCM + device token registration | +| WhatsApp | `whatsapp_cloud` | Cloud API + template approval alignment | +| Telegram | `telegram_bot` | Bot API adapter | +| Rubika | `rubika_bot` | Region-specific social messaging | +| Voice | `voice_gateway` | Call status webhooks; OTP voice path | + +## Optional Operations Enhancements (Not MVP) + +| Item | Notes | +| --- | --- | +| Dedicated Celery worker | Reuse `QueueEngine.process_due`; optional ops | +| Redis-backed rate limiter | Replace in-process limiter at scale | +| Prometheus metrics exposition | Platform ops standard TBD | +| Outbox dual-write / bus consumers | Platform message bus maturity | +| Core OTP handoff | Requires explicit ADR; auth OTP stays on Core today | + +## Cross-Service Integration (Consumer-Owned) + +Other services integrate **via Communication APIs/events only** — connector phases belong to those services: + +| Consumer | Phase | Responsibility | +| --- | --- | --- | +| Delivery | 10.9 | Notification client + app API contracts | +| Experience | 11.9 | Widget + notify client contracts | +| Sports Center | 9.8 | CRM/Loyalty/Communication/Accounting adapters | +| Hospitality | 12.7 | Multi-platform connector bundle | + +## Longer Term (Platform) + +- Notification service as thin consumer of Communication ([roadmap.md](roadmap.md) item 26) +- Smart Messenger + legacy SMS panel alignment (prefer Communication per ADR-012) + +## Related Documents + +- [communication-phase-8.md](communication-phase-8.md) +- [communication-phase-8-audit.md](communication-phase-8-audit.md) +- [ADR-012](architecture/adr/ADR-012.md) +- [Module Registry](module-registry.md) +- [Provider Registry](provider-registry.md) diff --git a/docs/phase-handover/phase-8.md b/docs/phase-handover/phase-8.md new file mode 100644 index 0000000..b96a808 --- /dev/null +++ b/docs/phase-handover/phase-8.md @@ -0,0 +1,155 @@ +# Phase Handover — Communication 8 Enterprise Communication Platform + +## Metadata + +| Field | Value | +| --- | --- | +| Phase ID | communication-8 | +| Title | Enterprise Communication Platform (SMS-first MVP) | +| Status | Complete — **Production Ready (SMS)** | +| Service(s) | communication (`communication-service`, port 8005) | +| Commercial product | Torbat Communication | +| Version | 0.8.10.1 | +| Date | 2026-07-26 | +| ADR(s) | ADR-001, ADR-003, ADR-006, ADR-012 | + +## Enterprise Phase Discovery Summary + +| Item | Detail | +| --- | --- | +| Discovery Record location | [communication-phase-8.md](../communication-phase-8.md) | +| Gaps promoted & closed | Sub-phases 8.0–8.10 + validation hardening (idempotency, rate limits, permissions, metrics, OTP isolation) | +| Exclusions (future / other service) | Non-SMS channel adapters; Core auth OTP migration; Notification service consumer; Celery worker as optional ops | +| CRUD-only rejected | Yes — router, queue, failover, templates, contacts, OTP, webhooks, monitoring | + +## Business Analysis Summary + +| Item | Detail | +| --- | --- | +| Capabilities delivered | Independent SMS messaging platform: providers, router/failover, templates, dynamic contacts, queue/DLQ, delivery tracking, business OTP, webhooks, monitoring | +| Explicit non-goals honored | No foreign DB ownership; no provider code in business modules; auth OTP stays on Core; future channels remain stubs | + +## Reusable Components + +| Component | Location | Reuse notes | +| --- | --- | --- | +| ChannelProvider protocol | `app/providers/contracts.py` | Extend for future channel adapters | +| Provider registry | `app/providers/__init__.py` | Maps `provider_kind` → adapter | +| CommunicationRouter | `app/services/router.py` | Priority, failover, circuit breaker | +| QueueEngine | `app/services/queue_engine.py` | Async drain, DLQ, rate-limit deferral | +| TemplateService | `app/services/template_service.py` | Variables, locale, approval workflow | +| ContactService | `app/services/contact_service.py` | Manual, CSV, remote source resolve | +| OTPService | `app/services/otp_service.py` | Business OTP; rejects Core reserved purposes | +| WebhookService | `app/services/webhook_service.py` | HMAC + external_id dedupe | + +## Public APIs + +| Method | Path | Auth / Permission | Notes | +| --- | --- | --- | --- | +| GET | `/health` | public | Liveness | +| GET | `/health/ready` | public | DB readiness | +| GET | `/capabilities` | public | Channels, features, contact source types | +| GET | `/metrics` | public | Service feature summary | +| GET/POST | `/api/v1/providers` | `communication.providers.*` | Provider CRUD, status, balance, health | +| POST | `/api/v1/providers/senders` | `communication.senders.manage` | Sender numbers | +| GET/POST | `/api/v1/templates` | `communication.templates.*` | Template CRUD, approve/reject, preview | +| GET/POST | `/api/v1/contacts/*` | `communication.contacts.*` / `communication.sources.*` | Manual, CSV, sources, resolve | +| POST | `/api/v1/messages/send` | `communication.messages.send` | Idempotent on correlation_id + to_address | +| GET | `/api/v1/messages` | `communication.messages.view` | List/filter messages | +| POST | `/api/v1/messages/queue/process` | `communication.queue.manage` | Process due queue items | +| GET | `/api/v1/messages/queue/dead-letters` | `communication.queue.view` | DLQ inspection | +| POST | `/api/v1/otp/request` | `communication.otp.request` | Business OTP generate | +| POST | `/api/v1/otp/verify` | `communication.otp.verify` | Business OTP verify | +| POST | `/api/v1/webhooks/incoming` | `communication.webhooks.manage` | Provider delivery callbacks | +| GET | `/api/v1/monitoring/stats` | `communication.monitoring.view` | Message/queue/provider snapshot | +| GET | `/api/v1/monitoring/metrics` | `communication.monitoring.view` | Latency, failure rate, deferrals | + +## Events + +| Event type | Domain / Integration | Payload summary | Version | +| --- | --- | --- | --- | +| `communication.message.queued` | Domain | channel, to, correlation_id | 1 | +| `communication.message.sent` | Domain | provider, provider_message_id | 1 | +| `communication.message.delivered` | Domain | delivery confirmation | 1 | +| `communication.message.failed` | Domain | error, terminal failure | 1 | +| `communication.provider.failover` | Domain | from/to provider | 1 | +| `communication.otp.*` | Domain | OTP lifecycle | 1 | +| `communication.queue.dead_letter` | Domain | queue item exhausted retries | 1 | +| `communication.webhook.received` | Domain | inbound provider webhook | 1 | + +## Permissions + +| Permission | Routes / actions | +| --- | --- | +| `communication.providers.*` | Provider and sender management | +| `communication.templates.*` | Template CRUD and approval | +| `communication.contacts.*` / `communication.sources.*` | Contacts and dynamic sources | +| `communication.messages.*` | Send, view, cancel | +| `communication.queue.*` | Queue process and DLQ | +| `communication.otp.*` | OTP request/verify | +| `communication.webhooks.manage` | Incoming webhooks | +| `communication.monitoring.view` | Stats and metrics | + +## Extension Points + +| Extension point | How to extend | Forbidden uses | +| --- | --- | --- | +| ChannelProvider | Register new adapter in provider registry | Provider code in business modules | +| Contact source types | Add resolver for new remote source | Copy foreign-module rows into communication_db | +| Template variables | Extend template schema + validators | Bypass approval for production sends | +| Queue worker | Optional Celery entry via `QueueEngine.process_due` | Blocking business transactions on send failure | + +## Known Limitations + +- **MVP scope:** SMS active (mock + Payamak); Email/Push/WhatsApp/Telegram/Rubika/Voice are stubs — see [communication-roadmap.md](../communication-roadmap.md) +- No dedicated Celery worker (optional ops; API path is correct) +- Publish-only event envelopes (no wired bus consumers yet) +- In-process rate limiter (per-instance) +- Sender unique constraint not soft-delete-aware +- Core auth OTP remains on Core (ADR-012 neutral note) + +## Migration Notes + +| Item | Detail | +| --- | --- | +| Alembic revision(s) | `0001_initial`, `0002_validation_hardening` | +| Upgrade steps | `alembic upgrade head` in communication service | +| Downgrade support | Yes | +| Data backfill | None | +| Breaking changes | None (additive validation hardening) | + +## Dependencies + +| Dependency | Type | Required for | +| --- | --- | --- | +| onboarding-4 | Phase | Core platform baseline | +| shared-lib | Library | Events, security, exceptions | +| Payamak / mock SMS | External provider | SMS delivery | + +## Next Phase Entry + +Communication is **feature-complete for the current MVP scope (SMS only)**. No registered Communication phases remain. + +| Field | Value | +| --- | --- | +| Recommended next phase | None (MVP complete) | +| Blockers for future channels | Explicit product decision + new phase registration when scoped | +| Entry checklist | Read this handover + [service-snapshots/communication.yaml](../service-snapshots/communication.yaml); future channels via [communication-roadmap.md](../communication-roadmap.md) only | + +## Completion Sign-Off + +- [x] Quality gates passed (see [communication-phase-8-audit.md](../communication-phase-8-audit.md)) +- [x] Tests green (42) +- [x] Documentation updated +- [x] Service snapshot generated +- [x] Progress / registries / manifests updated +- [x] Production Ready (SMS) declared +- [x] No TODO for claimed deliverables + +## Related Documents + +- [communication-phase-8.md](../communication-phase-8.md) +- [communication-phase-8-audit.md](../communication-phase-8-audit.md) +- [communication-roadmap.md](../communication-roadmap.md) +- [service-snapshots/communication.yaml](../service-snapshots/communication.yaml) +- [ADR-012](../architecture/adr/ADR-012.md) diff --git a/docs/service-snapshots/communication.yaml b/docs/service-snapshots/communication.yaml new file mode 100644 index 0000000..097c658 --- /dev/null +++ b/docs/service-snapshots/communication.yaml @@ -0,0 +1,116 @@ +# Communication Platform service snapshot — MVP feature-complete (SMS). +# Spec: docs/ai-framework/service-snapshot-policy.md + +schema_version: 1 +snapshot_version: 1 + +service_name: communication +commercial_product: Torbat Communication +current_version: "0.8.10.1" +current_phase: communication-8 +last_completed_phase: communication-8 +next_phase: null + +completed_phases: + - communication-8 +remaining_phases: [] + +registered_modules: + - providers + - senders + - templates + - contacts + - contact_sources + - messages + - queue + - delivery_tracking + - otp + - webhooks + - monitoring + - audit + +enabled_capabilities: + - sms_messaging + - provider_registry + - communication_router + - template_engine + - dynamic_contact_sources + - queue_engine + - delivery_tracking + - otp_platform + - incoming_webhooks + - monitoring + - production_ready_sms_mvp + +enabled_bundles: + - torbat_communication + +public_apis: + - { method: GET, path: /health, permission: public } + - { method: GET, path: /health/ready, permission: public } + - { method: GET, path: /capabilities, permission: public } + - { method: GET, path: /metrics, permission: public } + - { method: GET, path: /api/v1/providers, permission: communication.providers.view } + - { method: POST, path: /api/v1/providers, permission: communication.providers.manage } + - { method: GET, path: /api/v1/providers/status, permission: communication.providers.view } + - { method: POST, path: /api/v1/providers/senders, permission: communication.senders.manage } + - { method: GET, path: /api/v1/templates, permission: communication.templates.view } + - { method: POST, path: /api/v1/templates, permission: communication.templates.manage } + - { method: POST, path: "/api/v1/templates/{id}/approve", permission: communication.templates.approve } + - { method: GET, path: /api/v1/contacts/manual, permission: communication.contacts.view } + - { method: POST, path: /api/v1/contacts/sources, permission: communication.sources.manage } + - { method: POST, path: /api/v1/contacts/resolve, permission: communication.contacts.view } + - { method: POST, path: /api/v1/messages/send, permission: communication.messages.send } + - { method: GET, path: /api/v1/messages, permission: communication.messages.view } + - { method: POST, path: "/api/v1/messages/{id}/cancel", permission: communication.messages.cancel } + - { method: POST, path: /api/v1/messages/queue/process, permission: communication.queue.manage } + - { method: GET, path: /api/v1/messages/queue/dead-letters, permission: communication.queue.view } + - { method: POST, path: /api/v1/otp/request, permission: communication.otp.request } + - { method: POST, path: /api/v1/otp/verify, permission: communication.otp.verify } + - { method: POST, path: /api/v1/webhooks/incoming, permission: communication.webhooks.manage } + - { method: GET, path: /api/v1/monitoring/stats, permission: communication.monitoring.view } + - { method: GET, path: /api/v1/monitoring/metrics, permission: communication.monitoring.view } + +published_events: + - { event_type: communication.message.queued, version: "1" } + - { event_type: communication.message.sent, version: "1" } + - { event_type: communication.message.delivered, version: "1" } + - { event_type: communication.message.failed, version: "1" } + - { event_type: communication.message.cancelled, version: "1" } + - { event_type: communication.message.expired, version: "1" } + - { event_type: communication.provider.failover, version: "1" } + - { event_type: communication.provider.circuit_opened, version: "1" } + - { event_type: communication.provider.circuit_closed, version: "1" } + - { event_type: communication.template.approved, version: "1" } + - { event_type: communication.template.rejected, version: "1" } + - { event_type: communication.otp.generated, version: "1" } + - { event_type: communication.otp.verified, version: "1" } + - { event_type: communication.otp.failed, version: "1" } + - { event_type: communication.queue.dead_letter, version: "1" } + - { event_type: communication.webhook.received, version: "1" } + +permission_prefix: communication.* + +active_adrs: + - ADR-001 + - ADR-003 + - ADR-006 + - ADR-012 + +integration_contracts: + - ChannelProvider (in-service protocol) + - CRM / Loyalty / Sports / Restaurant / Marketplace / Accounting contact resolve (HTTP only) + - Core auth OTP remains on Core (no shared tables) + +known_limitations: + - MVP scope is SMS-only; Email/Push/WhatsApp/Telegram/Rubika/Voice are architecture-ready stubs + - Continuous Celery worker drain is optional ops work (API/process_immediately covers correctness) + - Real message bus consumers not wired (publish-only envelopes) + - Sender unique constraint is not soft-delete-aware + - In-process rate limiter is per-instance (acceptable for MVP) + - Future channels documented in communication-roadmap.md only — no registered 9.x phases + +open_todos: [] + +last_handover_reference: docs/phase-handover/phase-8.md +last_updated: "2026-07-26" diff --git a/frontend/app/api/communication/[...path]/route.ts b/frontend/app/api/communication/[...path]/route.ts new file mode 100644 index 0000000..5caa5b2 --- /dev/null +++ b/frontend/app/api/communication/[...path]/route.ts @@ -0,0 +1,53 @@ +import { NextRequest, NextResponse } from "next/server"; + +const UPSTREAM = + process.env.COMMUNICATION_SERVICE_URL || + process.env.NEXT_PUBLIC_COMMUNICATION_API_URL || + "http://localhost:8005"; + +async function proxy(req: NextRequest, pathSegments: string[]) { + const p = pathSegments.join("/"); + const url = new URL(req.url); + const target = `${UPSTREAM.replace(/\/$/, "")}/${p}${url.search}`; + const headers = new Headers(); + for (const h of ["authorization", "content-type", "x-tenant-id", "accept"]) { + const v = req.headers.get(h); + if (v) headers.set(h, v); + } + let body: ArrayBuffer | undefined; + if (req.method !== "GET" && req.method !== "HEAD") body = await req.arrayBuffer(); + try { + const res = await fetch(target, { method: req.method, headers, body, cache: "no-store" }); + const out = new Headers(); + const ct = res.headers.get("content-type"); + if (ct) out.set("content-type", ct); + return new NextResponse(await res.arrayBuffer(), { status: res.status, headers: out }); + } catch (err) { + const detail = err instanceof Error ? err.message : "upstream_unreachable"; + return NextResponse.json( + { error: { code: "communication_proxy_error", message: `پروکسی تربت ارتباط — ${detail}` } }, + { status: 502 } + ); + } +} + +export async function GET(req: NextRequest, ctx: { params: Promise<{ path: string[] }> }) { + const { path: p } = await ctx.params; + return proxy(req, p); +} +export async function POST(req: NextRequest, ctx: { params: Promise<{ path: string[] }> }) { + const { path: p } = await ctx.params; + return proxy(req, p); +} +export async function PATCH(req: NextRequest, ctx: { params: Promise<{ path: string[] }> }) { + const { path: p } = await ctx.params; + return proxy(req, p); +} +export async function PUT(req: NextRequest, ctx: { params: Promise<{ path: string[] }> }) { + const { path: p } = await ctx.params; + return proxy(req, p); +} +export async function DELETE(req: NextRequest, ctx: { params: Promise<{ path: string[] }> }) { + const { path: p } = await ctx.params; + return proxy(req, p); +} diff --git a/frontend/app/communication/ai-campaigns/error.tsx b/frontend/app/communication/ai-campaigns/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/ai-campaigns/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/ai-campaigns/loading.tsx b/frontend/app/communication/ai-campaigns/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/ai-campaigns/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/ai-campaigns/page.tsx b/frontend/app/communication/ai-campaigns/page.tsx new file mode 100644 index 0000000..30800d5 --- /dev/null +++ b/frontend/app/communication/ai-campaigns/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { AiCampaignsPage as default } from "@/modules/communication/features/aiCampaigns"; diff --git a/frontend/app/communication/analytics/error.tsx b/frontend/app/communication/analytics/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/analytics/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/analytics/loading.tsx b/frontend/app/communication/analytics/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/analytics/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/analytics/page.tsx b/frontend/app/communication/analytics/page.tsx new file mode 100644 index 0000000..5d86406 --- /dev/null +++ b/frontend/app/communication/analytics/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { AnalyticsPage as default } from "@/modules/communication/features/monitoring"; diff --git a/frontend/app/communication/api-keys/error.tsx b/frontend/app/communication/api-keys/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/api-keys/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/api-keys/loading.tsx b/frontend/app/communication/api-keys/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/api-keys/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/api-keys/page.tsx b/frontend/app/communication/api-keys/page.tsx new file mode 100644 index 0000000..9e15257 --- /dev/null +++ b/frontend/app/communication/api-keys/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { ApiKeysPage as default } from "@/modules/communication/features/apiKeys"; diff --git a/frontend/app/communication/audit/error.tsx b/frontend/app/communication/audit/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/audit/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/audit/loading.tsx b/frontend/app/communication/audit/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/audit/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/audit/page.tsx b/frontend/app/communication/audit/page.tsx new file mode 100644 index 0000000..b806c03 --- /dev/null +++ b/frontend/app/communication/audit/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { AuditPage as default } from "@/modules/communication/features/audit"; diff --git a/frontend/app/communication/automation/error.tsx b/frontend/app/communication/automation/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/automation/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/automation/loading.tsx b/frontend/app/communication/automation/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/automation/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/automation/page.tsx b/frontend/app/communication/automation/page.tsx new file mode 100644 index 0000000..1422638 --- /dev/null +++ b/frontend/app/communication/automation/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { MarketingAutomationPage as default } from "@/modules/communication/features/marketingAutomation"; diff --git a/frontend/app/communication/automation/triggers/error.tsx b/frontend/app/communication/automation/triggers/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/automation/triggers/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/automation/triggers/loading.tsx b/frontend/app/communication/automation/triggers/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/automation/triggers/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/automation/triggers/page.tsx b/frontend/app/communication/automation/triggers/page.tsx new file mode 100644 index 0000000..10a86fb --- /dev/null +++ b/frontend/app/communication/automation/triggers/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { AutomationTriggersPage as default } from "@/modules/communication/features/automationTriggers"; diff --git a/frontend/app/communication/campaign-orchestrator/error.tsx b/frontend/app/communication/campaign-orchestrator/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/campaign-orchestrator/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/campaign-orchestrator/loading.tsx b/frontend/app/communication/campaign-orchestrator/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/campaign-orchestrator/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/campaign-orchestrator/page.tsx b/frontend/app/communication/campaign-orchestrator/page.tsx new file mode 100644 index 0000000..5e2b049 --- /dev/null +++ b/frontend/app/communication/campaign-orchestrator/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { CampaignOrchestratorPage as default } from "@/modules/communication/features/campaignOrchestrator"; diff --git a/frontend/app/communication/capabilities/error.tsx b/frontend/app/communication/capabilities/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/capabilities/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/capabilities/loading.tsx b/frontend/app/communication/capabilities/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/capabilities/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/capabilities/page.tsx b/frontend/app/communication/capabilities/page.tsx new file mode 100644 index 0000000..581925e --- /dev/null +++ b/frontend/app/communication/capabilities/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { CapabilitiesPage as default } from "@/modules/communication/features/capabilities"; diff --git a/frontend/app/communication/connectors/error.tsx b/frontend/app/communication/connectors/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/connectors/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/connectors/loading.tsx b/frontend/app/communication/connectors/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/connectors/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/connectors/page.tsx b/frontend/app/communication/connectors/page.tsx new file mode 100644 index 0000000..7aee833 --- /dev/null +++ b/frontend/app/communication/connectors/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { ConnectorsPage as default } from "@/modules/communication/features/connectors"; diff --git a/frontend/app/communication/contacts/address-books/error.tsx b/frontend/app/communication/contacts/address-books/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/contacts/address-books/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/contacts/address-books/loading.tsx b/frontend/app/communication/contacts/address-books/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/contacts/address-books/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/contacts/address-books/page.tsx b/frontend/app/communication/contacts/address-books/page.tsx new file mode 100644 index 0000000..ed1ed57 --- /dev/null +++ b/frontend/app/communication/contacts/address-books/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { AddressBooksPage as default } from "@/modules/communication/features/recipients"; diff --git a/frontend/app/communication/contacts/recipients/error.tsx b/frontend/app/communication/contacts/recipients/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/contacts/recipients/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/contacts/recipients/loading.tsx b/frontend/app/communication/contacts/recipients/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/contacts/recipients/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/contacts/recipients/page.tsx b/frontend/app/communication/contacts/recipients/page.tsx new file mode 100644 index 0000000..291ecd3 --- /dev/null +++ b/frontend/app/communication/contacts/recipients/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { RecipientsPage as default } from "@/modules/communication/features/recipients"; diff --git a/frontend/app/communication/contacts/sources/error.tsx b/frontend/app/communication/contacts/sources/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/contacts/sources/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/contacts/sources/loading.tsx b/frontend/app/communication/contacts/sources/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/contacts/sources/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/contacts/sources/page.tsx b/frontend/app/communication/contacts/sources/page.tsx new file mode 100644 index 0000000..d0acad2 --- /dev/null +++ b/frontend/app/communication/contacts/sources/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { ContactSourcesPage as default } from "@/modules/communication/features/contactSources"; diff --git a/frontend/app/communication/conversations/error.tsx b/frontend/app/communication/conversations/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/conversations/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/conversations/loading.tsx b/frontend/app/communication/conversations/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/conversations/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/conversations/page.tsx b/frontend/app/communication/conversations/page.tsx new file mode 100644 index 0000000..0e79b62 --- /dev/null +++ b/frontend/app/communication/conversations/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { ConversationsPage as default } from "@/modules/communication/features/conversations"; diff --git a/frontend/app/communication/dashboard/error.tsx b/frontend/app/communication/dashboard/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/dashboard/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/dashboard/loading.tsx b/frontend/app/communication/dashboard/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/dashboard/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/dashboard/page.tsx b/frontend/app/communication/dashboard/page.tsx new file mode 100644 index 0000000..af6eba5 --- /dev/null +++ b/frontend/app/communication/dashboard/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { CommunicationDashboard as default } from "@/modules/communication/features/dashboard"; diff --git a/frontend/app/communication/email/error.tsx b/frontend/app/communication/email/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/email/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/email/loading.tsx b/frontend/app/communication/email/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/email/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/email/page.tsx b/frontend/app/communication/email/page.tsx new file mode 100644 index 0000000..575137b --- /dev/null +++ b/frontend/app/communication/email/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { EmailPage as default } from "@/modules/communication/features/email"; diff --git a/frontend/app/communication/error.tsx b/frontend/app/communication/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/health/error.tsx b/frontend/app/communication/health/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/health/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/health/loading.tsx b/frontend/app/communication/health/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/health/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/health/page.tsx b/frontend/app/communication/health/page.tsx new file mode 100644 index 0000000..7cf4c65 --- /dev/null +++ b/frontend/app/communication/health/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { HealthPage as default } from "@/modules/communication/features/health"; diff --git a/frontend/app/communication/inbox/error.tsx b/frontend/app/communication/inbox/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/inbox/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/inbox/loading.tsx b/frontend/app/communication/inbox/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/inbox/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/inbox/page.tsx b/frontend/app/communication/inbox/page.tsx new file mode 100644 index 0000000..ef14fbc --- /dev/null +++ b/frontend/app/communication/inbox/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { OmnichannelInboxPage as default } from "@/modules/communication/features/omnichannelInbox"; diff --git a/frontend/app/communication/integrations/error.tsx b/frontend/app/communication/integrations/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/integrations/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/integrations/loading.tsx b/frontend/app/communication/integrations/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/integrations/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/integrations/page.tsx b/frontend/app/communication/integrations/page.tsx new file mode 100644 index 0000000..19a32fb --- /dev/null +++ b/frontend/app/communication/integrations/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { IntegrationsHubPage as default } from "@/modules/communication/features/integrationsHub"; diff --git a/frontend/app/communication/journey-builder/error.tsx b/frontend/app/communication/journey-builder/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/journey-builder/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/journey-builder/loading.tsx b/frontend/app/communication/journey-builder/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/journey-builder/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/journey-builder/page.tsx b/frontend/app/communication/journey-builder/page.tsx new file mode 100644 index 0000000..1906810 --- /dev/null +++ b/frontend/app/communication/journey-builder/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { JourneyBuilderPage as default } from "@/modules/communication/features/journeyBuilder"; diff --git a/frontend/app/communication/layout.tsx b/frontend/app/communication/layout.tsx new file mode 100644 index 0000000..5c8bdf1 --- /dev/null +++ b/frontend/app/communication/layout.tsx @@ -0,0 +1,3 @@ +"use client"; +import { CommunicationRootLayout } from "@/modules/communication/components/createPortalLayout"; +export default CommunicationRootLayout; diff --git a/frontend/app/communication/live-chat/error.tsx b/frontend/app/communication/live-chat/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/live-chat/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/live-chat/loading.tsx b/frontend/app/communication/live-chat/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/live-chat/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/live-chat/page.tsx b/frontend/app/communication/live-chat/page.tsx new file mode 100644 index 0000000..64aa73a --- /dev/null +++ b/frontend/app/communication/live-chat/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { LiveChatPage as default } from "@/modules/communication/features/liveChat"; diff --git a/frontend/app/communication/loading.tsx b/frontend/app/communication/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/monitoring/error.tsx b/frontend/app/communication/monitoring/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/monitoring/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/monitoring/loading.tsx b/frontend/app/communication/monitoring/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/monitoring/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/monitoring/page.tsx b/frontend/app/communication/monitoring/page.tsx new file mode 100644 index 0000000..c1d067f --- /dev/null +++ b/frontend/app/communication/monitoring/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { MonitoringPage as default } from "@/modules/communication/features/monitoring"; diff --git a/frontend/app/communication/not-found.tsx b/frontend/app/communication/not-found.tsx new file mode 100644 index 0000000..b0d2d54 --- /dev/null +++ b/frontend/app/communication/not-found.tsx @@ -0,0 +1,7 @@ +import Link from "next/link"; +import { EmptyState, Button } from "@/components/ds"; +export default function NotFound() { + return ( + } /> + ); +} diff --git a/frontend/app/communication/notifications/error.tsx b/frontend/app/communication/notifications/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/notifications/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/notifications/loading.tsx b/frontend/app/communication/notifications/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/notifications/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/notifications/page.tsx b/frontend/app/communication/notifications/page.tsx new file mode 100644 index 0000000..6b57df9 --- /dev/null +++ b/frontend/app/communication/notifications/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { NotificationCenterPage as default } from "@/modules/communication/features/notificationCenter"; diff --git a/frontend/app/communication/otp/error.tsx b/frontend/app/communication/otp/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/otp/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/otp/loading.tsx b/frontend/app/communication/otp/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/otp/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/otp/page.tsx b/frontend/app/communication/otp/page.tsx new file mode 100644 index 0000000..e0d59f9 --- /dev/null +++ b/frontend/app/communication/otp/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { OtpPage as default } from "@/modules/communication/features/otp"; diff --git a/frontend/app/communication/page.tsx b/frontend/app/communication/page.tsx new file mode 100644 index 0000000..3613bce --- /dev/null +++ b/frontend/app/communication/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { ExecutiveDashboard as default } from "@/modules/communication/features/executiveDashboard"; diff --git a/frontend/app/communication/permissions/error.tsx b/frontend/app/communication/permissions/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/permissions/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/permissions/loading.tsx b/frontend/app/communication/permissions/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/permissions/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/permissions/page.tsx b/frontend/app/communication/permissions/page.tsx new file mode 100644 index 0000000..5fee33f --- /dev/null +++ b/frontend/app/communication/permissions/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { PermissionsPage as default } from "@/modules/communication/features/permissions"; diff --git a/frontend/app/communication/providers/dashboard/error.tsx b/frontend/app/communication/providers/dashboard/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/providers/dashboard/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/providers/dashboard/loading.tsx b/frontend/app/communication/providers/dashboard/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/providers/dashboard/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/providers/dashboard/page.tsx b/frontend/app/communication/providers/dashboard/page.tsx new file mode 100644 index 0000000..bcf2b60 --- /dev/null +++ b/frontend/app/communication/providers/dashboard/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { ProviderDashboard as default } from "@/modules/communication/features/providerDashboard"; diff --git a/frontend/app/communication/push/error.tsx b/frontend/app/communication/push/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/push/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/push/loading.tsx b/frontend/app/communication/push/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/push/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/push/page.tsx b/frontend/app/communication/push/page.tsx new file mode 100644 index 0000000..0a04c4f --- /dev/null +++ b/frontend/app/communication/push/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { PushPage as default } from "@/modules/communication/features/push"; diff --git a/frontend/app/communication/reports/error.tsx b/frontend/app/communication/reports/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/reports/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/reports/loading.tsx b/frontend/app/communication/reports/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/reports/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/reports/page.tsx b/frontend/app/communication/reports/page.tsx new file mode 100644 index 0000000..641f926 --- /dev/null +++ b/frontend/app/communication/reports/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { ReportsPage as default } from "@/modules/communication/features/monitoring"; diff --git a/frontend/app/communication/roles/error.tsx b/frontend/app/communication/roles/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/roles/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/roles/loading.tsx b/frontend/app/communication/roles/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/roles/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/roles/page.tsx b/frontend/app/communication/roles/page.tsx new file mode 100644 index 0000000..3e55ece --- /dev/null +++ b/frontend/app/communication/roles/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { RolesPage as default } from "@/modules/communication/features/roles"; diff --git a/frontend/app/communication/rubika/error.tsx b/frontend/app/communication/rubika/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/rubika/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/rubika/loading.tsx b/frontend/app/communication/rubika/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/rubika/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/rubika/page.tsx b/frontend/app/communication/rubika/page.tsx new file mode 100644 index 0000000..2cfaca3 --- /dev/null +++ b/frontend/app/communication/rubika/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { RubikaPage as default } from "@/modules/communication/features/rubika"; diff --git a/frontend/app/communication/settings/error.tsx b/frontend/app/communication/settings/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/settings/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/settings/loading.tsx b/frontend/app/communication/settings/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/settings/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/settings/page.tsx b/frontend/app/communication/settings/page.tsx new file mode 100644 index 0000000..9d720f8 --- /dev/null +++ b/frontend/app/communication/settings/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { SettingsPage as default } from "@/modules/communication/features/settings"; diff --git a/frontend/app/communication/sms/campaigns/error.tsx b/frontend/app/communication/sms/campaigns/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/sms/campaigns/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/sms/campaigns/loading.tsx b/frontend/app/communication/sms/campaigns/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/sms/campaigns/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/sms/campaigns/page.tsx b/frontend/app/communication/sms/campaigns/page.tsx new file mode 100644 index 0000000..db30939 --- /dev/null +++ b/frontend/app/communication/sms/campaigns/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { CampaignsPage as default } from "@/modules/communication/features/campaigns"; diff --git a/frontend/app/communication/sms/delivery-reports/error.tsx b/frontend/app/communication/sms/delivery-reports/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/sms/delivery-reports/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/sms/delivery-reports/loading.tsx b/frontend/app/communication/sms/delivery-reports/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/sms/delivery-reports/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/sms/delivery-reports/page.tsx b/frontend/app/communication/sms/delivery-reports/page.tsx new file mode 100644 index 0000000..9dcf50d --- /dev/null +++ b/frontend/app/communication/sms/delivery-reports/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { DeliveryReportsPage as default } from "@/modules/communication/features/deliveryReports"; diff --git a/frontend/app/communication/sms/error.tsx b/frontend/app/communication/sms/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/sms/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/sms/loading.tsx b/frontend/app/communication/sms/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/sms/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/sms/messages/error.tsx b/frontend/app/communication/sms/messages/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/sms/messages/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/sms/messages/loading.tsx b/frontend/app/communication/sms/messages/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/sms/messages/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/sms/messages/page.tsx b/frontend/app/communication/sms/messages/page.tsx new file mode 100644 index 0000000..c42a41f --- /dev/null +++ b/frontend/app/communication/sms/messages/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { MessagesPage as default } from "@/modules/communication/features/messages"; diff --git a/frontend/app/communication/sms/page.tsx b/frontend/app/communication/sms/page.tsx new file mode 100644 index 0000000..3e571e2 --- /dev/null +++ b/frontend/app/communication/sms/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { SmsDashboard as default } from "@/modules/communication/features/smsDashboard"; diff --git a/frontend/app/communication/sms/providers/error.tsx b/frontend/app/communication/sms/providers/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/sms/providers/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/sms/providers/loading.tsx b/frontend/app/communication/sms/providers/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/sms/providers/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/sms/providers/page.tsx b/frontend/app/communication/sms/providers/page.tsx new file mode 100644 index 0000000..523c05e --- /dev/null +++ b/frontend/app/communication/sms/providers/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { ProvidersPage as default } from "@/modules/communication/features/providers"; diff --git a/frontend/app/communication/sms/queue/error.tsx b/frontend/app/communication/sms/queue/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/sms/queue/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/sms/queue/loading.tsx b/frontend/app/communication/sms/queue/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/sms/queue/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/sms/queue/page.tsx b/frontend/app/communication/sms/queue/page.tsx new file mode 100644 index 0000000..4a2214e --- /dev/null +++ b/frontend/app/communication/sms/queue/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { QueuePage as default } from "@/modules/communication/features/queue"; diff --git a/frontend/app/communication/sms/send/error.tsx b/frontend/app/communication/sms/send/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/sms/send/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/sms/send/loading.tsx b/frontend/app/communication/sms/send/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/sms/send/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/sms/send/page.tsx b/frontend/app/communication/sms/send/page.tsx new file mode 100644 index 0000000..f23ed60 --- /dev/null +++ b/frontend/app/communication/sms/send/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { SendMessagePage as default } from "@/modules/communication/features/sendMessage"; diff --git a/frontend/app/communication/sms/senders/error.tsx b/frontend/app/communication/sms/senders/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/sms/senders/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/sms/senders/loading.tsx b/frontend/app/communication/sms/senders/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/sms/senders/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/sms/senders/page.tsx b/frontend/app/communication/sms/senders/page.tsx new file mode 100644 index 0000000..b2af081 --- /dev/null +++ b/frontend/app/communication/sms/senders/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { SendersPage as default } from "@/modules/communication/features/senders"; diff --git a/frontend/app/communication/sms/template-categories/error.tsx b/frontend/app/communication/sms/template-categories/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/sms/template-categories/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/sms/template-categories/loading.tsx b/frontend/app/communication/sms/template-categories/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/sms/template-categories/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/sms/template-categories/page.tsx b/frontend/app/communication/sms/template-categories/page.tsx new file mode 100644 index 0000000..74d7c75 --- /dev/null +++ b/frontend/app/communication/sms/template-categories/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { TemplateCategoriesPage as default } from "@/modules/communication/features/templateCategories"; diff --git a/frontend/app/communication/sms/templates/error.tsx b/frontend/app/communication/sms/templates/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/sms/templates/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/sms/templates/loading.tsx b/frontend/app/communication/sms/templates/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/sms/templates/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/sms/templates/page.tsx b/frontend/app/communication/sms/templates/page.tsx new file mode 100644 index 0000000..0e9089b --- /dev/null +++ b/frontend/app/communication/sms/templates/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { TemplatesPage as default } from "@/modules/communication/features/templates"; diff --git a/frontend/app/communication/telegram/error.tsx b/frontend/app/communication/telegram/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/telegram/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/telegram/loading.tsx b/frontend/app/communication/telegram/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/telegram/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/telegram/page.tsx b/frontend/app/communication/telegram/page.tsx new file mode 100644 index 0000000..b59c31a --- /dev/null +++ b/frontend/app/communication/telegram/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { TelegramPage as default } from "@/modules/communication/features/telegram"; diff --git a/frontend/app/communication/video/error.tsx b/frontend/app/communication/video/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/video/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/video/loading.tsx b/frontend/app/communication/video/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/video/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/video/page.tsx b/frontend/app/communication/video/page.tsx new file mode 100644 index 0000000..f1a3b4f --- /dev/null +++ b/frontend/app/communication/video/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { VideoPage as default } from "@/modules/communication/features/video"; diff --git a/frontend/app/communication/voice/error.tsx b/frontend/app/communication/voice/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/voice/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/voice/loading.tsx b/frontend/app/communication/voice/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/voice/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/voice/page.tsx b/frontend/app/communication/voice/page.tsx new file mode 100644 index 0000000..2a2c594 --- /dev/null +++ b/frontend/app/communication/voice/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { VoicePage as default } from "@/modules/communication/features/voice"; diff --git a/frontend/app/communication/webhooks/error.tsx b/frontend/app/communication/webhooks/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/webhooks/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/webhooks/loading.tsx b/frontend/app/communication/webhooks/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/webhooks/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/webhooks/page.tsx b/frontend/app/communication/webhooks/page.tsx new file mode 100644 index 0000000..473d707 --- /dev/null +++ b/frontend/app/communication/webhooks/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { WebhookCenterPage as default } from "@/modules/communication/features/webhookCenter"; diff --git a/frontend/app/communication/whatsapp/error.tsx b/frontend/app/communication/whatsapp/error.tsx new file mode 100644 index 0000000..8f69ee5 --- /dev/null +++ b/frontend/app/communication/whatsapp/error.tsx @@ -0,0 +1,5 @@ +"use client"; +import { ErrorState } from "@/components/ds"; +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ; +} diff --git a/frontend/app/communication/whatsapp/loading.tsx b/frontend/app/communication/whatsapp/loading.tsx new file mode 100644 index 0000000..c17a94d --- /dev/null +++ b/frontend/app/communication/whatsapp/loading.tsx @@ -0,0 +1,2 @@ +import { LoadingState } from "@/components/ds"; +export default function Loading() { return ; } diff --git a/frontend/app/communication/whatsapp/page.tsx b/frontend/app/communication/whatsapp/page.tsx new file mode 100644 index 0000000..3289c5b --- /dev/null +++ b/frontend/app/communication/whatsapp/page.tsx @@ -0,0 +1,2 @@ +"use client"; +export { WhatsappPage as default } from "@/modules/communication/features/whatsapp"; diff --git a/frontend/docs/communication-component-map.md b/frontend/docs/communication-component-map.md new file mode 100644 index 0000000..f697d22 --- /dev/null +++ b/frontend/docs/communication-component-map.md @@ -0,0 +1,50 @@ +# Communication — Component Map + +## Shell & layout + +| Component | Path | Role | +| --- | --- | --- | +| CommunicationRootLayout | `components/createPortalLayout.tsx` | Auth + tenant gate | +| CommunicationPortalShell | `components/CommunicationPortalShell.tsx` | Sidebar nav, dark mode, lock icons | +| CommunicationBreadcrumbs | `components/CommunicationBreadcrumbs.tsx` | RTL breadcrumbs | +| CommunicationFeatureLock | `components/CommunicationFeatureLock.tsx` | Locked module premium UI | + +## Data & CRUD + +| Component | Path | Role | +| --- | --- | --- | +| createCommunicationListPage | `components/CommunicationListCrudPage.tsx` | CRUD factory (providers, templates, contacts) | +| CommunicationTablePage | `design-system/CommunicationTablePage.tsx` | Search, pagination, toolbar | +| CommunicationStatGrid | `design-system/CommunicationStatGrid.tsx` | KPI cards | +| CommunicationStatusChip | `design-system/CommunicationStatusChip.tsx` | Message/provider status | + +## Shared (reused) + +| Source | Used for | +| --- | --- | +| `@/components/ds` | Button, Card, Dialog, Drawer, FormField, StatCard, DataTable | +| `@/src/shared/ui` | PageLoader, PermissionDeniedState, Timeline, DetailDrawer | +| `@/src/shared/hooks` | useDebouncedValue (available) | + +## Hooks + +| Hook | Path | +| --- | --- | +| useCommunicationCapabilities | `hooks/useCommunicationCapabilities.ts` | +| useCommunicationPermissions | `hooks/useCommunicationPermissions.ts` | + +## API + +| Module | Path | +| --- | --- | +| communicationApi | `services/communication-api.ts` | +| normalizeCapabilities | same | + +## Constants + +| File | Contents | +| --- | --- | +| portals.ts | Full nav tree (locked items visible) | +| featureRegistry.ts | FUTURE_MODULE_REGISTRY | +| resourceRegistry.ts | SMS CRUD configs | +| permissions.ts | communication.* tree | diff --git a/frontend/docs/communication-design-system.md b/frontend/docs/communication-design-system.md new file mode 100644 index 0000000..50374e9 --- /dev/null +++ b/frontend/docs/communication-design-system.md @@ -0,0 +1,35 @@ +# Communication — Design System + +## Tokens + +Defined in `frontend/modules/communication/design-system/tokens.ts` and CSS variables in `frontend/styles/globals.css`: + +| Token | Light | Purpose | +| --- | --- | --- | +| `--communication-accent` | `#6366f1` | Primary indigo | +| `--communication-accent-soft` | rgba indigo 12% | Backgrounds | +| `--communication-success` | `#059669` | Delivered / healthy | +| `--communication-warning` | `#d97706` | Queued / deferred | +| `--communication-danger` | `#dc2626` | Failed / circuit open | + +Dark mode variants in `.dark` block. + +## Principles + +- **RTL first** — `dir="rtl"` on portal shell +- **Mobile first** — collapsible sidebar, responsive grids +- **Dark mode ready** — ColorModeProvider toggle in header +- **Accessible** — aria labels on nav, semantic tables +- **Omnichannel ready** — channel badges from `/capabilities` + +## Reuse policy + +Use `frontend/shared/` and `@/src/shared/ui` — do not fork Button/Input/Dialog. Module-specific styling via `--communication-*` tokens only. + +## Status vocabulary + +`messageStatusLabels` / `messageStatusTone` in tokens.ts map backend statuses (queued, sent, delivered, failed, …) to Badge tones. + +## Locked module UI + +Feature lock pages use gradient hero, capability checklist, integration badges, and placeholder preview panel — consistent premium finish without functional actions. diff --git a/frontend/docs/communication-feature-lock-report.md b/frontend/docs/communication-feature-lock-report.md new file mode 100644 index 0000000..8534f03 --- /dev/null +++ b/frontend/docs/communication-feature-lock-report.md @@ -0,0 +1,48 @@ +# Communication — Feature Lock Report + +**Policy:** Future modules remain visible in navigation with lock icon. Pages render full premium UI via `CommunicationFeatureLock`. No unavailable APIs are called. + +## Auto-unlock rule + +When backend `/capabilities` includes `features[channel_] === true` or a dedicated feature key, `CommunicationFeatureLock` renders children instead of lock screen. + +## Locked modules (19) + +| Module | Feature key | Route | Bundle label | +| --- | --- | --- | --- | +| Email Platform | `channel_email` | `/communication/email` | Communication Pro | +| WhatsApp Business | `channel_whatsapp` | `/communication/whatsapp` | Omnichannel Pack | +| Telegram | `channel_telegram` | `/communication/telegram` | Omnichannel Pack | +| Rubika | `channel_rubika` | `/communication/rubika` | Regional Pack | +| Push Notifications | `channel_push` | `/communication/push` | Mobile Pack | +| Voice Calls | `channel_voice` | `/communication/voice` | Voice Pack | +| Video Calls | `channel_video` | `/communication/video` | Enterprise | +| Live Chat | `live_chat` | `/communication/live-chat` | Support Pack | +| Omnichannel Inbox | `omnichannel_inbox` | `/communication/inbox` | Omnichannel Pack | +| Marketing Automation | `marketing_automation` | `/communication/automation` | Marketing Pro | +| Automation Triggers | `automation_triggers` | `/communication/automation/triggers` | Automation | +| Journey Builder | `journey_builder` | `/communication/journey-builder` | Marketing Pro | +| AI Campaigns | `ai_campaigns` | `/communication/ai-campaigns` | AI Pack | +| Webhook Center | `webhook_center` | `/communication/webhooks` | Developer | +| Third-party Connectors | `third_party_connectors` | `/communication/connectors` | Integrations | +| Conversation Timeline | `customer_conversation_timeline` | `/communication/conversations` | CRM Plus | +| Campaign Orchestrator | `campaign_orchestrator` | `/communication/campaign-orchestrator` | Marketing Pro | +| Notification Center | `notification_center` | `/communication/notifications` | Platform | +| Template Categories | `template_categories` | `/communication/sms/template-categories` | SMS Pro | + +## Admin locked (no list API yet) + +| Module | Route | +| --- | --- | +| Roles | `/communication/roles` | +| Audit Logs UI | `/communication/audit` | +| API Keys | `/communication/api-keys` | +| Integrations Hub | `/communication/integrations` | + +## Active (unlocked) + +SMS platform features when `features.sms === true` (default from backend capabilities). + +## Navigation behavior + +`filterNavForDisplay` keeps all items visible; locked items show `Lock` icon in sidebar. Clicking navigates to full lock page — not hidden. diff --git a/frontend/docs/communication-frontend-architecture.md b/frontend/docs/communication-frontend-architecture.md new file mode 100644 index 0000000..4e62876 --- /dev/null +++ b/frontend/docs/communication-frontend-architecture.md @@ -0,0 +1,43 @@ +# Communication Platform — Frontend Architecture + +**Product:** Torbat Communication +**Status:** SMS MVP Production Ready +**Module root:** `frontend/modules/communication/` +**Routes:** `frontend/app/communication/` (thin App Router only) + +## Layers + +``` +app/communication/ → page.tsx, layout.tsx, loading.tsx, error.tsx only +frontend/modules/communication/ + ├── components/ → Portal shell, FeatureLock, ListCrud factory + ├── constants/ → portals, featureRegistry, resourceRegistry, permissions + ├── design-system/ → Table, StatGrid, StatusChip, tokens + ├── features/ → Page implementations + ├── hooks/ → capabilities, permissions + ├── pages/ → shared loaders/errors + ├── services/ → communication-api.ts (BFF client) + └── types/ +frontend/app/api/communication/[...path]/route.ts → BFF → :8005 +``` + +## Dependency rules + +- Business code lives in `frontend/modules/communication/` only +- Reuse `@/src/shared/ui`, `@/components/ds` — never duplicate +- No imports from sibling business modules +- Locked future modules use `CommunicationFeatureLock` — no API calls + +## Capability model + +Backend `/capabilities` returns `features: string[]`. Normalized to `Record` in `useCommunicationCapabilities`. Future channel features stay `false` until backend enables them; UI auto-unlocks via gate. + +## SMS (active) + +Real backend integration for: providers, senders, templates, contacts, sources, messages, queue, OTP, monitoring, health, capabilities. + +## Related + +- [communication-routing-map.md](./communication-routing-map.md) +- [communication-component-map.md](./communication-component-map.md) +- [communication-feature-lock-report.md](./communication-feature-lock-report.md) diff --git a/frontend/docs/communication-routing-map.md b/frontend/docs/communication-routing-map.md new file mode 100644 index 0000000..1d8b7b8 --- /dev/null +++ b/frontend/docs/communication-routing-map.md @@ -0,0 +1,52 @@ +# Communication — Routing Map + +| Route | Feature file | Backend | Lock | +| --- | --- | --- | --- | +| `/communication` | executiveDashboard | stats APIs | Open | +| `/communication/dashboard` | dashboard | same | Open | +| `/communication/providers/dashboard` | providerDashboard | providers/status | SMS | +| `/communication/sms` | smsDashboard | monitoring | SMS | +| `/communication/sms/providers` | providers | GET/POST/PATCH/DELETE providers | SMS | +| `/communication/sms/templates` | templates | templates CRUD | SMS | +| `/communication/sms/senders` | senders | senders API | SMS | +| `/communication/sms/campaigns` | campaigns (send UI) | messages/send | SMS | +| `/communication/sms/send` | sendMessage | messages/send | SMS | +| `/communication/sms/queue` | queue | queue/process, DLQ | SMS | +| `/communication/sms/delivery-reports` | deliveryReports | messages + timeline | SMS | +| `/communication/sms/messages` | messages | messages list | SMS | +| `/communication/contacts/recipients` | recipients | contacts/manual | SMS | +| `/communication/contacts/sources` | contactSources | contacts/sources | SMS | +| `/communication/contacts/address-books` | recipients | same | SMS | +| `/communication/otp` | otp | otp request/verify | SMS | +| `/communication/monitoring` | monitoring | monitoring/stats | SMS | +| `/communication/analytics` | monitoring (metrics) | monitoring/metrics | SMS | +| `/communication/reports` | monitoring | stats | SMS | +| `/communication/health` | health | /health, /health/ready | Open | +| `/communication/capabilities` | capabilities | /capabilities | Open | +| `/communication/settings` | settings | providers list | SMS | +| `/communication/permissions` | permissions | static tree | Open | +| `/communication/email` | email | — | Locked | +| `/communication/whatsapp` | whatsapp | — | Locked | +| `/communication/telegram` | telegram | — | Locked | +| `/communication/rubika` | rubika | — | Locked | +| `/communication/push` | push | — | Locked | +| `/communication/voice` | voice | — | Locked | +| `/communication/video` | video | — | Locked | +| `/communication/live-chat` | liveChat | — | Locked | +| `/communication/inbox` | omnichannelInbox | — | Locked | +| `/communication/automation` | marketingAutomation | — | Locked | +| `/communication/automation/triggers` | automationTriggers | — | Locked | +| `/communication/journey-builder` | journeyBuilder | — | Locked | +| `/communication/ai-campaigns` | aiCampaigns | — | Locked | +| `/communication/webhooks` | webhookCenter | — | Locked | +| `/communication/connectors` | connectors | — | Locked | +| `/communication/conversations` | conversations | — | Locked | +| `/communication/campaign-orchestrator` | campaignOrchestrator | — | Locked | +| `/communication/notifications` | notificationCenter | — | Locked | +| `/communication/sms/template-categories` | templateCategories | — | Locked | +| `/communication/roles` | roles | — | Locked | +| `/communication/audit` | audit | — | Locked | +| `/communication/api-keys` | apiKeys | — | Locked | +| `/communication/integrations` | integrationsHub | — | Locked | + +**Total routes:** 46 diff --git a/frontend/docs/communication-validation-report.md b/frontend/docs/communication-validation-report.md new file mode 100644 index 0000000..560983c --- /dev/null +++ b/frontend/docs/communication-validation-report.md @@ -0,0 +1,54 @@ +# Communication Frontend — Validation Report + +**Date:** 2026-07-26 +**Scope:** Torbat Communication frontend module + +## Automated checks + +| Check | Result | Notes | +| --- | --- | --- | +| TypeScript (`npm run typecheck`) | **Pass** | Zero errors | +| Production build (`npm run build`) | **Blocked** | Webpack cache ENOENT on environment; clean rebuild recommended | +| Lint | Not run | — | + +## Architecture validation + +| Rule | Status | +| --- | --- | +| Business code in `frontend/modules/communication/` | Pass | +| App Router thin (page/layout/loading/error only) | Pass — 46 routes | +| BFF at `/api/communication/*` → :8005 | Pass | +| Shared DS reuse | Pass | +| No mock SMS data in active pages | Pass | + +## SMS integration validation + +| Screen | API connected | +| --- | --- | +| Providers CRUD | `/api/v1/providers` | +| Senders | `/api/v1/providers/senders` | +| Templates | `/api/v1/templates` | +| Recipients / address books | `/api/v1/contacts/manual` | +| Contact sources | `/api/v1/contacts/sources` | +| Send / campaigns | `/api/v1/messages/send` | +| Message history / delivery reports | `/api/v1/messages`, timeline | +| Queue / DLQ | queue process + dead-letters | +| OTP | `/api/v1/otp/*` | +| Monitoring / analytics / reports | `/api/v1/monitoring/*` | +| Health / capabilities | `/health`, `/capabilities` | + +## UX states + +| State | SMS pages | +| --- | --- | +| Loading | loading.tsx + PageLoader | +| Empty | EmptyState in tables | +| Error | error.tsx + PageError | +| Permission | PermissionDeniedState | +| Success | toast on mutations | + +## Follow-up + +1. Run `Remove-Item -Recurse .next; npm run build` locally +2. Add `validate:communication-routes` script (optional) +3. Wire home dashboard link to `/communication` diff --git a/frontend/modules/communication/components/CommunicationBreadcrumbs.tsx b/frontend/modules/communication/components/CommunicationBreadcrumbs.tsx new file mode 100644 index 0000000..1e21b29 --- /dev/null +++ b/frontend/modules/communication/components/CommunicationBreadcrumbs.tsx @@ -0,0 +1,30 @@ +"use client"; + +import Link from "next/link"; +import { ChevronLeft } from "lucide-react"; + +export function CommunicationBreadcrumbs({ + current, + parent, +}: { + current: string; + parent?: { href: string; label: string }; +}) { + return ( + + ); +} diff --git a/frontend/modules/communication/components/CommunicationFeatureLock.tsx b/frontend/modules/communication/components/CommunicationFeatureLock.tsx new file mode 100644 index 0000000..9f06385 --- /dev/null +++ b/frontend/modules/communication/components/CommunicationFeatureLock.tsx @@ -0,0 +1,140 @@ +"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 ; + if (caps.error) return caps.refetch()} />; + + const enabled = isFeatureEnabled(caps.data?.features, config.feature); + if (enabled && children) return <>{children}; + + return ( +
+ +
+ + +
+
+
+ +
+
+ + {config.bundle ? `نیازمند ${config.bundle}` : "به‌زودی"} + +

{config.title}

+
+
+

{config.description}

+ {config.phase ? ( +

+ + فاز برنامه‌ریزی: {config.phase} +

+ ) : null} +
+
+

+ + قابلیت‌های برنامه‌ریزی‌شده +

+
    + {config.capabilities.map((c) => ( +
  • + + {c} +
  • + ))} +
+
+
+
+ + + +
+
+
+ +
+

پیش‌نمایش رابط کاربری

+

+ این ماژول با طراحی production-ready آماده است و پس از فعال‌سازی backend به‌صورت خودکار + باز می‌شود. +

+
+
+ {config.integrations?.length ? ( +
+

+ + یکپارچه‌سازی‌ها +

+
+ {config.integrations.map((i) => ( + + {i} + + ))} +
+
+ ) : null} +

+ باز شدن خودکار: وقتی /capabilities{" "} + ویژگی {config.feature} را فعال کند. +

+
+ + + + + + +
+
+
+
+
+ ); +} + +export function createCommunicationLockedPage(config: FeatureLockConfig) { + return function CommunicationLockedPage() { + return ; + }; +} + +export function createCommunicationFeatureGate(config: FeatureLockConfig, Page: React.ComponentType) { + return function CommunicationGatedPage() { + return ( + + + + ); + }; +} diff --git a/frontend/modules/communication/components/CommunicationListCrudPage.tsx b/frontend/modules/communication/components/CommunicationListCrudPage.tsx new file mode 100644 index 0000000..118866c --- /dev/null +++ b/frontend/modules/communication/components/CommunicationListCrudPage.tsx @@ -0,0 +1,429 @@ +"use client"; + +import { useCallback, useState } from "react"; +import { useForm, type FieldValues, type DefaultValues } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { toast } from "sonner"; +import { Plus, Pencil, Trash2, Download, RefreshCw, Eye } from "lucide-react"; +import Link from "next/link"; +import { z } from "zod"; +import { + Button, + Dialog, + Drawer, + Input, + ConfirmDialog, + Select, + Textarea, + FormField, +} from "@/components/ds"; +import { CommunicationTablePage, CommunicationStatGrid } from "@/modules/communication/design-system"; +import { + CommunicationPageLoader, + CommunicationPageError, + exportToCsv, +} from "@/modules/communication/pages/shared"; +import { useTenantId } from "@/hooks/useTenantId"; +import { useCommunicationLookups } from "@/modules/communication/hooks/useCommunicationCapabilities"; +import { useCommunicationPermissions } from "@/modules/communication/hooks/useCommunicationPermissions"; +import { CommunicationStatusChip } from "@/modules/communication/design-system"; +import { PermissionDeniedState } from "@/src/shared/ui"; + +export type CommunicationFieldConfig = { + name: string; + label: string; + type?: "text" | "number" | "email" | "tel" | "textarea" | "select" | "organization" | "hub"; + options?: { value: string; label: string }[]; + required?: boolean; + createOnly?: boolean; + editOnly?: boolean; + placeholder?: string; +}; + +export type CommunicationColumnConfig = { + key: string; + header: string; + type?: "status" | "datetime" | "text" | "link"; + linkPrefix?: string; + render?: (row: Record) => React.ReactNode; +}; + +type ApiResource = { + list: ( + tenantId: string, + params?: Record + ) => Promise[] | { items: Record[]; total: number }>; + get?: (tenantId: string, id: string) => Promise>; + create?: (tenantId: string, body: Record) => Promise; + update?: (tenantId: string, id: string, body: Record) => Promise; + remove?: (tenantId: string, id: string) => Promise; +}; + +export type CommunicationListConfig = { + title: string; + description?: string; + breadcrumb?: string; + resourceKey: string; + permission?: string; + api: ApiResource; + columns: CommunicationColumnConfig[]; + createFields?: CommunicationFieldConfig[]; + editFields?: CommunicationFieldConfig[]; + readOnly?: boolean; + detailHref?: (row: Record) => string | null; + rowActions?: { + label: string; + onClick: (tenantId: string, row: Record) => Promise; + variant?: "default" | "outline" | "danger"; + }[]; +}; + +function buildSchema(fields: CommunicationFieldConfig[]) { + const shape: Record = {}; + for (const f of fields) { + shape[f.name] = f.required + ? z.string().min(1, `${f.label} الزامی است`) + : z.string().optional(); + } + return z.object(shape); +} + +function normalizeList( + data: Record[] | { items: Record[]; total: number } +): Record[] { + if (Array.isArray(data)) return data; + return data.items ?? []; +} + +function renderCell(col: CommunicationColumnConfig, row: Record) { + if (col.render) return col.render(row); + const val = row[col.key]; + if (col.type === "status" && val) return ; + if (col.type === "datetime" && val) { + try { + return new Date(String(val)).toLocaleString("fa-IR"); + } catch { + return String(val); + } + } + if (col.type === "link" && col.linkPrefix && row.id) { + return ( + + {val != null ? String(val) : "—"} + + ); + } + return val != null ? String(val) : "—"; +} + +function renderField( + field: CommunicationFieldConfig, + register: ReturnType["register"], + mode: "create" | "edit", + lookups: { organizations: Record[]; hubs: Record[] } +) { + if (mode === "create" && field.editOnly) return null; + if (mode === "edit" && field.createOnly) return null; + + if (field.type === "textarea") { + return ( + +