Skip to content

Frontend Replacement Extension Guide ​

This guide documents the canonical process for adding new screens to the TendSocial replacement UI system. Follow these steps in order when extending the frontend.

Source of Truth Hierarchy ​

When implementing a new screen, consult these sources in order:

  1. Canva reference (temp_new_ui/) — visual and interaction specifications
  2. UX Design Specification (_bmad-output/planning-artifacts/ux-design-specification.md) — user flows and interaction patterns
  3. PRD (_bmad-output/planning-artifacts/prd.md) — feature definitions and requirements
  4. Architecture (_bmad-output/planning-artifacts/architecture.md) — system design decisions

New-Screen Implementation Checklist ​

Step 1: Define Route in Router ​

Add the route in apps/frontend/src/app/router.tsx:

tsx
// Example — add a new route for "Analytics Dashboard"
{
  path: 'analytics',
  lazy: () => import('@/routes/analytics/AnalyticsRoute'),
}

Reference: See Epic 4.1 (campaign hub) or Story 6.8 (blog redesign) for route adapter patterns.

Step 2: Create Route Adapter ​

Create a route adapter component in apps/frontend/src/routes/<feature>/<Feature>Route.tsx:

tsx
// apps/frontend/src/routes/analytics/AnalyticsRoute.tsx
import { AnalyticsPage } from '@/features/analytics/components/AnalyticsPage';

export function Component() {
  return <AnalyticsPage />;
}

Route adapters are thin wrappers that decouple routing from feature components. They live in apps/frontend/src/routes/*/.

Step 3: Create Feature Page Components ​

Create feature pages in apps/frontend/src/features/<feature>/components/<Page>.tsx.

State Management Pattern:

  • Async data (API calls): Use TanStack Query v5 hooks (see Story 4.5 for reference)
  • Local UI state: Use useContext within the feature boundary
  • Form state: Use controlled components with local state or the feature's context provider
tsx
// apps/frontend/src/features/analytics/components/AnalyticsPage.tsx
import { Page } from '@/components/shared/Page';
import { SectionCard } from '@/components/shared/SectionCard';

export function AnalyticsPage() {
  return (
    <Page title="Analytics" description="Track your content performance">
      <SectionCard title="Overview">
        {/* Page content */}
      </SectionCard>
    </Page>
  );
}

Step 4: Use Shared State Components ​

For pages with async data, use these shared components from apps/frontend/src/components/states/:

  • <LoadingState /> — loading/skeleton state
  • <ErrorState /> — error with retry action
  • <EmptyState /> — empty state with call-to-action

Step 5: Use Design Tokens ​

Use CSS variables or Tailwind utilities — never hardcode colors:

tsx
// ✅ Correct — uses design tokens
<div className="bg-background text-foreground border-border">
  Content
</div>

// ❌ Wrong — hardcoded colors
<div style={{ backgroundColor: '#ffffff', color: '#000000' }}>
  Content
</div>

Reference: See apps/docs/internal/architecture/ui-strategy.md and apps/docs/internal/frontend/ui-theming.md for the complete token reference. Tokens are defined in apps/frontend/src/styles/globals.css under the @theme block.

Step 6: Wrap Page in <Page> Component ​

Every feature page must use the <Page> component:

tsx
import { Page } from '@/components/shared/Page';

export function FeaturePage() {
  return (
    <Page title="Page Title" description="Page description">
      {/* Page content */}
    </Page>
  );
}

Reference: apps/frontend/src/components/shared/Page.tsx

Step 7: Write Tests ​

Component tests (*.test.tsx) — use vi.mock() for hooks:

tsx
import { describe, it, expect, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
import { AnalyticsPage } from './AnalyticsPage';

vi.mock('../hooks/useAnalytics');

it('renders loading state', () => { /* ... */ });

Contract tests (*.contract.test.tsx) — use MSW with absolute URLs:

tsx
import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node';

const server = setupServer(
  http.get('http://localhost:4000/api/analytics', () => {
    return HttpResponse.json({ data: [] });
  }),
);

Reference: apps/frontend/src/features/posts/components/PlantASeedComposer.contract.test.tsx — real example of the contract test pattern.

Step 8: Add Playwright E2E Test ​

If the new screen has a critical user workflow, add an E2E test in test/e2e/:

ts
// test/e2e/analytics.spec.ts
import { test, expect } from '@playwright/test';

test('analytics dashboard loads', async ({ page }) => {
  await page.goto('/analytics');
  await expect(page.locator('h1')).toHaveText('Analytics');
});

For visual regression tests, add screenshots following the pattern from Story 7.2.

Step 9: Update Documentation ​

After implementing, update:

  • apps/docs/internal/feature-matrix.md — add the new screen to the feature matrix
  • Relevant architecture file in apps/docs/internal/architecture/

Common Pitfalls to Avoid ​

1. Hardcoded Colors ​

Don't: Write raw hex codes like #ffffff, #000, or color names like white, black, slate-100. Do: Use design tokens via CSS variables (--background, --fg-1, etc.) or Tailwind utilities (bg-background, text-foreground).

2. Unsigned Asset URLs ​

Don't: Return raw S3/CDN URLs from asset APIs. Do: Use assetDto.ts to generate signed viewUrls with TTL ≤ 15 minutes. See CLAUDE.md media-transforms guidance.

3. Tenant Data Leaks ​

Don't: Use the base Prisma client for tenant-scoped queries. Do: Use getTenantPrisma(companyId) from apps/backend/src/infra/prisma.ts. See CLAUDE.md multi-tenant section.

4. Missing .js Extensions in ESM Imports ​

Don't: Write import x from './file' (missing extension). Do: Write import x from './file.js'. See CLAUDE.md ESM Module System section.

5. Skipping State Handling ​

Don't: Only implement the happy path (success state). Do: Handle loading, empty, error, and unauthorized states for every async data source.

Reference Implementations ​

PatternStory/EpicKey Files
Route adapterEpic 4.1 (Campaign Hub)apps/frontend/src/routes/campaigns/CampaignsRoute.tsx
State management (TanStack Query)Story 4.5Various feature hooks
Contract test patternEpic 4.xapps/frontend/src/features/posts/components/PlantASeedComposer.contract.test.tsx
Visual regression testStory 7.2test/e2e/visual-regression.spec.ts
Accessibility testStory 7.3test/e2e/accessibility-*.spec.ts
Page component usageAll Epic 1-5 screensapps/frontend/src/components/shared/Page.tsx
Design tokensStory 1.2apps/frontend/src/styles/globals.css

TendSocial Documentation