feat(communication): add SMS MVP frontend with governance artifacts.
Ship the full Communication portal (real SMS API, feature-locked future channels), BFF proxy, docs, snapshot, and phase handover. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
d579d0b142
commit
10c3c43a75
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
67
docs/communication-roadmap.md
Normal file
67
docs/communication-roadmap.md
Normal file
@ -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)
|
||||
155
docs/phase-handover/phase-8.md
Normal file
155
docs/phase-handover/phase-8.md
Normal file
@ -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)
|
||||
116
docs/service-snapshots/communication.yaml
Normal file
116
docs/service-snapshots/communication.yaml
Normal file
@ -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"
|
||||
53
frontend/app/api/communication/[...path]/route.ts
Normal file
53
frontend/app/api/communication/[...path]/route.ts
Normal file
@ -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);
|
||||
}
|
||||
5
frontend/app/communication/ai-campaigns/error.tsx
Normal file
5
frontend/app/communication/ai-campaigns/error.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
import { ErrorState } from "@/components/ds";
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
return <ErrorState message={error.message} onRetry={reset} />;
|
||||
}
|
||||
2
frontend/app/communication/ai-campaigns/loading.tsx
Normal file
2
frontend/app/communication/ai-campaigns/loading.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
import { LoadingState } from "@/components/ds";
|
||||
export default function Loading() { return <LoadingState label="بارگذاری…" />; }
|
||||
2
frontend/app/communication/ai-campaigns/page.tsx
Normal file
2
frontend/app/communication/ai-campaigns/page.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
"use client";
|
||||
export { AiCampaignsPage as default } from "@/modules/communication/features/aiCampaigns";
|
||||
5
frontend/app/communication/analytics/error.tsx
Normal file
5
frontend/app/communication/analytics/error.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
import { ErrorState } from "@/components/ds";
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
return <ErrorState message={error.message} onRetry={reset} />;
|
||||
}
|
||||
2
frontend/app/communication/analytics/loading.tsx
Normal file
2
frontend/app/communication/analytics/loading.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
import { LoadingState } from "@/components/ds";
|
||||
export default function Loading() { return <LoadingState label="بارگذاری…" />; }
|
||||
2
frontend/app/communication/analytics/page.tsx
Normal file
2
frontend/app/communication/analytics/page.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
"use client";
|
||||
export { AnalyticsPage as default } from "@/modules/communication/features/monitoring";
|
||||
5
frontend/app/communication/api-keys/error.tsx
Normal file
5
frontend/app/communication/api-keys/error.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
import { ErrorState } from "@/components/ds";
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
return <ErrorState message={error.message} onRetry={reset} />;
|
||||
}
|
||||
2
frontend/app/communication/api-keys/loading.tsx
Normal file
2
frontend/app/communication/api-keys/loading.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
import { LoadingState } from "@/components/ds";
|
||||
export default function Loading() { return <LoadingState label="بارگذاری…" />; }
|
||||
2
frontend/app/communication/api-keys/page.tsx
Normal file
2
frontend/app/communication/api-keys/page.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
"use client";
|
||||
export { ApiKeysPage as default } from "@/modules/communication/features/apiKeys";
|
||||
5
frontend/app/communication/audit/error.tsx
Normal file
5
frontend/app/communication/audit/error.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
import { ErrorState } from "@/components/ds";
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
return <ErrorState message={error.message} onRetry={reset} />;
|
||||
}
|
||||
2
frontend/app/communication/audit/loading.tsx
Normal file
2
frontend/app/communication/audit/loading.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
import { LoadingState } from "@/components/ds";
|
||||
export default function Loading() { return <LoadingState label="بارگذاری…" />; }
|
||||
2
frontend/app/communication/audit/page.tsx
Normal file
2
frontend/app/communication/audit/page.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
"use client";
|
||||
export { AuditPage as default } from "@/modules/communication/features/audit";
|
||||
5
frontend/app/communication/automation/error.tsx
Normal file
5
frontend/app/communication/automation/error.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
import { ErrorState } from "@/components/ds";
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
return <ErrorState message={error.message} onRetry={reset} />;
|
||||
}
|
||||
2
frontend/app/communication/automation/loading.tsx
Normal file
2
frontend/app/communication/automation/loading.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
import { LoadingState } from "@/components/ds";
|
||||
export default function Loading() { return <LoadingState label="بارگذاری…" />; }
|
||||
2
frontend/app/communication/automation/page.tsx
Normal file
2
frontend/app/communication/automation/page.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
"use client";
|
||||
export { MarketingAutomationPage as default } from "@/modules/communication/features/marketingAutomation";
|
||||
5
frontend/app/communication/automation/triggers/error.tsx
Normal file
5
frontend/app/communication/automation/triggers/error.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
import { ErrorState } from "@/components/ds";
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
return <ErrorState message={error.message} onRetry={reset} />;
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
import { LoadingState } from "@/components/ds";
|
||||
export default function Loading() { return <LoadingState label="بارگذاری…" />; }
|
||||
2
frontend/app/communication/automation/triggers/page.tsx
Normal file
2
frontend/app/communication/automation/triggers/page.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
"use client";
|
||||
export { AutomationTriggersPage as default } from "@/modules/communication/features/automationTriggers";
|
||||
@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
import { ErrorState } from "@/components/ds";
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
return <ErrorState message={error.message} onRetry={reset} />;
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
import { LoadingState } from "@/components/ds";
|
||||
export default function Loading() { return <LoadingState label="بارگذاری…" />; }
|
||||
@ -0,0 +1,2 @@
|
||||
"use client";
|
||||
export { CampaignOrchestratorPage as default } from "@/modules/communication/features/campaignOrchestrator";
|
||||
5
frontend/app/communication/capabilities/error.tsx
Normal file
5
frontend/app/communication/capabilities/error.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
import { ErrorState } from "@/components/ds";
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
return <ErrorState message={error.message} onRetry={reset} />;
|
||||
}
|
||||
2
frontend/app/communication/capabilities/loading.tsx
Normal file
2
frontend/app/communication/capabilities/loading.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
import { LoadingState } from "@/components/ds";
|
||||
export default function Loading() { return <LoadingState label="بارگذاری…" />; }
|
||||
2
frontend/app/communication/capabilities/page.tsx
Normal file
2
frontend/app/communication/capabilities/page.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
"use client";
|
||||
export { CapabilitiesPage as default } from "@/modules/communication/features/capabilities";
|
||||
5
frontend/app/communication/connectors/error.tsx
Normal file
5
frontend/app/communication/connectors/error.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
import { ErrorState } from "@/components/ds";
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
return <ErrorState message={error.message} onRetry={reset} />;
|
||||
}
|
||||
2
frontend/app/communication/connectors/loading.tsx
Normal file
2
frontend/app/communication/connectors/loading.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
import { LoadingState } from "@/components/ds";
|
||||
export default function Loading() { return <LoadingState label="بارگذاری…" />; }
|
||||
2
frontend/app/communication/connectors/page.tsx
Normal file
2
frontend/app/communication/connectors/page.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
"use client";
|
||||
export { ConnectorsPage as default } from "@/modules/communication/features/connectors";
|
||||
@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
import { ErrorState } from "@/components/ds";
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
return <ErrorState message={error.message} onRetry={reset} />;
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
import { LoadingState } from "@/components/ds";
|
||||
export default function Loading() { return <LoadingState label="بارگذاری…" />; }
|
||||
@ -0,0 +1,2 @@
|
||||
"use client";
|
||||
export { AddressBooksPage as default } from "@/modules/communication/features/recipients";
|
||||
5
frontend/app/communication/contacts/recipients/error.tsx
Normal file
5
frontend/app/communication/contacts/recipients/error.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
import { ErrorState } from "@/components/ds";
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
return <ErrorState message={error.message} onRetry={reset} />;
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
import { LoadingState } from "@/components/ds";
|
||||
export default function Loading() { return <LoadingState label="بارگذاری…" />; }
|
||||
2
frontend/app/communication/contacts/recipients/page.tsx
Normal file
2
frontend/app/communication/contacts/recipients/page.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
"use client";
|
||||
export { RecipientsPage as default } from "@/modules/communication/features/recipients";
|
||||
5
frontend/app/communication/contacts/sources/error.tsx
Normal file
5
frontend/app/communication/contacts/sources/error.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
import { ErrorState } from "@/components/ds";
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
return <ErrorState message={error.message} onRetry={reset} />;
|
||||
}
|
||||
2
frontend/app/communication/contacts/sources/loading.tsx
Normal file
2
frontend/app/communication/contacts/sources/loading.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
import { LoadingState } from "@/components/ds";
|
||||
export default function Loading() { return <LoadingState label="بارگذاری…" />; }
|
||||
2
frontend/app/communication/contacts/sources/page.tsx
Normal file
2
frontend/app/communication/contacts/sources/page.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
"use client";
|
||||
export { ContactSourcesPage as default } from "@/modules/communication/features/contactSources";
|
||||
5
frontend/app/communication/conversations/error.tsx
Normal file
5
frontend/app/communication/conversations/error.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
import { ErrorState } from "@/components/ds";
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
return <ErrorState message={error.message} onRetry={reset} />;
|
||||
}
|
||||
2
frontend/app/communication/conversations/loading.tsx
Normal file
2
frontend/app/communication/conversations/loading.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
import { LoadingState } from "@/components/ds";
|
||||
export default function Loading() { return <LoadingState label="بارگذاری…" />; }
|
||||
2
frontend/app/communication/conversations/page.tsx
Normal file
2
frontend/app/communication/conversations/page.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
"use client";
|
||||
export { ConversationsPage as default } from "@/modules/communication/features/conversations";
|
||||
5
frontend/app/communication/dashboard/error.tsx
Normal file
5
frontend/app/communication/dashboard/error.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
import { ErrorState } from "@/components/ds";
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
return <ErrorState message={error.message} onRetry={reset} />;
|
||||
}
|
||||
2
frontend/app/communication/dashboard/loading.tsx
Normal file
2
frontend/app/communication/dashboard/loading.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
import { LoadingState } from "@/components/ds";
|
||||
export default function Loading() { return <LoadingState label="بارگذاری…" />; }
|
||||
2
frontend/app/communication/dashboard/page.tsx
Normal file
2
frontend/app/communication/dashboard/page.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
"use client";
|
||||
export { CommunicationDashboard as default } from "@/modules/communication/features/dashboard";
|
||||
5
frontend/app/communication/email/error.tsx
Normal file
5
frontend/app/communication/email/error.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
import { ErrorState } from "@/components/ds";
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
return <ErrorState message={error.message} onRetry={reset} />;
|
||||
}
|
||||
2
frontend/app/communication/email/loading.tsx
Normal file
2
frontend/app/communication/email/loading.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
import { LoadingState } from "@/components/ds";
|
||||
export default function Loading() { return <LoadingState label="بارگذاری…" />; }
|
||||
2
frontend/app/communication/email/page.tsx
Normal file
2
frontend/app/communication/email/page.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
"use client";
|
||||
export { EmailPage as default } from "@/modules/communication/features/email";
|
||||
5
frontend/app/communication/error.tsx
Normal file
5
frontend/app/communication/error.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
import { ErrorState } from "@/components/ds";
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
return <ErrorState message={error.message} onRetry={reset} />;
|
||||
}
|
||||
5
frontend/app/communication/health/error.tsx
Normal file
5
frontend/app/communication/health/error.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
import { ErrorState } from "@/components/ds";
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
return <ErrorState message={error.message} onRetry={reset} />;
|
||||
}
|
||||
2
frontend/app/communication/health/loading.tsx
Normal file
2
frontend/app/communication/health/loading.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
import { LoadingState } from "@/components/ds";
|
||||
export default function Loading() { return <LoadingState label="بارگذاری…" />; }
|
||||
2
frontend/app/communication/health/page.tsx
Normal file
2
frontend/app/communication/health/page.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
"use client";
|
||||
export { HealthPage as default } from "@/modules/communication/features/health";
|
||||
5
frontend/app/communication/inbox/error.tsx
Normal file
5
frontend/app/communication/inbox/error.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
import { ErrorState } from "@/components/ds";
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
return <ErrorState message={error.message} onRetry={reset} />;
|
||||
}
|
||||
2
frontend/app/communication/inbox/loading.tsx
Normal file
2
frontend/app/communication/inbox/loading.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
import { LoadingState } from "@/components/ds";
|
||||
export default function Loading() { return <LoadingState label="بارگذاری…" />; }
|
||||
2
frontend/app/communication/inbox/page.tsx
Normal file
2
frontend/app/communication/inbox/page.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
"use client";
|
||||
export { OmnichannelInboxPage as default } from "@/modules/communication/features/omnichannelInbox";
|
||||
5
frontend/app/communication/integrations/error.tsx
Normal file
5
frontend/app/communication/integrations/error.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
import { ErrorState } from "@/components/ds";
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
return <ErrorState message={error.message} onRetry={reset} />;
|
||||
}
|
||||
2
frontend/app/communication/integrations/loading.tsx
Normal file
2
frontend/app/communication/integrations/loading.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
import { LoadingState } from "@/components/ds";
|
||||
export default function Loading() { return <LoadingState label="بارگذاری…" />; }
|
||||
2
frontend/app/communication/integrations/page.tsx
Normal file
2
frontend/app/communication/integrations/page.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
"use client";
|
||||
export { IntegrationsHubPage as default } from "@/modules/communication/features/integrationsHub";
|
||||
5
frontend/app/communication/journey-builder/error.tsx
Normal file
5
frontend/app/communication/journey-builder/error.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
import { ErrorState } from "@/components/ds";
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
return <ErrorState message={error.message} onRetry={reset} />;
|
||||
}
|
||||
2
frontend/app/communication/journey-builder/loading.tsx
Normal file
2
frontend/app/communication/journey-builder/loading.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
import { LoadingState } from "@/components/ds";
|
||||
export default function Loading() { return <LoadingState label="بارگذاری…" />; }
|
||||
2
frontend/app/communication/journey-builder/page.tsx
Normal file
2
frontend/app/communication/journey-builder/page.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
"use client";
|
||||
export { JourneyBuilderPage as default } from "@/modules/communication/features/journeyBuilder";
|
||||
3
frontend/app/communication/layout.tsx
Normal file
3
frontend/app/communication/layout.tsx
Normal file
@ -0,0 +1,3 @@
|
||||
"use client";
|
||||
import { CommunicationRootLayout } from "@/modules/communication/components/createPortalLayout";
|
||||
export default CommunicationRootLayout;
|
||||
5
frontend/app/communication/live-chat/error.tsx
Normal file
5
frontend/app/communication/live-chat/error.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
import { ErrorState } from "@/components/ds";
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
return <ErrorState message={error.message} onRetry={reset} />;
|
||||
}
|
||||
2
frontend/app/communication/live-chat/loading.tsx
Normal file
2
frontend/app/communication/live-chat/loading.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
import { LoadingState } from "@/components/ds";
|
||||
export default function Loading() { return <LoadingState label="بارگذاری…" />; }
|
||||
2
frontend/app/communication/live-chat/page.tsx
Normal file
2
frontend/app/communication/live-chat/page.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
"use client";
|
||||
export { LiveChatPage as default } from "@/modules/communication/features/liveChat";
|
||||
2
frontend/app/communication/loading.tsx
Normal file
2
frontend/app/communication/loading.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
import { LoadingState } from "@/components/ds";
|
||||
export default function Loading() { return <LoadingState label="بارگذاری…" />; }
|
||||
5
frontend/app/communication/monitoring/error.tsx
Normal file
5
frontend/app/communication/monitoring/error.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
import { ErrorState } from "@/components/ds";
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
return <ErrorState message={error.message} onRetry={reset} />;
|
||||
}
|
||||
2
frontend/app/communication/monitoring/loading.tsx
Normal file
2
frontend/app/communication/monitoring/loading.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
import { LoadingState } from "@/components/ds";
|
||||
export default function Loading() { return <LoadingState label="بارگذاری…" />; }
|
||||
2
frontend/app/communication/monitoring/page.tsx
Normal file
2
frontend/app/communication/monitoring/page.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
"use client";
|
||||
export { MonitoringPage as default } from "@/modules/communication/features/monitoring";
|
||||
7
frontend/app/communication/not-found.tsx
Normal file
7
frontend/app/communication/not-found.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
import Link from "next/link";
|
||||
import { EmptyState, Button } from "@/components/ds";
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<EmptyState title="صفحه یافت نشد" description="مسیر ارتباطات نامعتبر است." action={<Link href="/communication"><Button>داشبورد</Button></Link>} />
|
||||
);
|
||||
}
|
||||
5
frontend/app/communication/notifications/error.tsx
Normal file
5
frontend/app/communication/notifications/error.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
import { ErrorState } from "@/components/ds";
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
return <ErrorState message={error.message} onRetry={reset} />;
|
||||
}
|
||||
2
frontend/app/communication/notifications/loading.tsx
Normal file
2
frontend/app/communication/notifications/loading.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
import { LoadingState } from "@/components/ds";
|
||||
export default function Loading() { return <LoadingState label="بارگذاری…" />; }
|
||||
2
frontend/app/communication/notifications/page.tsx
Normal file
2
frontend/app/communication/notifications/page.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
"use client";
|
||||
export { NotificationCenterPage as default } from "@/modules/communication/features/notificationCenter";
|
||||
5
frontend/app/communication/otp/error.tsx
Normal file
5
frontend/app/communication/otp/error.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
import { ErrorState } from "@/components/ds";
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
return <ErrorState message={error.message} onRetry={reset} />;
|
||||
}
|
||||
2
frontend/app/communication/otp/loading.tsx
Normal file
2
frontend/app/communication/otp/loading.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
import { LoadingState } from "@/components/ds";
|
||||
export default function Loading() { return <LoadingState label="بارگذاری…" />; }
|
||||
2
frontend/app/communication/otp/page.tsx
Normal file
2
frontend/app/communication/otp/page.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
"use client";
|
||||
export { OtpPage as default } from "@/modules/communication/features/otp";
|
||||
2
frontend/app/communication/page.tsx
Normal file
2
frontend/app/communication/page.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
"use client";
|
||||
export { ExecutiveDashboard as default } from "@/modules/communication/features/executiveDashboard";
|
||||
5
frontend/app/communication/permissions/error.tsx
Normal file
5
frontend/app/communication/permissions/error.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
import { ErrorState } from "@/components/ds";
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
return <ErrorState message={error.message} onRetry={reset} />;
|
||||
}
|
||||
2
frontend/app/communication/permissions/loading.tsx
Normal file
2
frontend/app/communication/permissions/loading.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
import { LoadingState } from "@/components/ds";
|
||||
export default function Loading() { return <LoadingState label="بارگذاری…" />; }
|
||||
2
frontend/app/communication/permissions/page.tsx
Normal file
2
frontend/app/communication/permissions/page.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
"use client";
|
||||
export { PermissionsPage as default } from "@/modules/communication/features/permissions";
|
||||
5
frontend/app/communication/providers/dashboard/error.tsx
Normal file
5
frontend/app/communication/providers/dashboard/error.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
import { ErrorState } from "@/components/ds";
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
return <ErrorState message={error.message} onRetry={reset} />;
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
import { LoadingState } from "@/components/ds";
|
||||
export default function Loading() { return <LoadingState label="بارگذاری…" />; }
|
||||
2
frontend/app/communication/providers/dashboard/page.tsx
Normal file
2
frontend/app/communication/providers/dashboard/page.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
"use client";
|
||||
export { ProviderDashboard as default } from "@/modules/communication/features/providerDashboard";
|
||||
5
frontend/app/communication/push/error.tsx
Normal file
5
frontend/app/communication/push/error.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
import { ErrorState } from "@/components/ds";
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
return <ErrorState message={error.message} onRetry={reset} />;
|
||||
}
|
||||
2
frontend/app/communication/push/loading.tsx
Normal file
2
frontend/app/communication/push/loading.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
import { LoadingState } from "@/components/ds";
|
||||
export default function Loading() { return <LoadingState label="بارگذاری…" />; }
|
||||
2
frontend/app/communication/push/page.tsx
Normal file
2
frontend/app/communication/push/page.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
"use client";
|
||||
export { PushPage as default } from "@/modules/communication/features/push";
|
||||
5
frontend/app/communication/reports/error.tsx
Normal file
5
frontend/app/communication/reports/error.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
import { ErrorState } from "@/components/ds";
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
return <ErrorState message={error.message} onRetry={reset} />;
|
||||
}
|
||||
2
frontend/app/communication/reports/loading.tsx
Normal file
2
frontend/app/communication/reports/loading.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
import { LoadingState } from "@/components/ds";
|
||||
export default function Loading() { return <LoadingState label="بارگذاری…" />; }
|
||||
2
frontend/app/communication/reports/page.tsx
Normal file
2
frontend/app/communication/reports/page.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
"use client";
|
||||
export { ReportsPage as default } from "@/modules/communication/features/monitoring";
|
||||
5
frontend/app/communication/roles/error.tsx
Normal file
5
frontend/app/communication/roles/error.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
import { ErrorState } from "@/components/ds";
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
return <ErrorState message={error.message} onRetry={reset} />;
|
||||
}
|
||||
2
frontend/app/communication/roles/loading.tsx
Normal file
2
frontend/app/communication/roles/loading.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
import { LoadingState } from "@/components/ds";
|
||||
export default function Loading() { return <LoadingState label="بارگذاری…" />; }
|
||||
2
frontend/app/communication/roles/page.tsx
Normal file
2
frontend/app/communication/roles/page.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
"use client";
|
||||
export { RolesPage as default } from "@/modules/communication/features/roles";
|
||||
5
frontend/app/communication/rubika/error.tsx
Normal file
5
frontend/app/communication/rubika/error.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
import { ErrorState } from "@/components/ds";
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
return <ErrorState message={error.message} onRetry={reset} />;
|
||||
}
|
||||
2
frontend/app/communication/rubika/loading.tsx
Normal file
2
frontend/app/communication/rubika/loading.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
import { LoadingState } from "@/components/ds";
|
||||
export default function Loading() { return <LoadingState label="بارگذاری…" />; }
|
||||
2
frontend/app/communication/rubika/page.tsx
Normal file
2
frontend/app/communication/rubika/page.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
"use client";
|
||||
export { RubikaPage as default } from "@/modules/communication/features/rubika";
|
||||
5
frontend/app/communication/settings/error.tsx
Normal file
5
frontend/app/communication/settings/error.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
import { ErrorState } from "@/components/ds";
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
return <ErrorState message={error.message} onRetry={reset} />;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user