feat(residential-toggle): port-level module gate for Residential
Adds a `residential_module_enabled` port setting (default ON) that hides/disables the entire Residential surface when an admin turns it off, mirroring the Tenancies / Invoices / Expenses module-toggle pattern. Disabling is a soft hide — residential clients/interests are preserved and reappear on re-enable. Surfaces gated: - Route guard: new residential/layout.tsx renders ModuleDisabledPage (covers all 5 residential pages) - Sidebar "Residential" section + mobile more-sheet tile (SSR-resolved residentialModuleByPort threaded layout → app-shell → sidebar) - Global search: residential client/interest buckets early-return at the shared chokepoint so disabled-port records don't dead-end - Public intake: /api/public/residential-inquiries 404s when off - Admin Switch in settings-manager (writes via settings PUT) Service TDD'd (residential-module.test.ts, 6 tests) plus a disabled-port rejection test on the public endpoint. tsc + lint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
||||
import { resolveSubject } from '@/lib/email/resolve-subject';
|
||||
import { getBrandingShell } from '@/lib/email/branding-resolver';
|
||||
import { getPortBrandingConfig, getPortEmailConfig } from '@/lib/services/port-config';
|
||||
import { assertResidentialModuleEnabled } from '@/lib/services/residential-module.service';
|
||||
import { env } from '@/lib/env';
|
||||
import { parseBody } from '@/lib/api/route-helpers';
|
||||
import { errorResponse, RateLimitError, ValidationError } from '@/lib/errors';
|
||||
@@ -61,6 +62,12 @@ export async function POST(req: NextRequest) {
|
||||
throw new ValidationError('Unknown port');
|
||||
}
|
||||
|
||||
// Reject when the port has the Residential module turned off — a
|
||||
// disabled port shouldn't silently accept residential leads it can't
|
||||
// see in the CRM. Throws NotFoundError → 404 (mirrors the v1 route
|
||||
// + entity-tab gates for the other module toggles).
|
||||
await assertResidentialModuleEnabled(portId);
|
||||
|
||||
// If the website didn't pre-normalize, parse server-side. International
|
||||
// strings parse without a hint; national-format submissions need a country.
|
||||
let phoneE164 = data.phoneE164 ?? null;
|
||||
|
||||
Reference in New Issue
Block a user