# Phase 2B: Translation Dictionary UI Implementation Plan

**Version:** 1.0.0
**Target:** GPT-5.1-Codex-Max Implementation
**Created:** December 2024
**Status:** Ready for Implementation

---

## Executive Summary

This document provides a comprehensive implementation plan for the Translation Dictionary UI - a critical feature enabling token mapping between design systems (DSS Core Principle #2). The backend API is complete with 12 endpoints. This plan covers the frontend implementation using the existing Web Components architecture.

---

## 1. File Structure

### Files to CREATE

```
/home/overbits/dss/admin-ui/
├── js/
│   ├── modules/
│   │   └── translations/
│   │       ├── TranslationsModule.js         # MODIFY (replace placeholder)
│   │       ├── components/
│   │       │   ├── DictionaryList.js         # CREATE - List/filter dictionaries
│   │       │   ├── DictionaryEditor.js       # CREATE - Create/edit dictionary form
│   │       │   ├── DictionaryDetail.js       # CREATE - View dictionary with mappings
│   │       │   ├── MappingTable.js           # CREATE - Token mappings table
│   │       │   ├── MappingEditor.js          # CREATE - Single mapping editor modal
│   │       │   ├── ValidationDashboard.js    # CREATE - Validation results display
│   │       │   ├── CoverageWidget.js         # CREATE - Coverage visualization
│   │       │   └── ImportExportPanel.js      # CREATE - Bulk operations UI
│   │       └── index.js                      # CREATE - Module exports
│   ├── services/
│   │   └── translation-service.js            # CREATE - API wrapper service
│   └── stores/
│       └── translation-store.js              # CREATE - Translation state management
```

### Files to MODIFY

```
/home/overbits/dss/admin-ui/
├── js/
│   ├── core/
│   │   └── router.js                         # Already has /translations route
│   ├── stores/
│   │   └── context-store.js                  # Add translations context prompt
│   └── modules/
│       └── translations/
│           └── TranslationsModule.js         # Replace placeholder implementation
```

---

## 2. Component Architecture

### 2.1 Component Hierarchy

```
TranslationsModule (Main Container)
├── DictionaryList (Left Panel - List View)
│   ├── Search/Filter Bar
│   ├── Dictionary Card (repeated)
│   │   ├── Status Badge
│   │   ├── Coverage Indicator
│   │   └── Action Buttons
│   └── Create Dictionary Button
│
├── DictionaryDetail (Center Panel - Selected Dictionary)
│   ├── Header Section
│   │   ├── Title/Description
│   │   ├── Status Badge
│   │   └── Action Bar (Edit, Validate, Export)
│   ├── CoverageWidget
│   ├── MappingTable
│   │   ├── Column Headers
│   │   ├── Mapping Rows (virtualized if large)
│   │   └── Pagination
│   └── ValidationDashboard (collapsible)
│
├── DictionaryEditor (Modal)
│   ├── Form Fields
│   │   ├── Name (required)
│   │   ├── Description
│   │   ├── Source System
│   │   ├── Target System
│   │   ├── Tags
│   │   └── Status
│   └── Save/Cancel Buttons
│
├── MappingEditor (Modal)
│   ├── Source Token Input
│   ├── Target Token Input
│   ├── Transform Rule Builder
│   ├── Confidence Score
│   ├── Notes
│   └── Validated Checkbox
│
└── ImportExportPanel (Slide-over)
    ├── Import Tab
    │   ├── File Drop Zone
    │   ├── JSON Preview
    │   └── Import Button
    └── Export Tab
        ├── Format Selection
        └── Download Button
```

### 2.2 Data Flow

```
┌─────────────────────────────────────────────────────────────────┐
│                      TranslationsModule                          │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │              translation-store.js (State)                    ││
│  │  - dictionaries: []                                         ││
│  │  - selectedDictionaryId: string                             ││
│  │  - selectedDictionary: object (with mappings)               ││
│  │  - validation: object                                       ││
│  │  - coverage: object                                         ││
│  │  - loading: { dictionaries, dictionary, validation, ... }   ││
│  │  - errors: {}                                               ││
│  │  - filters: { search, status, project }                     ││
│  └─────────────────────────────────────────────────────────────┘│
│           │                                                      │
│           ▼                                                      │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │           translation-service.js (API Layer)                 ││
│  │  - listDictionaries(filters)                                ││
│  │  - getDictionary(id)                                        ││
│  │  - createDictionary(data)                                   ││
│  │  - updateDictionary(id, data)                               ││
│  │  - deleteDictionary(id)                                     ││
│  │  - createMapping(dictionaryId, data)                        ││
│  │  - updateMapping(dictionaryId, mappingId, data)             ││
│  │  - deleteMapping(dictionaryId, mappingId)                   ││
│  │  - bulkImportMappings(dictionaryId, mappings)               ││
│  │  - validateDictionary(id)                                   ││
│  │  - getCoverage(id)                                          ││
│  │  - exportDictionary(id)                                     ││
│  └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
```

### 2.3 State Management Strategy

The module follows the existing patterns from `app-store.js`:

1. **Dedicated Store** (`translation-store.js`):
   - Extends EventTarget for reactive subscriptions
   - Request deduplication pattern (pendingRequests Map)
   - Loading/error state tracking per operation
   - Auto-sync with context-store for project filtering

2. **Component Subscriptions**:
   - Components subscribe to specific state keys
   - Cleanup unsubscribes in `disconnectedCallback()`
   - Use debouncing for search/filter inputs

---

## 3. Implementation Order

### Task 1: Create Translation Service (API Layer)
**Priority:** P0 - Foundation
**Effort:** 2 hours
**Dependencies:** None

Create `/js/services/translation-service.js`:

```javascript
/**
 * translation-service.js
 * API wrapper for Translation Dictionary endpoints
 */

import apiClient from './api-client.js';

class TranslationService {
  constructor() {
    this.baseUrl = '/translations';
  }

  // ========== Dictionary Operations ==========

  async listDictionaries(filters = {}) {
    const params = new URLSearchParams();
    if (filters.projectId) params.set('projectId', filters.projectId);
    if (filters.status) params.set('status', filters.status);
    if (filters.limit) params.set('limit', filters.limit);
    if (filters.offset) params.set('offset', filters.offset);

    const queryString = params.toString();
    const url = queryString ? `${this.baseUrl}?${queryString}` : this.baseUrl;
    return apiClient.request('GET', url);
  }

  async getDictionary(id) {
    return apiClient.request('GET', `${this.baseUrl}/${id}`);
  }

  async createDictionary(data) {
    return apiClient.request('POST', this.baseUrl, data);
  }

  async updateDictionary(id, data) {
    return apiClient.request('PUT', `${this.baseUrl}/${id}`, data);
  }

  async deleteDictionary(id) {
    return apiClient.request('DELETE', `${this.baseUrl}/${id}`);
  }

  // ========== Mapping Operations ==========

  async createMapping(dictionaryId, data) {
    return apiClient.request('POST', `${this.baseUrl}/${dictionaryId}/mappings`, data);
  }

  async updateMapping(dictionaryId, mappingId, data) {
    return apiClient.request('PUT', `${this.baseUrl}/${dictionaryId}/mappings/${mappingId}`, data);
  }

  async deleteMapping(dictionaryId, mappingId) {
    return apiClient.request('DELETE', `${this.baseUrl}/${dictionaryId}/mappings/${mappingId}`);
  }

  async bulkImportMappings(dictionaryId, mappings) {
    return apiClient.request('POST', `${this.baseUrl}/${dictionaryId}/mappings/bulk`, { mappings });
  }

  // ========== Validation & Analysis ==========

  async validateDictionary(id) {
    return apiClient.request('GET', `${this.baseUrl}/${id}/validate`);
  }

  async getCoverage(id) {
    return apiClient.request('GET', `${this.baseUrl}/${id}/coverage`);
  }

  async exportDictionary(id) {
    return apiClient.request('GET', `${this.baseUrl}/${id}/export`);
  }
}

export default new TranslationService();
```

---

### Task 2: Create Translation Store (State Management)
**Priority:** P0 - Foundation
**Effort:** 3 hours
**Dependencies:** Task 1

Create `/js/stores/translation-store.js`:

```javascript
/**
 * translation-store.js
 * Centralized state management for Translation Dictionaries
 */

import translationService from '../services/translation-service.js';
import contextStore from './context-store.js';

class TranslationStore extends EventTarget {
  constructor() {
    super();
    this.state = {
      // Data
      dictionaries: [],
      selectedDictionaryId: null,
      selectedDictionary: null,
      validation: null,
      coverage: null,

      // UI State
      filters: {
        search: '',
        status: null,
        projectId: null
      },
      pagination: {
        total: 0,
        limit: 50,
        offset: 0
      },

      // Loading states
      loading: {
        dictionaries: false,
        dictionary: false,
        validation: false,
        coverage: false,
        creating: false,
        updating: false,
        deleting: false,
        importing: false
      },

      // Errors
      errors: {}
    };

    this.pendingRequests = new Map();

    // Subscribe to project changes
    contextStore.subscribeToKey('projectId', (newProjectId) => {
      if (newProjectId) {
        this.setFilter('projectId', newProjectId);
        this.fetchDictionaries();
      }
    });
  }

  // === State Access ===

  get(key) {
    return key ? this.state[key] : this.state;
  }

  getState() {
    return { ...this.state };
  }

  // === State Updates ===

  set(updates) {
    const prevState = { ...this.state };
    this.state = { ...this.state, ...updates };
    this._notify(updates, prevState);
  }

  setLoading(key, loading = true) {
    this.set({
      loading: { ...this.state.loading, [key]: loading }
    });
  }

  setError(key, error) {
    this.set({
      errors: { ...this.state.errors, [key]: error }
    });
  }

  clearError(key) {
    const errors = { ...this.state.errors };
    delete errors[key];
    this.set({ errors });
  }

  setFilter(key, value) {
    this.set({
      filters: { ...this.state.filters, [key]: value }
    });
  }

  // === Subscriptions ===

  subscribe(callback) {
    const handler = (event) => callback(event.detail);
    this.addEventListener('state-change', handler);
    return () => this.removeEventListener('state-change', handler);
  }

  subscribeToKey(key, callback) {
    const handler = (event) => {
      const { changes } = event.detail;
      if (changes[key]) {
        callback(changes[key].newValue, changes[key].oldValue);
      }
    };
    this.addEventListener('state-change', handler);
    return () => this.removeEventListener('state-change', handler);
  }

  _notify(updates, prevState) {
    const changes = {};
    for (const key in updates) {
      if (JSON.stringify(prevState[key]) !== JSON.stringify(updates[key])) {
        changes[key] = { oldValue: prevState[key], newValue: updates[key] };
      }
    }

    if (Object.keys(changes).length > 0) {
      this.dispatchEvent(new CustomEvent('state-change', {
        detail: { state: this.state, changes }
      }));
    }
  }

  // === Dictionary Operations ===

  async fetchDictionaries() {
    const requestKey = 'dictionaries';

    if (this.pendingRequests.has(requestKey)) {
      return this.pendingRequests.get(requestKey);
    }

    const requestPromise = (async () => {
      this.setLoading('dictionaries', true);
      this.clearError('dictionaries');

      try {
        const { filters, pagination } = this.state;
        const result = await translationService.listDictionaries({
          projectId: filters.projectId,
          status: filters.status,
          limit: pagination.limit,
          offset: pagination.offset
        });

        this.set({
          dictionaries: result.dictionaries || [],
          pagination: {
            ...this.state.pagination,
            total: result.total || 0
          }
        });

        return result.dictionaries;
      } catch (error) {
        this.setError('dictionaries', error.message);
        throw error;
      } finally {
        this.setLoading('dictionaries', false);
        this.pendingRequests.delete(requestKey);
      }
    })();

    this.pendingRequests.set(requestKey, requestPromise);
    return requestPromise;
  }

  async selectDictionary(id) {
    if (!id) {
      this.set({
        selectedDictionaryId: null,
        selectedDictionary: null,
        validation: null,
        coverage: null
      });
      return;
    }

    this.set({ selectedDictionaryId: id });
    this.setLoading('dictionary', true);
    this.clearError('dictionary');

    try {
      const result = await translationService.getDictionary(id);
      this.set({ selectedDictionary: result.dictionary });

      // Auto-fetch coverage for selected dictionary
      this.fetchCoverage(id);

      return result.dictionary;
    } catch (error) {
      this.setError('dictionary', error.message);
      throw error;
    } finally {
      this.setLoading('dictionary', false);
    }
  }

  async createDictionary(data) {
    this.setLoading('creating', true);
    this.clearError('creating');

    try {
      const result = await translationService.createDictionary({
        ...data,
        projectId: this.state.filters.projectId || contextStore.get('projectId')
      });

      // Refresh list and select new dictionary
      await this.fetchDictionaries();
      await this.selectDictionary(result.dictionary.id);

      return result.dictionary;
    } catch (error) {
      this.setError('creating', error.message);
      throw error;
    } finally {
      this.setLoading('creating', false);
    }
  }

  async updateDictionary(id, data) {
    this.setLoading('updating', true);
    this.clearError('updating');

    try {
      const result = await translationService.updateDictionary(id, data);

      // Update in list
      this.set({
        dictionaries: this.state.dictionaries.map(d =>
          d.id === id ? result.dictionary : d
        ),
        selectedDictionary: this.state.selectedDictionaryId === id
          ? result.dictionary
          : this.state.selectedDictionary
      });

      return result.dictionary;
    } catch (error) {
      this.setError('updating', error.message);
      throw error;
    } finally {
      this.setLoading('updating', false);
    }
  }

  async deleteDictionary(id) {
    this.setLoading('deleting', true);
    this.clearError('deleting');

    try {
      await translationService.deleteDictionary(id);

      // Remove from list
      this.set({
        dictionaries: this.state.dictionaries.filter(d => d.id !== id),
        selectedDictionaryId: this.state.selectedDictionaryId === id ? null : this.state.selectedDictionaryId,
        selectedDictionary: this.state.selectedDictionaryId === id ? null : this.state.selectedDictionary
      });
    } catch (error) {
      this.setError('deleting', error.message);
      throw error;
    } finally {
      this.setLoading('deleting', false);
    }
  }

  // === Mapping Operations ===

  async createMapping(data) {
    const dictionaryId = this.state.selectedDictionaryId;
    if (!dictionaryId) throw new Error('No dictionary selected');

    this.setLoading('creating', true);
    this.clearError('creating');

    try {
      const result = await translationService.createMapping(dictionaryId, data);

      // Refresh selected dictionary to get updated mappings
      await this.selectDictionary(dictionaryId);

      return result.mapping;
    } catch (error) {
      this.setError('creating', error.message);
      throw error;
    } finally {
      this.setLoading('creating', false);
    }
  }

  async updateMapping(mappingId, data) {
    const dictionaryId = this.state.selectedDictionaryId;
    if (!dictionaryId) throw new Error('No dictionary selected');

    this.setLoading('updating', true);
    this.clearError('updating');

    try {
      const result = await translationService.updateMapping(dictionaryId, mappingId, data);

      // Update mapping in place
      if (this.state.selectedDictionary) {
        const mappings = this.state.selectedDictionary.Mappings || [];
        this.set({
          selectedDictionary: {
            ...this.state.selectedDictionary,
            Mappings: mappings.map(m => m.id === mappingId ? result.mapping : m)
          }
        });
      }

      return result.mapping;
    } catch (error) {
      this.setError('updating', error.message);
      throw error;
    } finally {
      this.setLoading('updating', false);
    }
  }

  async deleteMapping(mappingId) {
    const dictionaryId = this.state.selectedDictionaryId;
    if (!dictionaryId) throw new Error('No dictionary selected');

    this.setLoading('deleting', true);
    this.clearError('deleting');

    try {
      await translationService.deleteMapping(dictionaryId, mappingId);

      // Remove mapping from local state
      if (this.state.selectedDictionary) {
        const mappings = this.state.selectedDictionary.Mappings || [];
        this.set({
          selectedDictionary: {
            ...this.state.selectedDictionary,
            Mappings: mappings.filter(m => m.id !== mappingId)
          }
        });
      }
    } catch (error) {
      this.setError('deleting', error.message);
      throw error;
    } finally {
      this.setLoading('deleting', false);
    }
  }

  async bulkImportMappings(mappings) {
    const dictionaryId = this.state.selectedDictionaryId;
    if (!dictionaryId) throw new Error('No dictionary selected');

    this.setLoading('importing', true);
    this.clearError('importing');

    try {
      const result = await translationService.bulkImportMappings(dictionaryId, mappings);

      // Refresh selected dictionary
      await this.selectDictionary(dictionaryId);

      return result;
    } catch (error) {
      this.setError('importing', error.message);
      throw error;
    } finally {
      this.setLoading('importing', false);
    }
  }

  // === Validation & Analysis ===

  async fetchValidation(id = null) {
    const dictionaryId = id || this.state.selectedDictionaryId;
    if (!dictionaryId) return;

    this.setLoading('validation', true);
    this.clearError('validation');

    try {
      const result = await translationService.validateDictionary(dictionaryId);
      this.set({ validation: result.validation });
      return result.validation;
    } catch (error) {
      this.setError('validation', error.message);
      throw error;
    } finally {
      this.setLoading('validation', false);
    }
  }

  async fetchCoverage(id = null) {
    const dictionaryId = id || this.state.selectedDictionaryId;
    if (!dictionaryId) return;

    this.setLoading('coverage', true);
    this.clearError('coverage');

    try {
      const result = await translationService.getCoverage(dictionaryId);
      this.set({ coverage: result.coverage });
      return result.coverage;
    } catch (error) {
      this.setError('coverage', error.message);
      throw error;
    } finally {
      this.setLoading('coverage', false);
    }
  }

  async exportDictionary(id = null) {
    const dictionaryId = id || this.state.selectedDictionaryId;
    if (!dictionaryId) throw new Error('No dictionary selected');

    try {
      const result = await translationService.exportDictionary(dictionaryId);
      return result.export;
    } catch (error) {
      this.setError('export', error.message);
      throw error;
    }
  }
}

export default new TranslationStore();
```

---

### Task 3: Create Dictionary List Component
**Priority:** P1 - Core UI
**Effort:** 4 hours
**Dependencies:** Tasks 1, 2

Create `/js/modules/translations/components/DictionaryList.js`:

```javascript
/**
 * DictionaryList.js
 * List and filter translation dictionaries
 */

import { ComponentHelpers } from '../../../utils/component-helpers.js';
import translationStore from '../../../stores/translation-store.js';

class DictionaryList extends HTMLElement {
  constructor() {
    super();
    this.unsubscribe = null;
    this.searchDebounceTimer = null;
  }

  connectedCallback() {
    this.render();
    this.setupEventListeners();
    this.subscribeToStore();
  }

  disconnectedCallback() {
    if (this.unsubscribe) {
      this.unsubscribe();
    }
    if (this.searchDebounceTimer) {
      clearTimeout(this.searchDebounceTimer);
    }
  }

  subscribeToStore() {
    this.unsubscribe = translationStore.subscribe(({ state, changes }) => {
      if (changes.dictionaries || changes.loading || changes.selectedDictionaryId) {
        this.renderList();
      }
    });
  }

  setupEventListeners() {
    // Search input
    const searchInput = this.querySelector('#dictionary-search');
    if (searchInput) {
      searchInput.addEventListener('input', (e) => this.handleSearch(e.target.value));
    }

    // Status filter
    const statusFilter = this.querySelector('#status-filter');
    if (statusFilter) {
      statusFilter.addEventListener('change', (e) => {
        translationStore.setFilter('status', e.target.value || null);
        translationStore.fetchDictionaries();
      });
    }

    // Create button
    const createBtn = this.querySelector('#create-dictionary-btn');
    if (createBtn) {
      createBtn.addEventListener('click', () => {
        this.dispatchEvent(new CustomEvent('create-dictionary', {
          bubbles: true, composed: true
        }));
      });
    }
  }

  handleSearch(value) {
    if (this.searchDebounceTimer) {
      clearTimeout(this.searchDebounceTimer);
    }
    this.searchDebounceTimer = setTimeout(() => {
      translationStore.setFilter('search', value);
      // Note: Search is client-side filter for now
      this.renderList();
    }, 300);
  }

  handleSelectDictionary(id) {
    translationStore.selectDictionary(id);
    this.dispatchEvent(new CustomEvent('dictionary-selected', {
      detail: { id },
      bubbles: true, composed: true
    }));
  }

  renderList() {
    const container = this.querySelector('#dictionaries-container');
    if (!container) return;

    const state = translationStore.getState();
    const { dictionaries, loading, filters, selectedDictionaryId } = state;

    if (loading.dictionaries) {
      container.innerHTML = ComponentHelpers.renderLoading('Loading dictionaries...');
      return;
    }

    if (state.errors.dictionaries) {
      container.innerHTML = ComponentHelpers.renderError('Failed to load dictionaries', { message: state.errors.dictionaries });
      return;
    }

    // Apply client-side search filter
    let filtered = dictionaries;
    if (filters.search) {
      const search = filters.search.toLowerCase();
      filtered = dictionaries.filter(d =>
        d.name.toLowerCase().includes(search) ||
        (d.description && d.description.toLowerCase().includes(search))
      );
    }

    if (filtered.length === 0) {
      container.innerHTML = ComponentHelpers.renderEmpty(
        filters.search ? 'No dictionaries match your search' : 'No translation dictionaries yet',
        filters.search ? '🔍' : '📚'
      );
      return;
    }

    container.innerHTML = filtered.map(dict => this.renderDictionaryCard(dict, selectedDictionaryId === dict.id)).join('');

    // Add click handlers
    container.querySelectorAll('.dictionary-card').forEach(card => {
      card.addEventListener('click', () => {
        this.handleSelectDictionary(card.dataset.id);
      });
    });
  }

  renderDictionaryCard(dict, isSelected) {
    const statusColors = {
      draft: 'warning',
      active: 'success',
      archived: 'error'
    };

    const coverage = dict.metadata?.coverage || 0;
    const mappingCount = dict.mappingCount || 0;

    return `
      <div class="dictionary-card ${isSelected ? 'dictionary-card--selected' : ''}"
           data-id="${dict.id}"
           tabindex="0"
           role="button"
           aria-pressed="${isSelected}">
        <div class="dictionary-card__header">
          <h4 class="dictionary-card__title">${ComponentHelpers.escapeHtml(dict.name)}</h4>
          ${ComponentHelpers.createBadge(dict.status, statusColors[dict.status] || 'info')}
        </div>
        ${dict.description ? `
          <p class="dictionary-card__description">
            ${ComponentHelpers.truncateText(ComponentHelpers.escapeHtml(dict.description), 80)}
          </p>
        ` : ''}
        <div class="dictionary-card__meta">
          <span class="dictionary-card__stat">
            <span class="dictionary-card__stat-icon">🔗</span>
            ${mappingCount} mappings
          </span>
          <span class="dictionary-card__stat">
            <span class="dictionary-card__stat-icon">📊</span>
            ${coverage}% coverage
          </span>
        </div>
        <div class="dictionary-card__footer">
          <span class="dictionary-card__date">
            ${ComponentHelpers.formatRelativeTime(dict.updatedAt)}
          </span>
        </div>
      </div>
    `;
  }

  render() {
    this.innerHTML = `
      <style>
        .dictionary-list {
          display: flex;
          flex-direction: column;
          height: 100%;
          background: var(--vscode-sidebar);
          border-right: 1px solid var(--vscode-border);
        }

        .dictionary-list__header {
          padding: 16px;
          border-bottom: 1px solid var(--vscode-border);
        }

        .dictionary-list__title {
          font-size: 14px;
          font-weight: 600;
          margin-bottom: 12px;
        }

        .dictionary-list__filters {
          display: flex;
          flex-direction: column;
          gap: 8px;
        }

        .dictionary-list__search {
          width: 100%;
          padding: 6px 10px;
          font-size: 12px;
          border: 1px solid var(--vscode-input-border);
          border-radius: 4px;
          background: var(--vscode-input-background);
          color: var(--vscode-input-foreground);
        }

        .dictionary-list__status-filter {
          width: 100%;
          padding: 6px 10px;
          font-size: 12px;
          border: 1px solid var(--vscode-input-border);
          border-radius: 4px;
          background: var(--vscode-input-background);
          color: var(--vscode-input-foreground);
        }

        .dictionary-list__create-btn {
          width: 100%;
          padding: 8px 16px;
          font-size: 12px;
          font-weight: 500;
          background: var(--vscode-button-background);
          color: var(--vscode-button-foreground);
          border: none;
          border-radius: 4px;
          cursor: pointer;
          margin-top: 8px;
        }

        .dictionary-list__create-btn:hover {
          background: var(--vscode-button-hoverBackground);
        }

        .dictionary-list__content {
          flex: 1;
          overflow-y: auto;
          padding: 8px;
        }

        .dictionary-card {
          padding: 12px;
          margin-bottom: 8px;
          background: var(--vscode-editor-background);
          border: 1px solid var(--vscode-border);
          border-radius: 6px;
          cursor: pointer;
          transition: all 0.15s ease;
        }

        .dictionary-card:hover {
          border-color: var(--vscode-focusBorder);
        }

        .dictionary-card--selected {
          border-color: var(--vscode-focusBorder);
          background: var(--vscode-list-activeSelectionBackground);
        }

        .dictionary-card__header {
          display: flex;
          justify-content: space-between;
          align-items: flex-start;
          margin-bottom: 8px;
        }

        .dictionary-card__title {
          font-size: 13px;
          font-weight: 600;
          margin: 0;
          flex: 1;
        }

        .dictionary-card__description {
          font-size: 11px;
          color: var(--vscode-descriptionForeground);
          margin: 0 0 8px 0;
          line-height: 1.4;
        }

        .dictionary-card__meta {
          display: flex;
          gap: 12px;
          margin-bottom: 8px;
        }

        .dictionary-card__stat {
          display: flex;
          align-items: center;
          gap: 4px;
          font-size: 11px;
          color: var(--vscode-descriptionForeground);
        }

        .dictionary-card__stat-icon {
          font-size: 12px;
        }

        .dictionary-card__footer {
          font-size: 10px;
          color: var(--vscode-descriptionForeground);
        }
      </style>

      <div class="dictionary-list">
        <div class="dictionary-list__header">
          <h3 class="dictionary-list__title">Translation Dictionaries</h3>
          <div class="dictionary-list__filters">
            <input
              type="text"
              id="dictionary-search"
              class="dictionary-list__search"
              placeholder="Search dictionaries..."
            />
            <select id="status-filter" class="dictionary-list__status-filter">
              <option value="">All Status</option>
              <option value="draft">Draft</option>
              <option value="active">Active</option>
              <option value="archived">Archived</option>
            </select>
            <button id="create-dictionary-btn" class="dictionary-list__create-btn">
              + Create Dictionary
            </button>
          </div>
        </div>
        <div id="dictionaries-container" class="dictionary-list__content">
          ${ComponentHelpers.renderLoading('Loading dictionaries...')}
        </div>
      </div>
    `;
  }
}

customElements.define('dictionary-list', DictionaryList);
export default DictionaryList;
```

---

### Task 4: Create Dictionary Detail & Mapping Table Components
**Priority:** P1 - Core UI
**Effort:** 6 hours
**Dependencies:** Tasks 1-3

Create `/js/modules/translations/components/DictionaryDetail.js` and `/js/modules/translations/components/MappingTable.js`.

**DictionaryDetail.js** - Main detail view with header, action bar, and mapping table.

**MappingTable.js** - Table component for displaying and editing token mappings:
- Sortable columns (sourceToken, targetToken, validated, confidence)
- Inline validation toggle
- Edit/Delete actions per row
- Pagination for large datasets

---

### Task 5: Create Editor Modals (Dictionary & Mapping)
**Priority:** P1 - Core UI
**Effort:** 5 hours
**Dependencies:** Tasks 1-4

Create:
- `/js/modules/translations/components/DictionaryEditor.js` - Form for create/edit dictionary
- `/js/modules/translations/components/MappingEditor.js` - Modal for create/edit mapping

Key patterns:
- Form validation before submit
- Loading states on submit button
- Error display within modal
- Close on successful save
- ESC key to close

---

### Task 6: Create Validation Dashboard & Coverage Widget
**Priority:** P2 - Analysis Features
**Effort:** 4 hours
**Dependencies:** Tasks 1-4

Create:
- `/js/modules/translations/components/ValidationDashboard.js`
- `/js/modules/translations/components/CoverageWidget.js`

**CoverageWidget** - Visual percentage display:
- Circular progress indicator
- Color coding (green >80%, yellow >50%, red <50%)
- Total/Mapped/Unmapped token counts

**ValidationDashboard** - Expandable panel showing:
- Overall validation status
- Error list with line references
- Warning list
- Suggested fixes

---

### Task 7: Create Import/Export Panel
**Priority:** P2 - Bulk Operations
**Effort:** 4 hours
**Dependencies:** Tasks 1-5

Create `/js/modules/translations/components/ImportExportPanel.js`:
- Import: File drop zone, JSON validation, preview, conflict resolution
- Export: Format selection (JSON), download trigger
- Results summary (created/updated/errors)

---

### Task 8: Integrate TranslationsModule & Final Testing
**Priority:** P0 - Integration
**Effort:** 4 hours
**Dependencies:** All previous tasks

Update `/js/modules/translations/TranslationsModule.js`:

```javascript
/**
 * TranslationsModule.js
 * Main container for Translation Dictionary management (DSS Principle #2)
 */

import translationStore from '../../stores/translation-store.js';
import contextStore from '../../stores/context-store.js';
import { ComponentHelpers } from '../../utils/component-helpers.js';

// Import components
import './components/DictionaryList.js';
import './components/DictionaryDetail.js';
import './components/DictionaryEditor.js';
import './components/MappingEditor.js';
import './components/ValidationDashboard.js';
import './components/CoverageWidget.js';
import './components/ImportExportPanel.js';

class TranslationsModule extends HTMLElement {
  constructor() {
    super();
    this.unsubscribe = [];
    this.modals = {
      dictionaryEditor: null,
      mappingEditor: null,
      importExport: null
    };
  }

  connectedCallback() {
    this.render();
    this.setupEventListeners();
    this.loadInitialData();
  }

  disconnectedCallback() {
    this.unsubscribe.forEach(fn => fn());
    this.unsubscribe = [];
  }

  async loadInitialData() {
    // Get current project from context
    const projectId = contextStore.get('projectId');
    if (projectId) {
      translationStore.setFilter('projectId', projectId);
      await translationStore.fetchDictionaries();
    }
  }

  setupEventListeners() {
    // Listen for create dictionary request
    this.addEventListener('create-dictionary', () => this.openDictionaryEditor());

    // Listen for edit dictionary request
    this.addEventListener('edit-dictionary', (e) => this.openDictionaryEditor(e.detail.dictionary));

    // Listen for dictionary selection
    this.addEventListener('dictionary-selected', (e) => {
      const detailPanel = this.querySelector('dictionary-detail');
      if (detailPanel) {
        detailPanel.setAttribute('dictionary-id', e.detail.id);
      }
    });

    // Listen for create mapping request
    this.addEventListener('create-mapping', () => this.openMappingEditor());

    // Listen for edit mapping request
    this.addEventListener('edit-mapping', (e) => this.openMappingEditor(e.detail.mapping));

    // Listen for import/export request
    this.addEventListener('open-import-export', () => this.openImportExport());

    // Subscribe to project changes
    this.unsubscribe.push(
      contextStore.subscribeToKey('projectId', (projectId) => {
        if (projectId) {
          translationStore.setFilter('projectId', projectId);
          translationStore.fetchDictionaries();
        }
      })
    );
  }

  openDictionaryEditor(dictionary = null) {
    // Implementation: Open modal or slide-over for dictionary creation/editing
    const editor = document.createElement('dictionary-editor');
    if (dictionary) {
      editor.setAttribute('dictionary-id', dictionary.id);
    }
    this.appendChild(editor);
  }

  openMappingEditor(mapping = null) {
    const editor = document.createElement('mapping-editor');
    if (mapping) {
      editor.setAttribute('mapping-id', mapping.id);
    }
    this.appendChild(editor);
  }

  openImportExport() {
    const panel = document.createElement('import-export-panel');
    this.appendChild(panel);
  }

  render() {
    const hasProject = contextStore.hasProject();

    this.innerHTML = `
      <style>
        .translations-module {
          display: flex;
          height: 100%;
          background: var(--vscode-editor-background);
        }

        .translations-module__sidebar {
          width: 320px;
          min-width: 280px;
          max-width: 400px;
          flex-shrink: 0;
        }

        .translations-module__main {
          flex: 1;
          overflow: hidden;
          display: flex;
          flex-direction: column;
        }

        .translations-module__empty {
          display: flex;
          flex-direction: column;
          align-items: center;
          justify-content: center;
          height: 100%;
          padding: 48px;
          text-align: center;
        }

        .translations-module__empty-icon {
          font-size: 64px;
          margin-bottom: 24px;
        }

        .translations-module__empty-title {
          font-size: 20px;
          font-weight: 600;
          margin-bottom: 12px;
        }

        .translations-module__empty-description {
          font-size: 14px;
          color: var(--vscode-descriptionForeground);
          max-width: 400px;
          line-height: 1.6;
        }

        .no-project-warning {
          display: flex;
          flex-direction: column;
          align-items: center;
          justify-content: center;
          height: 100%;
          padding: 48px;
          text-align: center;
        }
      </style>

      ${hasProject ? `
        <div class="translations-module">
          <div class="translations-module__sidebar">
            <dictionary-list></dictionary-list>
          </div>
          <div class="translations-module__main">
            <dictionary-detail></dictionary-detail>
          </div>
        </div>
      ` : `
        <div class="no-project-warning">
          <div style="font-size: 64px; margin-bottom: 24px;">📁</div>
          <h2 style="font-size: 20px; font-weight: 600; margin-bottom: 12px;">No Project Selected</h2>
          <p style="font-size: 14px; color: var(--vscode-descriptionForeground); max-width: 400px;">
            Please select a project from the header to manage translation dictionaries.
            Translation dictionaries map tokens between design systems.
          </p>
        </div>
      `}
    `;
  }
}

customElements.define('dss-translations-module', TranslationsModule);
export default TranslationsModule;
```

---

## 4. UI/UX Design Patterns

### 4.1 Layout Approach

**Master-Detail Pattern** (similar to VS Code):
- Left sidebar: Dictionary list (320px fixed)
- Center: Selected dictionary detail (flexible)
- Right: Contextual panels (validation, import/export) as slide-overs

### 4.2 User Workflows

**Workflow 1: Create New Dictionary**
1. User clicks "+ Create Dictionary" button
2. Modal opens with form
3. Fill: Name (required), Description, Source/Target System, Tags
4. Click "Create" -> Dictionary created and selected
5. Empty mapping table shown with "+ Add Mapping" CTA

**Workflow 2: Add Token Mappings**
1. Select dictionary from list
2. Click "+ Add Mapping" in mapping table
3. Modal opens: Source Token, Target Token, Transform Rule (optional)
4. Click "Save" -> Mapping added to table
5. Coverage widget updates automatically

**Workflow 3: Bulk Import Mappings**
1. Select dictionary
2. Click "Import" button in action bar
3. Slide-over opens with file drop zone
4. Drop/select JSON file
5. Preview shows parsed mappings
6. Click "Import" -> Progress shown
7. Results summary: X created, Y updated, Z errors

**Workflow 4: Validate Dictionary**
1. Select dictionary
2. Click "Validate" button
3. Loading indicator in validation dashboard
4. Results shown: errors, warnings, suggestions
5. Click error to scroll to mapping

### 4.3 Error Handling

```javascript
// Standard error display pattern
const handleApiError = async (operation, callback) => {
  try {
    await callback();
    ComponentHelpers.showToast?.(`${operation} successful`, 'success');
  } catch (error) {
    ComponentHelpers.showToast?.(`${operation} failed: ${error.message}`, 'error');
    // Errors are also stored in translationStore.state.errors
  }
};
```

### 4.4 Loading States

Each operation has dedicated loading state:
- `loading.dictionaries` - List loading
- `loading.dictionary` - Detail loading
- `loading.validation` - Validation running
- `loading.creating` - Create operation
- `loading.updating` - Update operation
- `loading.deleting` - Delete operation
- `loading.importing` - Bulk import

Components check these states and render appropriate UI:
- Skeleton loaders for lists
- Spinner overlay for actions
- Disabled buttons during operations

---

## 5. Integration Points

### 5.1 Navigation Access

The route `/translations` is already registered in `/js/core/router.js`:

```javascript
{
  path: '/translations',
  name: 'Translations',
  handler: () => this.loadModule('dss-translations-module', () => import('../modules/translations/TranslationsModule.js'))
}
```

Access via:
- Direct URL: `#translations`
- Navigation item in sidebar (if configured)
- Router navigation: `router.navigate('translations')`

### 5.2 Context Store Integration

Add translations context prompt in `/js/stores/context-store.js`:

```javascript
const PAGE_CONTEXT_PROMPTS = {
  // ... existing prompts ...
  translations: 'You are helping manage translation dictionaries that map tokens between design systems. The user can create dictionaries, add token mappings, validate coverage, and import/export mappings.'
};
```

### 5.3 Project Context Sync

Translation dictionaries are project-scoped. The module:
1. Reads `projectId` from `contextStore` on mount
2. Subscribes to `projectId` changes
3. Filters dictionaries by current project
4. Passes `projectId` when creating new dictionaries

### 5.4 Toast Notifications

Use existing notification system:

```javascript
import { ComponentHelpers } from '../utils/component-helpers.js';

// Success
ComponentHelpers.showToast?.('Dictionary created successfully', 'success');

// Error
ComponentHelpers.showToast?.('Failed to create dictionary', 'error');

// Info
ComponentHelpers.showToast?.('Import in progress...', 'info');
```

---

## 6. Technical Decisions

### 6.1 Reusable Components

**Components to create that may be reused:**

1. **Modal** - Generic modal wrapper (if not existing)
2. **ConfirmDialog** - Confirmation before destructive actions
3. **FileDropZone** - Drag-and-drop file upload area
4. **ProgressRing** - Circular progress indicator for coverage
5. **DataTable** - Sortable, paginated table (extend for mappings)

### 6.2 API Calling Patterns

Follow existing patterns from `app-store.js`:

```javascript
// Request deduplication
async fetchDictionaries() {
  const requestKey = 'dictionaries';

  // Return existing promise if in flight
  if (this.pendingRequests.has(requestKey)) {
    return this.pendingRequests.get(requestKey);
  }

  const requestPromise = (async () => {
    try {
      // ... API call
    } finally {
      this.pendingRequests.delete(requestKey);
    }
  })();

  this.pendingRequests.set(requestKey, requestPromise);
  return requestPromise;
}
```

### 6.3 Form Validation

Client-side validation before API calls:

```javascript
const validateDictionaryForm = (data) => {
  const errors = {};

  if (!data.name || data.name.trim().length < 3) {
    errors.name = 'Name must be at least 3 characters';
  }

  if (data.name && data.name.length > 255) {
    errors.name = 'Name must be less than 255 characters';
  }

  return {
    isValid: Object.keys(errors).length === 0,
    errors
  };
};
```

### 6.4 Keyboard Navigation

Implement for accessibility:
- Tab through list items
- Enter to select
- Arrow keys in list
- ESC to close modals
- Focus trap in modals

---

## 7. Testing Checklist

### Unit Tests
- [ ] TranslationService API methods
- [ ] TranslationStore state management
- [ ] Form validation functions
- [ ] ComponentHelpers utilities

### Integration Tests
- [ ] Create dictionary flow
- [ ] Add/edit/delete mapping flow
- [ ] Bulk import flow
- [ ] Validation flow
- [ ] Export flow

### E2E Tests
- [ ] Full workflow: Create dictionary -> Add mappings -> Validate -> Export
- [ ] Error handling: Invalid data, network errors
- [ ] Responsive behavior

---

## 8. Success Criteria

1. **Functional Requirements:**
   - [ ] Can list, filter, and search dictionaries
   - [ ] Can create/edit/delete dictionaries
   - [ ] Can add/edit/delete token mappings
   - [ ] Can bulk import mappings from JSON
   - [ ] Can validate dictionary mappings
   - [ ] Can view coverage statistics
   - [ ] Can export dictionary as JSON

2. **Non-Functional Requirements:**
   - [ ] Loads in < 2s on initial page visit
   - [ ] Responsive updates after actions (< 500ms)
   - [ ] Proper error handling and user feedback
   - [ ] Accessible (keyboard navigation, ARIA labels)
   - [ ] Works with existing project context

3. **Code Quality:**
   - [ ] Follows existing codebase patterns
   - [ ] No console errors in production
   - [ ] Clean separation of concerns (service/store/components)

---

## Appendix A: Backend API Reference

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/api/translations` | GET | List dictionaries (pagination, filters) |
| `/api/translations/:id` | GET | Get dictionary with mappings |
| `/api/translations` | POST | Create dictionary |
| `/api/translations/:id` | PUT | Update dictionary |
| `/api/translations/:id` | DELETE | Archive dictionary |
| `/api/translations/:id/mappings` | POST | Create mapping |
| `/api/translations/:id/mappings/:mappingId` | PUT | Update mapping |
| `/api/translations/:id/mappings/:mappingId` | DELETE | Delete mapping |
| `/api/translations/:id/mappings/bulk` | POST | Bulk import mappings |
| `/api/translations/:id/validate` | GET | Run validation |
| `/api/translations/:id/coverage` | GET | Calculate coverage |
| `/api/translations/:id/export` | GET | Export dictionary |

---

## Appendix B: Data Models

### TranslationDictionary
```typescript
interface TranslationDictionary {
  id: string;           // UUID
  name: string;         // 3-255 chars
  description: string | null;
  projectId: string;    // UUID
  createdBy: string;    // UUID
  status: 'draft' | 'active' | 'archived';
  version: number;
  metadata: {
    sourceSystem: string | null;
    targetSystem: string | null;
    coverage: number;
    validationStatus: 'pending' | 'valid' | 'invalid';
    lastValidated: string | null;
    tags: string[];
  };
  createdAt: string;
  updatedAt: string;
  Mappings?: TranslationMapping[];
}
```

### TranslationMapping
```typescript
interface TranslationMapping {
  id: string;           // UUID
  dictionaryId: string; // UUID
  sourceToken: string;
  targetToken: string;
  transformRule: object | null;
  validated: boolean;
  confidence: number;   // 0-1
  notes: string | null;
  createdAt: string;
  updatedAt: string;
}
```

---

**End of Implementation Plan**
