diff --git a/docs/superpowers/plans/2026-04-05-angular-react-migration-implementation.md b/docs/superpowers/plans/2026-04-05-angular-react-migration-implementation.md
new file mode 100644
index 000000000..d2f180ec7
--- /dev/null
+++ b/docs/superpowers/plans/2026-04-05-angular-react-migration-implementation.md
@@ -0,0 +1,1767 @@
+# Angular → React Migration Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Migrate Aeroflot Flights Angular 12 application to React 18 with pixel-perfect visual and functional parity, validated by 1,225+ e2e tests and BackstopJS visual regression.
+
+**Architecture:** Monorepo structure with both Angular and React versions running simultaneously. Single e2e test suite validates both. SCSS copied directly (no CSS Modules). PrimeReact replaces PrimeNG (identical CSS classes). Vite for dev speed, future Rsbuild support.
+
+**Tech Stack:** React 18, Vite, PrimeReact, react-router-dom, i18next, axios, @tanstack/react-query, Zustand, Cypress, BackstopJS
+
+---
+
+## Phase 1: Infrastructure Setup
+
+### Task 1: Create monorepo structure and move Angular app to apps/angular
+
+**Files:**
+- Create: `apps/angular/` (move existing `ClientApp/` here)
+- Create: `apps/react/` (new React app)
+- Create: `e2e/` (shared e2e tests)
+- Create: `package.json` (root monorepo config)
+
+- [ ] **Step 1: Create root package.json for monorepo**
+
+```json
+{
+ "name": "aeroflot-flights",
+ "private": true,
+ "workspaces": [
+ "apps/angular",
+ "apps/react",
+ "e2e"
+ ],
+ "scripts": {
+ "dev:angular": "cd apps/angular && npm start",
+ "dev:react": "cd apps/react && npm run dev",
+ "dev:both": "concurrently \"npm run dev:angular\" \"npm run dev:react\"",
+ "validate": "cd e2e && bash ../scripts/full-validation.sh"
+ },
+ "devDependencies": {
+ "concurrently": "^8.2.0"
+ }
+}
+```
+
+- [ ] **Step 2: Move Angular ClientApp to apps/angular**
+
+```bash
+mkdir -p apps
+mv ClientApp apps/angular
+cd apps/angular
+# Verify angular.json exists
+test -f angular.json && echo "✅ Angular app moved"
+```
+
+- [ ] **Step 3: Create apps/react directory structure**
+
+```bash
+mkdir -p apps/react/src/{app,styles,assets,components,features}
+mkdir -p apps/react/public
+```
+
+- [ ] **Step 4: Create e2e directory structure**
+
+```bash
+mkdir -p e2e/{cypress/integration/{online-board,flight-details,schedule,flights-map,components,navigation,responsive,i18n,error-handling,search-history,integration,performance,accessibility},cypress/support/helpers,backstop/{engine_scripts/{puppet,playwright},bitmaps_reference,bitmaps_test,html_report_react,results},scripts}
+```
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add -A
+git commit -m "infrastructure: create monorepo structure with apps/angular, apps/react, e2e directories"
+```
+
+---
+
+### Task 2: Scaffold React app with Vite
+
+**Files:**
+- Create: `apps/react/package.json`
+- Create: `apps/react/vite.config.ts`
+- Create: `apps/react/tsconfig.json`
+- Create: `apps/react/src/main.tsx`
+- Create: `apps/react/src/index.html`
+- Create: `apps/react/src/app/App.tsx`
+
+- [ ] **Step 1: Create React package.json**
+
+```json
+{
+ "name": "@aeroflot-flights/react",
+ "version": "1.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc && vite build",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-router-dom": "^6.15.0",
+ "primereact": "^10.0.0",
+ "primeicons": "^6.0.0",
+ "leaflet": "^1.7.1",
+ "i18next": "^23.7.0",
+ "i18next-http-backend": "^2.4.0",
+ "react-i18next": "^13.5.0",
+ "axios": "^1.6.0",
+ "@tanstack/react-query": "^5.28.0",
+ "zustand": "^4.4.0"
+ },
+ "devDependencies": {
+ "vite": "^5.0.0",
+ "@vitejs/plugin-react": "^4.2.0",
+ "typescript": "^5.3.0",
+ "@types/react": "^18.2.0",
+ "@types/react-dom": "^18.2.0",
+ "sass": "^1.69.0"
+ }
+}
+```
+
+- [ ] **Step 2: Create vite.config.ts**
+
+```typescript
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+import path from 'path'
+
+export default defineConfig({
+ plugins: [react()],
+ server: {
+ port: 3001,
+ open: false,
+ },
+ resolve: {
+ alias: {
+ '@app': path.resolve(__dirname, './src/app'),
+ '@styles': path.resolve(__dirname, './src/styles'),
+ '@assets': path.resolve(__dirname, './src/assets'),
+ },
+ },
+ build: {
+ outDir: 'dist',
+ sourcemap: false,
+ },
+})
+```
+
+- [ ] **Step 3: Create tsconfig.json**
+
+```json
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "useDefineForClassFields": true,
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true,
+ "resolveJsonModule": true,
+ "moduleResolution": "bundler",
+ "baseUrl": ".",
+ "paths": {
+ "@app/*": ["./src/app/*"],
+ "@styles/*": ["./src/styles/*"],
+ "@assets/*": ["./src/assets/*"]
+ }
+ },
+ "include": ["src"],
+ "references": [{ "path": "./tsconfig.node.json" }]
+}
+```
+
+- [ ] **Step 4: Create tsconfig.node.json**
+
+```json
+{
+ "compilerOptions": {
+ "composite": true,
+ "skipLibCheck": true,
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "allowSyntheticDefaultImports": true
+ },
+ "include": ["vite.config.ts"]
+}
+```
+
+- [ ] **Step 5: Create src/index.html**
+
+```html
+
+
+
+
+
+
+ Aeroflot Flights
+
+
+
+
+
+
+```
+
+- [ ] **Step 6: Create src/main.tsx**
+
+```typescript
+import React from 'react'
+import ReactDOM from 'react-dom/client'
+import App from './app/App'
+import './styles/index.scss'
+
+ReactDOM.createRoot(document.getElementById('root')!).render(
+
+
+ ,
+)
+```
+
+- [ ] **Step 7: Create src/app/App.tsx**
+
+```typescript
+import React from 'react'
+
+export default function App() {
+ return (
+
+
Aeroflot Flights - React Migration
+
Coming soon...
+
+ )
+}
+```
+
+- [ ] **Step 8: Test React app builds**
+
+```bash
+cd apps/react
+npm install
+npm run build
+# Expected: dist/ folder created with index.html, assets/
+echo "✅ React app builds successfully"
+```
+
+- [ ] **Step 9: Commit**
+
+```bash
+git add apps/react/
+git commit -m "infrastructure: scaffold React app with Vite, TypeScript, and basic App component"
+```
+
+---
+
+### Task 3: Set up e2e folder with Cypress config
+
+**Files:**
+- Create: `e2e/package.json`
+- Create: `e2e/cypress.config.ts`
+- Create: `e2e/tsconfig.json`
+- Create: `e2e/cypress/support/commands.ts`
+- Create: `e2e/cypress/support/index.ts`
+
+- [ ] **Step 1: Create e2e/package.json**
+
+```json
+{
+ "name": "@aeroflot-flights/e2e",
+ "version": "1.0.0",
+ "private": true,
+ "scripts": {
+ "cypress:open": "cypress open",
+ "cypress:run": "cypress run",
+ "backstop:reference": "backstop reference --config=backstop/backstop-angular.json",
+ "backstop:test": "backstop test --config=backstop/backstop-react.json",
+ "validate": "bash ../scripts/full-validation.sh"
+ },
+ "devDependencies": {
+ "cypress": "^13.6.0",
+ "backstopjs": "^6.3.0",
+ "@cypress/schematic": "^2.5.0",
+ "typescript": "^5.3.0"
+ }
+}
+```
+
+- [ ] **Step 2: Create cypress.config.ts**
+
+```typescript
+import { defineConfig } from 'cypress'
+
+export default defineConfig({
+ e2e: {
+ baseUrl: 'http://localhost:3000',
+ supportFile: 'cypress/support/index.ts',
+ specPattern: 'cypress/integration/**/*.cy.ts',
+ viewportWidth: 1440,
+ viewportHeight: 900,
+ video: false,
+ screenshotOnRunFailure: true,
+ requestTimeout: 10000,
+ responseTimeout: 10000,
+ defaultCommandTimeout: 10000,
+ },
+ component: {
+ devServer: {
+ framework: 'react',
+ bundler: 'vite',
+ },
+ },
+})
+```
+
+- [ ] **Step 3: Create cypress/support/commands.ts**
+
+```typescript
+///
+
+Cypress.Commands.add('getByTestId', (id: string, timeout = 8000) => {
+ return cy.get(`[data-testid="${id}"]`, { timeout })
+})
+
+Cypress.Commands.add('forbidGeolocation', () => {
+ cy.window().then(win => {
+ cy.stub(win.navigator.geolocation, 'getCurrentPosition').rejects(
+ new Error('Geolocation forbidden')
+ )
+ })
+})
+
+declare global {
+ namespace Cypress {
+ interface Chainable {
+ getByTestId(id: string, timeout?: number): Chainable>
+ forbidGeolocation(): Chainable
+ }
+ }
+}
+
+export {}
+```
+
+- [ ] **Step 4: Create cypress/support/index.ts**
+
+```typescript
+import './commands'
+
+beforeEach(() => {
+ // Clear localStorage before each test
+ cy.window().then(win => {
+ win.localStorage.clear()
+ })
+})
+
+afterEach(() => {
+ // Clean up after each test
+ cy.window().then(win => {
+ win.localStorage.clear()
+ })
+})
+```
+
+- [ ] **Step 5: Create e2e/tsconfig.json**
+
+```json
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "lib": ["ES2020", "DOM"],
+ "module": "ESNext",
+ "moduleResolution": "node",
+ "strict": true,
+ "resolveJsonModule": true,
+ "types": ["cypress", "node"]
+ },
+ "include": ["cypress/**/*.ts", "cypress/**/*.tsx"]
+}
+```
+
+- [ ] **Step 6: Verify Cypress installs**
+
+```bash
+cd e2e
+npm install
+npx cypress --version
+# Expected: Version output like "13.6.0"
+echo "✅ Cypress installed"
+```
+
+- [ ] **Step 7: Commit**
+
+```bash
+git add e2e/
+git commit -m "infrastructure: set up e2e folder with Cypress config and support commands"
+```
+
+---
+
+## Phase 2: Styling Foundation
+
+### Task 4: Copy global SCSS files from Angular to React
+
+**Files:**
+- Create: `apps/react/src/styles/` (all 30 global SCSS files from Angular)
+
+- [ ] **Step 1: Copy framework and global style files**
+
+```bash
+cd apps/react/src/styles
+
+# Copy from Angular
+cp ../../../apps/angular/src/styles/framework.scss .
+cp ../../../apps/angular/src/styles/_reset.scss .
+cp ../../../apps/angular/src/styles/_colors.scss .
+cp ../../../apps/angular/src/styles/_fonts.scss .
+cp ../../../apps/angular/src/styles/_fonts.classes.scss .
+cp ../../../apps/angular/src/styles/_shadows.scss .
+cp ../../../apps/angular/src/styles/_variables.scss .
+cp ../../../apps/angular/src/styles/_prime-styles.scss .
+cp ../../../apps/angular/src/styles/_prime-calendar.scss .
+cp ../../../apps/angular/src/styles/_layout.scss .
+cp ../../../apps/angular/src/styles/_icons.scss .
+cp ../../../apps/angular/src/styles/_buttons.scss .
+cp ../../../apps/angular/src/styles/_tooltips.scss .
+cp ../../../apps/angular/src/styles/_overrides.scss .
+cp ../../../apps/angular/src/styles/_banners.scss .
+cp ../../../apps/angular/src/styles/_logos.scss .
+cp ../../../apps/angular/src/styles/_common.scss .
+cp ../../../apps/angular/src/styles/_scrollbar.scss .
+cp ../../../apps/angular/src/styles/_grid-sizes.scss .
+cp ../../../apps/angular/src/styles/_leaflet-popup.scss .
+cp ../../../apps/angular/src/styles/styles/_mixins.scss ./
+cp ../../../apps/angular/src/styles/_screen.scss .
+cp ../../../apps/angular/src/styles/_positioning.scss .
+cp ../../../apps/angular/src/styles/_layouts.scss .
+
+echo "✅ Global SCSS files copied"
+```
+
+- [ ] **Step 2: Copy pages and adaptive styles**
+
+```bash
+# Create page directories
+mkdir -p pages/board pages/schedule adaptive
+
+# Copy page styles
+cp ../../../apps/angular/src/styles/pages/board/*.scss pages/board/
+cp ../../../apps/angular/src/styles/pages/schedule/*.scss pages/schedule/
+cp ../../../apps/angular/src/styles/pages/adaptive/*.scss adaptive/
+
+echo "✅ Page and adaptive styles copied"
+```
+
+- [ ] **Step 3: Create index.scss that imports all styles**
+
+```scss
+@import 'framework';
+@import 'reset';
+@import 'screen';
+@import 'layouts';
+@import 'colors';
+@import 'fonts';
+@import 'fonts.classes';
+@import 'shadows';
+@import 'variables';
+@import 'prime-styles';
+@import 'layout';
+@import 'icons';
+@import 'buttons';
+@import 'tooltips';
+@import 'overrides';
+@import 'prime-calendar';
+@import 'pages/board/index';
+@import 'pages/schedule/index';
+@import 'banners';
+@import 'logos';
+@import 'common';
+@import 'scrollbar';
+@import 'grid-sizes';
+@import 'leaflet-popup';
+@import 'positioning';
+@import 'mixins';
+```
+
+- [ ] **Step 4: Copy assets (fonts, images)**
+
+```bash
+# Copy fonts
+mkdir -p ../assets/fonts
+cp ../../../apps/angular/src/assets/fonts/* ../assets/fonts/
+
+# Copy images
+mkdir -p ../assets/images
+cp -r ../../../apps/angular/src/assets/*.{png,jpg,jpeg,svg,ico} ../assets/ 2>/dev/null || true
+
+echo "✅ Assets copied"
+```
+
+- [ ] **Step 5: Verify SCSS compiles without errors**
+
+```bash
+cd apps/react
+npm install
+
+# Try building
+npm run build 2>&1 | grep -i "error" || echo "✅ SCSS compiles successfully"
+```
+
+- [ ] **Step 6: Commit**
+
+```bash
+git add apps/react/src/styles apps/react/src/assets
+git commit -m "styles: copy all global and page SCSS files and assets from Angular"
+```
+
+---
+
+### Task 5: Copy component SCSS files from Angular to React
+
+**Files:**
+- Create: `apps/react/src/app/` (75+ component SCSS files from Angular)
+
+- [ ] **Step 1: Create component directory structure**
+
+```bash
+# Create all component directories matching Angular structure
+mkdir -p apps/react/src/app/components/city-autocomplete
+mkdir -p apps/react/src/app/components/page-layout
+mkdir -p apps/react/src/app/features/online-board/pages
+mkdir -p apps/react/src/app/features/online-board/components
+mkdir -p apps/react/src/app/features/schedule/pages
+mkdir -p apps/react/src/app/features/flights-map
+mkdir -p apps/react/src/app/modules/components
+mkdir -p apps/react/src/app/modules/pages
+
+echo "✅ Component directories created"
+```
+
+- [ ] **Step 2: Copy component SCSS files**
+
+```bash
+# This is a bulk copy operation - copy all .scss files from Angular components
+find apps/angular/src/app -name "*.component.scss" | while read file; do
+ # Get relative path
+ rel_path="${file#apps/angular/src/app/}"
+ # Create target directory
+ target_dir="apps/react/src/app/$(dirname "$rel_path")"
+ mkdir -p "$target_dir"
+ # Copy file, renaming .component.scss to .scss
+ target_file="${file#apps/angular/}"
+ target_file="${target_file%.component.scss}.scss"
+ cp "$file" "apps/react/${target_file%.component.scss}.scss"
+done
+
+echo "✅ Component SCSS files copied"
+```
+
+- [ ] **Step 3: Verify all SCSS files copied**
+
+```bash
+# Count SCSS files
+angular_count=$(find apps/angular/src/app -name "*.component.scss" | wc -l)
+react_count=$(find apps/react/src/app -name "*.scss" | wc -l)
+
+echo "Angular SCSS components: $angular_count"
+echo "React SCSS files: $react_count"
+
+if [ "$react_count" -gt "50" ]; then
+ echo "✅ All component SCSS files copied"
+else
+ echo "⚠️ Warning: React has fewer SCSS files than expected"
+fi
+```
+
+- [ ] **Step 4: Create component index files that import SCSS**
+
+Each component will have minimal TypeScript that imports its styles:
+
+```bash
+cat > apps/react/src/app/components/city-autocomplete/city-autocomplete.scss << 'EOF'
+@use "src/styles/framework" as *;
+
+.city-autocomplete {
+ display: flex;
+ flex-direction: column;
+
+ &__labels-container {
+ justify-content: space-between;
+ margin-bottom: $space-m;
+ width: 100%;
+ display: flex;
+ align-items: center;
+ }
+
+ &__label {
+ @include font-overflow();
+ @include font-small($gray);
+ }
+
+ &__input {
+ display: flex;
+ flex-direction: row;
+ position: relative;
+ align-items: center;
+ width: 100%;
+ @include control-border-shadow();
+ }
+
+ // Copy all remaining styles from Angular
+}
+EOF
+```
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add apps/react/src/app
+git commit -m "styles: copy all 75+ component SCSS files from Angular maintaining folder structure"
+```
+
+---
+
+## Phase 3: Core Components
+
+### Task 6: Create Button component (first core UI component)
+
+**Files:**
+- Create: `apps/react/src/app/components/button/button.tsx`
+- Create: `apps/react/src/app/components/button/button.scss`
+- Create: `apps/react/src/app/components/button/index.ts`
+
+- [ ] **Step 1: Create button component**
+
+```typescript
+// apps/react/src/app/components/button/button.tsx
+import React from 'react'
+import './button.scss'
+
+export interface ButtonProps
+ extends React.ButtonHTMLAttributes {
+ variant?: 'primary' | 'secondary' | 'tertiary'
+ size?: 'small' | 'medium' | 'large'
+ disabled?: boolean
+ loading?: boolean
+ children: React.ReactNode
+}
+
+export const Button: React.FC = ({
+ variant = 'primary',
+ size = 'medium',
+ disabled = false,
+ loading = false,
+ className = '',
+ ...props
+}) => {
+ return (
+
+ )
+}
+```
+
+- [ ] **Step 2: Copy button SCSS from Angular**
+
+```bash
+# Copy from Angular's button styles
+cp apps/angular/src/styles/_buttons.scss apps/react/src/app/components/button/button.scss
+
+# Verify it exists
+test -f apps/react/src/app/components/button/button.scss && echo "✅ Button SCSS copied"
+```
+
+- [ ] **Step 3: Create index.ts for exports**
+
+```typescript
+// apps/react/src/app/components/button/index.ts
+export { Button } from './button'
+export type { ButtonProps } from './button'
+```
+
+- [ ] **Step 4: Create test file**
+
+```typescript
+// e2e/cypress/integration/components/button.cy.ts
+describe('Button Component', () => {
+ beforeEach(() => {
+ cy.visit('http://localhost:3001')
+ })
+
+ it('should render button with text', () => {
+ cy.getByTestId('button').should('exist')
+ })
+
+ it('should have correct styling', () => {
+ cy.getByTestId('button')
+ .should('have.css', 'padding')
+ .should('have.css', 'border-radius')
+ })
+})
+```
+
+- [ ] **Step 5: Verify button compiles**
+
+```bash
+cd apps/react
+npm run build 2>&1 | grep -i "error" || echo "✅ Button component compiles"
+```
+
+- [ ] **Step 6: Commit**
+
+```bash
+git add apps/react/src/app/components/button e2e/cypress/integration/components/button.cy.ts
+git commit -m "feat: implement Button component with styling from Angular"
+```
+
+---
+
+### Task 7: Create Input component
+
+**Files:**
+- Create: `apps/react/src/app/components/input/input.tsx`
+- Create: `apps/react/src/app/components/input/input.scss`
+- Create: `apps/react/src/app/components/input/index.ts`
+
+- [ ] **Step 1: Create input component**
+
+```typescript
+// apps/react/src/app/components/input/input.tsx
+import React from 'react'
+import './input.scss'
+
+export interface InputProps
+ extends React.InputHTMLAttributes {
+ label?: string
+ error?: string
+ helperText?: string
+}
+
+export const Input: React.FC = ({
+ label,
+ error,
+ helperText,
+ className = '',
+ ...props
+}) => {
+ return (
+
+ {label && }
+
+ {error && {error}}
+ {helperText && {helperText}}
+
+ )
+}
+```
+
+- [ ] **Step 2: Create input SCSS**
+
+```scss
+// apps/react/src/app/components/input/input.scss
+@use "src/styles/framework" as *;
+
+.input-wrapper {
+ display: flex;
+ flex-direction: column;
+ margin-bottom: $space-m;
+}
+
+.input-label {
+ @include font-small($gray);
+ margin-bottom: $space-s2;
+ font-weight: 600;
+}
+
+.input {
+ padding: $space-s2 $space-m;
+ border: 1px solid $border-input;
+ border-radius: $border-radius;
+ font-size: 16px;
+ transition: border-color 0.3s ease;
+
+ &:focus {
+ outline: none;
+ border-color: $primary-color;
+ }
+
+ &--error {
+ border-color: $red;
+ }
+}
+
+.input-error {
+ @include font-small($red);
+ margin-top: $space-s;
+}
+
+.input-helper {
+ @include font-small($gray);
+ margin-top: $space-s;
+}
+```
+
+- [ ] **Step 3: Create index.ts**
+
+```typescript
+// apps/react/src/app/components/input/index.ts
+export { Input } from './input'
+export type { InputProps } from './input'
+```
+
+- [ ] **Step 4: Create test**
+
+```typescript
+// e2e/cypress/integration/components/input.cy.ts
+describe('Input Component', () => {
+ beforeEach(() => {
+ cy.visit('http://localhost:3001')
+ })
+
+ it('should render input with label', () => {
+ cy.getByTestId('input').should('exist')
+ })
+
+ it('should show error state', () => {
+ cy.getByTestId('input')
+ .should('exist')
+ })
+})
+```
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add apps/react/src/app/components/input e2e/cypress/integration/components/input.cy.ts
+git commit -m "feat: implement Input component with error and helper text support"
+```
+
+---
+
+### Task 8: Create core component collection (Modal, Tabs, DatePicker)
+
+**Files:** Create 3 more core components (Modal, Tabs, DatePicker)
+
+- [ ] **Step 1: Create Modal component**
+
+```typescript
+// apps/react/src/app/components/modal/modal.tsx
+import React from 'react'
+import './modal.scss'
+
+export interface ModalProps {
+ isOpen: boolean
+ onClose: () => void
+ title?: string
+ children: React.ReactNode
+ size?: 'small' | 'medium' | 'large'
+}
+
+export const Modal: React.FC = ({
+ isOpen,
+ onClose,
+ title,
+ children,
+ size = 'medium',
+}) => {
+ if (!isOpen) return null
+
+ return (
+ <>
+
+
+ {title && (
+
+
{title}
+
+
+ )}
+
{children}
+
+ >
+ )
+}
+```
+
+- [ ] **Step 2: Create Tabs component**
+
+```typescript
+// apps/react/src/app/components/tabs/tabs.tsx
+import React, { useState } from 'react'
+import './tabs.scss'
+
+export interface Tab {
+ id: string
+ label: string
+ content: React.ReactNode
+}
+
+export interface TabsProps {
+ tabs: Tab[]
+ defaultTab?: string
+ onTabChange?: (tabId: string) => void
+}
+
+export const Tabs: React.FC = ({
+ tabs,
+ defaultTab = tabs[0]?.id,
+ onTabChange,
+}) => {
+ const [activeTab, setActiveTab] = useState(defaultTab)
+
+ const handleTabClick = (tabId: string) => {
+ setActiveTab(tabId)
+ onTabChange?.(tabId)
+ }
+
+ return (
+
+
+ {tabs.map(tab => (
+
+ ))}
+
+
+ {tabs.map(tab => (
+
+ {tab.content}
+
+ ))}
+
+
+ )
+}
+```
+
+- [ ] **Step 3: Create DatePicker component**
+
+```typescript
+// apps/react/src/app/components/date-picker/date-picker.tsx
+import React from 'react'
+import './date-picker.scss'
+
+export interface DatePickerProps {
+ value?: string
+ onChange?: (date: string) => void
+ placeholder?: string
+ label?: string
+ error?: string
+}
+
+export const DatePicker: React.FC = ({
+ value,
+ onChange,
+ placeholder = 'DD.MM.YYYY',
+ label,
+ error,
+}) => {
+ return (
+
+ {label && }
+ onChange?.(e.target.value)}
+ placeholder={placeholder}
+ data-testid="date-picker-input"
+ />
+ {error && {error}}
+
+ )
+}
+```
+
+- [ ] **Step 4: Create SCSS for all three components**
+
+```bash
+# Copy SCSS from Angular for modals, tabs, date pickers
+cp apps/angular/src/styles/pages/board/components/page-tabs.scss apps/react/src/app/components/tabs/tabs.scss
+cp apps/angular/src/styles/styles/_prime-calendar.scss apps/react/src/app/components/date-picker/date-picker.scss
+
+# Create modal SCSS
+cat > apps/react/src/app/components/modal/modal.scss << 'EOF'
+@use "src/styles/framework" as *;
+
+.modal-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: rgba(0, 0, 0, 0.4);
+ z-index: 999;
+}
+
+.modal {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ background-color: white;
+ border-radius: $border-radius;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+ z-index: 1000;
+ max-height: 90vh;
+ overflow-y: auto;
+
+ &--small {
+ width: 400px;
+ }
+
+ &--medium {
+ width: 600px;
+ }
+
+ &--large {
+ width: 800px;
+ }
+}
+
+.modal-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: $space-l;
+ border-bottom: 1px solid #e0e0e0;
+}
+
+.modal-title {
+ margin: 0;
+ font-size: 20px;
+ font-weight: 600;
+}
+
+.modal-close {
+ background: none;
+ border: none;
+ font-size: 24px;
+ cursor: pointer;
+ color: #666;
+
+ &:hover {
+ color: #000;
+ }
+}
+
+.modal-content {
+ padding: $space-l;
+}
+EOF
+```
+
+- [ ] **Step 5: Create index.ts for each component**
+
+```bash
+cat > apps/react/src/app/components/modal/index.ts << 'EOF'
+export { Modal } from './modal'
+export type { ModalProps } from './modal'
+EOF
+
+cat > apps/react/src/app/components/tabs/index.ts << 'EOF'
+export { Tabs } from './tabs'
+export type { TabsProps, Tab } from './tabs'
+EOF
+
+cat > apps/react/src/app/components/date-picker/index.ts << 'EOF'
+export { DatePicker } from './date-picker'
+export type { DatePickerProps } from './date-picker'
+EOF
+```
+
+- [ ] **Step 6: Create tests for all three**
+
+```typescript
+// e2e/cypress/integration/components/modal.cy.ts
+describe('Modal Component', () => {
+ it('should render modal when open', () => {
+ cy.visit('http://localhost:3001')
+ cy.getByTestId('modal').should('exist')
+ })
+})
+
+// e2e/cypress/integration/components/tabs.cy.ts
+describe('Tabs Component', () => {
+ it('should switch tabs when clicked', () => {
+ cy.visit('http://localhost:3001')
+ cy.getByTestId('tabs').should('exist')
+ })
+})
+
+// e2e/cypress/integration/components/date-picker.cy.ts
+describe('DatePicker Component', () => {
+ it('should accept date input', () => {
+ cy.visit('http://localhost:3001')
+ cy.getByTestId('date-picker-input').type('01.01.2024')
+ })
+})
+```
+
+- [ ] **Step 7: Commit**
+
+```bash
+git add apps/react/src/app/components/{modal,tabs,date-picker}
+git add e2e/cypress/integration/components/{modal,tabs,date-picker}.cy.ts
+git commit -m "feat: implement core UI components (Modal, Tabs, DatePicker) with styling and tests"
+```
+
+---
+
+## Phase 4: Routing Setup
+
+### Task 9: Create React Router configuration
+
+**Files:**
+- Create: `apps/react/src/app/App.tsx` (with routing)
+- Create: `apps/react/src/app/router.tsx`
+- Create: `apps/react/src/app/pages/` (stub pages)
+
+- [ ] **Step 1: Create router configuration**
+
+```typescript
+// apps/react/src/app/router.tsx
+import { createBrowserRouter } from 'react-router-dom'
+import App from './App'
+import Home from './pages/Home'
+import NotFound from './pages/NotFound'
+
+export const router = createBrowserRouter(
+ [
+ {
+ path: '/',
+ element: ,
+ children: [
+ {
+ index: true,
+ element: ,
+ },
+ {
+ path: ':lang/onlineboard/arrival/:code/:datetime',
+ element: Arrival Board Page
,
+ },
+ {
+ path: ':lang/onlineboard/departure/:code/:datetime',
+ element: Departure Board Page
,
+ },
+ {
+ path: ':lang/schedule/:code/:datetime',
+ element: Schedule Page
,
+ },
+ {
+ path: '*',
+ element: ,
+ },
+ ],
+ },
+ ],
+ {
+ basename: '/',
+ }
+)
+```
+
+- [ ] **Step 2: Update App.tsx to use router**
+
+```typescript
+// apps/react/src/app/App.tsx
+import React from 'react'
+import { Outlet } from 'react-router-dom'
+import './App.scss'
+
+export default function App() {
+ return (
+
+ )
+}
+```
+
+- [ ] **Step 3: Create stub pages**
+
+```typescript
+// apps/react/src/app/pages/Home.tsx
+import React from 'react'
+
+export default function Home() {
+ return (
+
+
Home
+
Welcome to Aeroflot Flights
+
+ )
+}
+
+// apps/react/src/app/pages/NotFound.tsx
+import React from 'react'
+
+export default function NotFound() {
+ return (
+
+
404 - Page Not Found
+
+ )
+}
+```
+
+- [ ] **Step 4: Update main.tsx to use router**
+
+```typescript
+// apps/react/src/main.tsx
+import React from 'react'
+import ReactDOM from 'react-dom/client'
+import { RouterProvider } from 'react-router-dom'
+import { router } from './app/router'
+import './styles/index.scss'
+
+ReactDOM.createRoot(document.getElementById('root')!).render(
+
+
+ ,
+)
+```
+
+- [ ] **Step 5: Create App.scss**
+
+```scss
+// apps/react/src/app/App.scss
+@use "src/styles/framework" as *;
+
+.app {
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+}
+
+.app-header {
+ background-color: white;
+ padding: $space-xl;
+ border-bottom: 1px solid #e0e0e0;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+
+ h1 {
+ margin: 0;
+ font-size: 24px;
+ font-weight: 600;
+ }
+}
+
+.app-main {
+ flex: 1;
+ padding: $space-xl;
+ max-width: $site-width;
+ width: 100%;
+ margin: 0 auto;
+}
+
+.app-footer {
+ background-color: #f5f5f5;
+ padding: $space-xl;
+ text-align: center;
+ border-top: 1px solid #e0e0e0;
+
+ p {
+ margin: 0;
+ color: #666;
+ font-size: 14px;
+ }
+}
+```
+
+- [ ] **Step 6: Verify routing works**
+
+```bash
+cd apps/react
+npm run dev &
+sleep 3
+curl -s http://localhost:3001 | grep -q "Aeroflot Flights" && echo "✅ React routing works"
+pkill -f "vite"
+```
+
+- [ ] **Step 7: Commit**
+
+```bash
+git add apps/react/src/app/{router.tsx,App.tsx,App.scss,pages/}
+git add apps/react/src/main.tsx
+git commit -m "feat: set up React Router with basic routing and layout structure"
+```
+
+---
+
+## Phase 5: E2E Test Infrastructure (Expanded)
+
+### Task 10: Create test helper files and base test templates
+
+**Files:**
+- Create: `e2e/cypress/support/helpers/api.helpers.ts`
+- Create: `e2e/cypress/support/helpers/navigation.helpers.ts`
+- Create: `e2e/cypress/support/helpers/assertions.helpers.ts`
+- Create: `e2e/cypress/support/helpers/data.helpers.ts`
+- Create: `e2e/cypress/integration/online-board/01-arrival-search.cy.ts`
+
+- [ ] **Step 1: Create API helpers**
+
+```typescript
+// e2e/cypress/support/helpers/api.helpers.ts
+export const setupApiInterceptors = () => {
+ cy.intercept('GET', '**/api/flights/**', { fixture: 'flights.json' }).as('getFlights')
+ cy.intercept('GET', '**/api/cities/**', { fixture: 'cities.json' }).as('getCities')
+ cy.intercept('GET', '**/api/schedule/**', { fixture: 'schedule.json' }).as('getSchedule')
+}
+
+export const mockApiError = (endpoint: string, statusCode: number = 500) => {
+ cy.intercept('GET', endpoint, {
+ statusCode,
+ body: { error: 'API Error' },
+ }).as('apiError')
+}
+
+export const mockApiSuccess = (endpoint: string, fixture: string) => {
+ cy.intercept('GET', endpoint, { fixture }).as('apiSuccess')
+}
+```
+
+- [ ] **Step 2: Create navigation helpers**
+
+```typescript
+// e2e/cypress/support/helpers/navigation.helpers.ts
+export const navigateToHome = () => {
+ cy.visit('/')
+}
+
+export const navigateToArrivalBoard = (cityCode: string, date: string) => {
+ cy.visit(`/ru-ru/onlineboard/arrival/${cityCode}/${date}-0000-2400`)
+}
+
+export const navigateToDepartureBoard = (cityCode: string, date: string) => {
+ cy.visit(`/ru-ru/onlineboard/departure/${cityCode}/${date}-0000-2400`)
+}
+
+export const navigateToSchedule = (cityCode: string, date: string) => {
+ cy.visit(`/ru-ru/schedule/${cityCode}/${date}`)
+}
+```
+
+- [ ] **Step 3: Create assertions helpers**
+
+```typescript
+// e2e/cypress/support/helpers/assertions.helpers.ts
+export const assertButtonStylesCorrect = (selector: string) => {
+ cy.get(selector)
+ .should('have.css', 'padding')
+ .should('have.css', 'border-radius')
+ .should('have.css', 'background-color')
+}
+
+export const assertInputStylesCorrect = (selector: string) => {
+ cy.get(selector)
+ .should('have.css', 'padding')
+ .should('have.css', 'border-radius')
+ .should('have.css', 'border')
+}
+
+export const assertModalStylesCorrect = (selector: string) => {
+ cy.get(selector)
+ .should('have.css', 'position', 'fixed')
+ .should('have.css', 'background-color')
+ .should('have.css', 'z-index')
+}
+
+export const assertResponsiveLayout = () => {
+ // Mobile
+ cy.viewport(375, 667)
+ cy.getByTestId('page-loaded').should('be.visible')
+
+ // Tablet
+ cy.viewport(768, 1024)
+ cy.getByTestId('page-loaded').should('be.visible')
+
+ // Desktop
+ cy.viewport(1440, 900)
+ cy.getByTestId('page-loaded').should('be.visible')
+}
+```
+
+- [ ] **Step 4: Create data helpers**
+
+```typescript
+// e2e/cypress/support/helpers/data.helpers.ts
+export const TEST_DATA = {
+ cities: {
+ moscow: { code: 'SVO', name: 'Москва' },
+ spb: { code: 'LED', name: 'Санкт-Петербург' },
+ anapa: { code: 'AAQ', name: 'Анапа' },
+ },
+ dates: {
+ today: new Date().toLocaleDateString('ru-RU'),
+ tomorrow: new Date(Date.now() + 86400000).toLocaleDateString('ru-RU'),
+ nextWeek: new Date(Date.now() + 604800000).toLocaleDateString('ru-RU'),
+ },
+}
+
+export const generateFlightNumber = () => {
+ return `SU${Math.floor(Math.random() * 1000)}`
+}
+
+export const getCurrentDate = (format = 'DD.MM.YYYY') => {
+ const now = new Date()
+ const day = String(now.getDate()).padStart(2, '0')
+ const month = String(now.getMonth() + 1).padStart(2, '0')
+ const year = now.getFullYear()
+ return `${day}.${month}.${year}`
+}
+```
+
+- [ ] **Step 5: Create first test suite template**
+
+```typescript
+// e2e/cypress/integration/online-board/01-arrival-search.cy.ts
+import * as moment from 'moment'
+
+describe('Online Board: Arrival Search', () => {
+ const BASE_URLS = {
+ angular: 'http://localhost:3000',
+ react: 'http://localhost:3001',
+ }
+
+ const arrivalCity = {
+ name: 'Анапа',
+ code: 'AAQ',
+ }
+
+ const today = moment().format('DD.MM.YYYY')
+
+ ;['angular', 'react'].forEach(version => {
+ describe(`${version.toUpperCase()} version`, () => {
+ beforeEach(() => {
+ cy.visit(`${BASE_URLS[version]}/`)
+ cy.intercept('GET', '**api/flights/**').as('getFlights')
+ })
+
+ it('should render home page', () => {
+ cy.getByTestId('page-loaded').should('exist')
+ })
+
+ it('should have arrival search button', () => {
+ cy.getByTestId('arrival-search-button').should('be.visible')
+ })
+
+ it('should search flights with city input', () => {
+ cy.getByTestId('city-autocomplete-input')
+ .should('exist')
+ .type(`${arrivalCity.name}`)
+ cy.getByTestId('calendar-input').type(today)
+ cy.getByTestId('arrival-search-button').click()
+
+ cy.wait('@getFlights')
+ cy.getByTestId('flight-result').should('have.length.at.least', 0)
+ })
+
+ it('should display search results', () => {
+ cy.getByTestId('city-autocomplete-input').type(`${arrivalCity.name}`)
+ cy.getByTestId('calendar-input').type(today)
+ cy.getByTestId('arrival-search-button').click()
+
+ cy.wait('@getFlights')
+ // Results should be visible or empty state should show
+ cy.getByTestId('board-search-result')
+ .or(cy.getByTestId('empty-state'))
+ .should('be.visible')
+ })
+ })
+ })
+})
+```
+
+- [ ] **Step 6: Update cypress support/index.ts to load helpers**
+
+```typescript
+// e2e/cypress/support/index.ts
+import './commands'
+import './helpers/api.helpers'
+import './helpers/navigation.helpers'
+import './helpers/assertions.helpers'
+import './helpers/data.helpers'
+
+beforeEach(() => {
+ cy.window().then(win => {
+ win.localStorage.clear()
+ })
+})
+
+afterEach(() => {
+ cy.window().then(win => {
+ win.localStorage.clear()
+ })
+})
+```
+
+- [ ] **Step 7: Verify tests can run (will fail, which is expected)**
+
+```bash
+cd e2e
+npm run cypress:run -- --config baseUrl=http://localhost:3001 --spec "cypress/integration/online-board/01-arrival-search.cy.ts" || echo "⚠️ Tests running (failures expected at this stage)"
+```
+
+- [ ] **Step 8: Commit**
+
+```bash
+git add e2e/cypress/support/helpers/ e2e/cypress/integration/online-board/
+git commit -m "test: create e2e test helpers and first Arrival Search test suite"
+```
+
+---
+
+## Phase 6: Feature Implementation (Online Board Arrival)
+
+### Task 11: Implement CityAutocomplete component
+
+**Files:**
+- Create: `apps/react/src/app/components/city-autocomplete/city-autocomplete.tsx`
+- Create: `apps/react/src/app/components/city-autocomplete/city-autocomplete.scss`
+- Create: `apps/react/src/app/components/city-autocomplete/index.ts`
+
+*(Detailed component implementation following the same pattern as Button - copy logic from Angular, update React patterns)*
+
+- [ ] **Step 1-5:** [Same pattern as Task 6 - Button component]
+ - Create component with React hooks
+ - Copy SCSS from Angular
+ - Create index.ts
+ - Add data-testid attributes matching Angular version
+ - Test and commit
+
+---
+
+### Task 12: Implement OnlineBoard Arrival feature
+
+*(High-level feature integrating multiple components)*
+
+- [ ] **Steps 1-8:** Build Arrivalsearch page using components from Task 11 and earlier core components
+
+---
+
+## Phase 7: BackstopJS Setup
+
+### Task 13: Create BackstopJS configuration for Angular (baseline)
+
+**Files:**
+- Create: `e2e/backstop/backstop-angular.json`
+- Create: `e2e/backstop/engine_scripts/puppet/runBefore.js`
+- Create: `e2e/backstop/engine_scripts/puppet/runAfter.js`
+
+*(Detailed configuration as specified in design document)*
+
+---
+
+### Task 14: Create BackstopJS configuration for React (comparison)
+
+**Files:**
+- Create: `e2e/backstop/backstop-react.json`
+
+*(Mirror of angular config, pointing to React localhost:3001)*
+
+---
+
+## Phase 8: Validation Scripts
+
+### Task 15: Create full-validation.sh script
+
+**Files:**
+- Create: `scripts/full-validation.sh`
+
+*(Complete automation script as specified in design document)*
+
+---
+
+## Phase 9: Complete Test Suite
+
+### Tasks 16-55: Write remaining 1,000+ tests
+
+Organized by feature:
+- **Tasks 16-20:** Online Board tests (80+ tests each module)
+- **Tasks 21-25:** Flight Details tests (50+ tests)
+- **Tasks 26-30:** Schedule tests (50+ tests)
+- **Tasks 31-35:** Components tests (50+ tests)
+- **Tasks 36-40:** Navigation tests (30+ tests)
+- **Tasks 41-45:** Responsive tests (50+ tests)
+- **Tasks 46-50:** i18n tests (30+ tests)
+- **Tasks 51-55:** Error handling & integration tests (100+ tests)
+
+Each task follows same pattern:
+1. Write failing tests
+2. Verify they fail
+3. Implement feature/component
+4. Verify tests pass
+5. Commit
+
+---
+
+## Phase 10: Visual Validation & Final Checks
+
+### Task 56: Generate BackstopJS baseline from Angular
+
+- [ ] **Step 1:** Start Angular version
+```bash
+cd apps/angular && npm start &
+sleep 10
+```
+
+- [ ] **Step 2:** Run BackstopJS reference capture
+```bash
+cd e2e
+npm run backstop:reference
+```
+
+- [ ] **Step 3:** Verify baseline images created
+```bash
+test -d backstop/bitmaps_reference && [ $(ls -1 backstop/bitmaps_reference | wc -l) -gt 100 ] && echo "✅ Baseline created"
+```
+
+- [ ] **Step 4:** Commit baseline
+```bash
+git add e2e/backstop/bitmaps_reference/
+git commit -m "test: generate BackstopJS baseline from Angular version"
+```
+
+---
+
+### Task 57: Run BackstopJS comparison on React
+
+- [ ] **Step 1:** Start React version
+```bash
+cd apps/react && npm run dev &
+sleep 10
+```
+
+- [ ] **Step 2:** Run BackstopJS test comparison
+```bash
+cd e2e
+npm run backstop:test
+```
+
+- [ ] **Step 3:** Review visual diff report
+```bash
+open e2e/backstop/html_report_react/index.html
+```
+
+- [ ] **Step 4:** Document any visual differences**
+
+If differences exist, note them and create issues for fixes.
+
+- [ ] **Step 5:** Commit results
+```bash
+git add e2e/backstop/bitmaps_test_react/
+git commit -m "test: run BackstopJS comparison - React vs Angular baseline"
+```
+
+---
+
+### Task 58: Fix any visual differences
+
+For each visual diff found:
+
+- [ ] **Step 1:** Identify the CSS property causing diff
+- [ ] **Step 2:** Update React component SCSS
+- [ ] **Step 3:** Regenerate BackstopJS screenshot
+- [ ] **Step 4:** Verify 0% diff
+- [ ] **Step 5:** Commit fix
+
+---
+
+### Task 59: Run full validation suite
+
+- [ ] **Step 1:** Start both versions
+```bash
+npm run dev:both &
+sleep 10
+```
+
+- [ ] **Step 2:** Run full validation
+```bash
+npm run validate
+```
+
+- [ ] **Step 3:** Verify all tests pass
+Expected output:
+```
+✅ Angular E2E Tests: PASSED (1225 tests)
+✅ React E2E Tests: PASSED (1225 tests)
+✅ Visual Regression: PASSED (0% diff)
+
+🎉 ALL VALIDATIONS PASSED
+```
+
+- [ ] **Step 4:** Final commit**
+```bash
+git commit -m "test: complete full validation - 1,225 tests pass, 0% visual diff"
+```
+
+---
+
+## Execution Checklist
+
+After all tasks completed:
+
+- [ ] All 1,225+ e2e tests pass on both Angular and React
+- [ ] BackstopJS shows 0% visual diff
+- [ ] `npm run validate` completes successfully
+- [ ] Both versions run on localhost:3000 and 3001
+- [ ] All commits are clean and descriptive
+- [ ] Repository ready for production testing
+
+---
+
+## Notes
+
+- Each task should produce working, testable code
+- Commit after every logical step
+- Use exact file paths provided
+- Follow TDD when applicable (write test, watch fail, implement, watch pass)
+- Update this checklist as you progress
+- Stop if you encounter blockers and document them
+