From 7489692d143faa1058a118eeda7f8768894f8fbd Mon Sep 17 00:00:00 2001 From: Ricardo Vega Date: Sun, 11 Oct 2026 13:10:22 -0500 Subject: [PATCH] fix(web): name Noctusoft, Inc. as the operator on every public page Twilio rejection 30482 ("Business Email Address Must Use an Official Domain"): the contact email is on noctusoft.com, which the site never named. The contact email is locked to the approved customer profile, so the site now says who operates it. "FieldView.Live is operated by Noctusoft, Inc. (https://noctusoft.com)" appears in the site footer, right under the heading of /privacy, /terms and /sms, and in the home page business section. One OperatedBy component holds the wording. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_0183M8krzbhhBfqP9Efw7Pcb --- apps/web/app/__tests__/operated-by.test.tsx | 61 +++++++++++++++++++++ apps/web/app/page.tsx | 5 +- apps/web/app/privacy/page.tsx | 3 + apps/web/app/sms/page.tsx | 2 + apps/web/app/terms/page.tsx | 3 + apps/web/components/OperatedBy.tsx | 23 ++++++++ apps/web/components/PublicLegalFooter.tsx | 3 + 7 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 apps/web/app/__tests__/operated-by.test.tsx create mode 100644 apps/web/components/OperatedBy.tsx diff --git a/apps/web/app/__tests__/operated-by.test.tsx b/apps/web/app/__tests__/operated-by.test.tsx new file mode 100644 index 0000000..36cdbb1 --- /dev/null +++ b/apps/web/app/__tests__/operated-by.test.tsx @@ -0,0 +1,61 @@ +/** + * Carrier reviewers (Twilio toll-free verification, rejection 30482) match the + * contact email's domain to the operator, so every public policy page and the + * site footer name Noctusoft, Inc. as the operator. + */ +import { render, screen } from '@testing-library/react'; +import type { AnchorHTMLAttributes, ReactNode } from 'react'; +import { describe, expect, it, vi } from 'vitest'; + +import { PublicLegalFooter } from '@/components/PublicLegalFooter'; + +import HomePage from '../page'; +import PrivacyPage from '../privacy/page'; +import SmsOptInPage from '../sms/page'; +import TermsPage from '../terms/page'; + +vi.mock('next/link', () => ({ + default: ({ children, href, ...rest }: AnchorHTMLAttributes & { children: ReactNode }) => ( + + {children} + + ), +})); + +vi.mock('@/components/SmsOptInPageForm', () => ({ + SmsOptInPageForm: () =>
, +})); + +const OPERATED_BY = 'FieldView.Live is operated by Noctusoft, Inc. (https://noctusoft.com)'; + +function expectOperatedBy(container: HTMLElement): void { + const line = container.querySelector('[data-testid="text-operated-by"]'); + expect(line).not.toBeNull(); + expect(line).toHaveTextContent(OPERATED_BY); + expect(line?.querySelector('a')).toHaveAttribute('href', 'https://noctusoft.com'); +} + +describe('operated by Noctusoft, Inc.', () => { + it('is in the site footer', () => { + render(); + expectOperatedBy(screen.getByTestId('footer-public-legal')); + }); + + it('is in the home page business section', () => { + render(); + expectOperatedBy(screen.getByTestId('section-about-business')); + }); + + it.each([ + ['privacy', PrivacyPage, 'page-privacy'], + ['terms', TermsPage, 'page-terms'], + ['sms', SmsOptInPage, 'page-sms'], + ])('is near the top of /%s', (_name, Page, testId) => { + render(); + const page = screen.getByTestId(testId); + expectOperatedBy(page); + // Right under the page heading, before the body copy. + const heading = page.querySelector('h1'); + expect(heading?.nextElementSibling).toHaveAttribute('data-testid', 'text-operated-by'); + }); +}); diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx index 5abed63..a70419d 100644 --- a/apps/web/app/page.tsx +++ b/apps/web/app/page.tsx @@ -1,5 +1,7 @@ import Link from 'next/link'; +import { OperatedBy } from '@/components/OperatedBy'; + export default function HomePage() { return (
@@ -90,8 +92,9 @@ export default function HomePage() {

FieldView.Live streams youth sports games. Teams and event owners share one link; parents and fans watch - free or buy a game. FieldView.Live is operated by Noctusoft, Inc. + free or buy a game.

+

Support:{' '}

Privacy Policy

+

Last updated: October 1, 2026

{SMS_BRAND} ("we", "us") operates the FieldView.Live streaming platform. This policy describes how we diff --git a/apps/web/app/sms/page.tsx b/apps/web/app/sms/page.tsx index d0b09af..0bd65b4 100644 --- a/apps/web/app/sms/page.tsx +++ b/apps/web/app/sms/page.tsx @@ -2,6 +2,7 @@ import { SMS_BRAND, SMS_SUPPORT_EMAIL } from '@fieldview/data-model'; import type { Metadata } from 'next'; import Link from 'next/link'; +import { OperatedBy } from '@/components/OperatedBy'; import { SmsOptInPageForm } from '@/components/SmsOptInPageForm'; export const metadata: Metadata = { @@ -13,6 +14,7 @@ export default function SmsOptInPage() { return (

Text messages from {SMS_BRAND}

+

{SMS_BRAND} streams youth sports games. Parents and fans can choose to get game alerts, score updates, and payment links by text for the teams and games they follow or buy. These are messages about games you chose, diff --git a/apps/web/app/terms/page.tsx b/apps/web/app/terms/page.tsx index 6b5e123..56c98a7 100644 --- a/apps/web/app/terms/page.tsx +++ b/apps/web/app/terms/page.tsx @@ -1,6 +1,8 @@ import { SMS_BRAND, SMS_PURPOSE_PHRASE, SMS_SUPPORT_EMAIL } from '@fieldview/data-model'; import type { Metadata } from 'next'; +import { OperatedBy } from '@/components/OperatedBy'; + export const metadata: Metadata = { title: 'Terms of Service', description: `Terms of service for ${SMS_BRAND}`, @@ -10,6 +12,7 @@ export default function TermsPage() { return (

Terms of Service

+

Last updated: October 1, 2026

diff --git a/apps/web/components/OperatedBy.tsx b/apps/web/components/OperatedBy.tsx new file mode 100644 index 0000000..78e09ac --- /dev/null +++ b/apps/web/components/OperatedBy.tsx @@ -0,0 +1,23 @@ +const OPERATOR_NAME = 'Noctusoft, Inc.'; +const OPERATOR_URL = 'https://noctusoft.com'; + +export interface OperatedByProps { + className?: string; +} + +/** + * Names the company behind FieldView.Live. Carrier reviewers (Twilio + * toll-free verification) match the contact email's domain to this operator, + * so it appears in the site footer and at the top of each policy page. + */ +export function OperatedBy({ className }: OperatedByProps) { + return ( +

+ FieldView.Live is operated by {OPERATOR_NAME} ( + + {OPERATOR_URL} + + ) +

+ ); +} diff --git a/apps/web/components/PublicLegalFooter.tsx b/apps/web/components/PublicLegalFooter.tsx index 7683702..16279e3 100644 --- a/apps/web/components/PublicLegalFooter.tsx +++ b/apps/web/components/PublicLegalFooter.tsx @@ -1,5 +1,7 @@ import Link from 'next/link'; +import { OperatedBy } from '@/components/OperatedBy'; + export function PublicLegalFooter() { return (
+ ); }