# HTML Data Mapping Reference (references/html-data-mapping.md)

> **Used by:** step-05b-deploy (Deploy Interactive HTML), step-03d-validate (incremental HTML)
> **Purpose:** Exact mapping from feature.json to HTML FEATURE_DATA and EMBEDDED_ARTIFACTS objects

---

## FEATURE_DATA Mapping

Build a JSON object following this **exact mapping** from feature.json to the HTML data model:

```javascript
{
  metadata: {
    applicationName: master.metadata.application,       // e.g. "RH"
    applicationId: master.id,                           // e.g. "FEAT-001"
    version: master.version,                            // e.g. "1.0"
    createdAt: master.metadata.createdAt,
    lastModified: master.metadata.updatedAt,
    analysisMode: master.metadata.analysisMode || "interactive"  // always "interactive"
  },
  cadrage: {
    // CONTEXT SECTION — lean format (merged from problem/asIs/toBe)
    // NOTE: 01-data-init.js has backward compat for old problem/current/vision format
    context: {
      problem: master.cadrage.problem || "",             // flat string from feature.json
      trigger: master.cadrage.trigger || "",              // flat string from feature.json
      currentSituation: master.cadrage.asIs || "",        // flat string from feature.json
      desiredSituation: master.cadrage.toBe || "",        // flat string from feature.json
      painPoints: (master.cadrage.stakeholders || [])
        .flatMap(s => s.painPoints || []).join("\n"),     // aggregate all painPoints
      acceptanceCriteria: (master.cadrage.acceptanceCriteria || [])
        .map(ac => ac.criterion).join("\n")               // AC → newline-separated string
    },
    stakeholders: master.cadrage.stakeholders.map(s => ({
      role: s.role,
      function: s.function || "",
      tasks: s.tasks || [],
      frequency: mapFrequency(s.frequency),             // "Quotidien"→"daily", etc.
      access: mapAccess(s.involvement),                 // "decision-maker"→"admin", "end-user"→"contributor"
      frustrations: (s.painPoints || []).join("\n")
    })),
    scope: {
      vital: (master.cadrage.globalScope.mustHave || [])
        .map(item => ({ name: item, description: "" })),      // string[] → {name,description}[]
      important: (master.cadrage.globalScope.shouldHave || [])
        .map(item => ({ name: item, description: "" })),
      optional: (master.cadrage.globalScope.couldHave || [])
        .map(item => ({ name: item, description: "" })),
      excluded: (master.cadrage.globalScope.outOfScope || [])
        .map(item => ({ name: item, description: "" }))
    },
    risks: (master.cadrage.risks || []).map(r => ({
      description: r.description,
      probability: r.probability,                       // "high" | "medium" | "low"
      impact: r.impact,
      mitigation: r.mitigation || ""
    })),
    criteria: (master.cadrage.acceptanceCriteria || []).map(ac => ({
      text: ac.criterion,
      validated: ac.validated || false
    }))
  },
  modules: master.modules.map(m => ({
    code: m.code,
    name: m.name || m.code,                             // display name (MANDATORY, separate from code)
    description: m.description || "",
    featureType: m.featureType || "data-centric",
    priority: m.priority || "must",
    entities: m.entities || [],
    anticipatedSections: m.anticipatedSections || [],    // [{code, description, resources[]}]
    dependencies: m.dependencies || [],
    dependents: m.dependents || [],
    estimatedComplexity: m.estimatedComplexity || "medium",
    status: m.status || "handed-off"
  })),
  dependencies: (master.dependencyGraph?.edges || []).map(e => ({
    from: e.from,
    to: e.to,
    description: e.description || ""
  })),
  moduleSpecs: {
    // FOR EACH module: read module feature.json, then map:
    // [moduleCode]: { useCases, businessRules, entities, permissions, notes, mockupNotes }
  },
  consolidation: {
    interactions: (master.consolidation?.crossModuleInteractions || []).map(i => ({
      from: i.fromModule,
      to: i.toModule,
      description: i.description || ""
    })),
    e2eFlows: (master.consolidation?.e2eFlows || []).map(f => ({
      name: f.name,
      steps: (f.steps || []).map(s => ({ module: s.module, action: s.action })),
      actors: (f.steps || []).map(s => s.permission).join(", ")
    }))
  },
  handoff: master.handoff || {}
}
```

### Module Specs Mapping

For EACH module in `master.modules[]`:

1. Read the module feature.json at `master.modules[i].featureJsonPath`
2. Map to `moduleSpecs[moduleCode]`:

```javascript
moduleSpecs[moduleCode] = {
  useCases: (moduleFeature.specification?.useCases || []).map(uc => ({
    name: uc.name,
    actor: uc.primaryActor,
    steps: (uc.mainScenario || []).join("\n"),           // array → newline-separated string
    alternative: (uc.alternativeScenarios || [])
      .map(a => a.name + ": " + (a.steps || []).join(", ")).join("\n")
  })),
  businessRules: (moduleFeature.analysis?.businessRules || []).map(br => ({
    name: br.name,
    category: br.category,                              // "validation"|"calculation"|"workflow"|"security"|"data"
    statement: br.statement,
    example: (br.examples || []).map(e => e.input + " → " + e.expected).join("; ")
  })),
  entities: (moduleFeature.analysis?.entities || []).map(ent => ({
    name: ent.name,
    description: ent.description || "",
    attributes: (ent.attributes || []).map(a => ({
      name: a.name,
      description: a.description || ""
    })),
    relationships: (ent.relationships || []).map(r =>
      r.target + " (" + r.type + ") - " + (r.description || "")
    )
  })),
  permissions: buildPermissionKeys(moduleFeature),      // see below
  notes: "",
  mockupNotes: ""  // Deprecated: wireframes now embedded separately in EMBEDDED_ARTIFACTS
}
```

### Permission Keys Function

The HTML uses `"Role|Action"` format (e.g. `"RH Admin|Consulter"`):

```javascript
function buildPermissionKeys(moduleFeature) {
  const keys = [];
  const matrix = moduleFeature.specification?.permissionMatrix;
  if (!matrix) return keys;
  const actionMap = { read: "Consulter", create: "Creer", update: "Modifier",
                      delete: "Supprimer", validate: "Valider", export: "Exporter",
                      submit: "Valider", import: "Creer" };
  (matrix.roleAssignments || []).forEach(ra => {
    (ra.permissions || []).forEach(permPath => {
      const action = permPath.split(".").pop();         // last segment = action
      if (action === "*") {
        // Wildcard: expand to all standard actions
        Object.values(actionMap).forEach(uiAction => {
          const key = ra.role + "|" + uiAction;
          if (!keys.includes(key)) keys.push(key);
        });
      } else {
        const uiAction = actionMap[action] || action;
        keys.push(ra.role + "|" + uiAction);
      }
    });
  });
  return keys;
}
```

### Frequency Mapping

```
"Quotidien" → "daily", "Hebdomadaire" → "weekly", "Mensuel" → "monthly"
Default: "daily"
```

### Access Mapping (involvement → access)

```
"decision-maker" → "admin", "end-user" with manager-like tasks → "manager"
"end-user" → "contributor", "observer" → "viewer"
Default: "contributor"
```

---

## EMBEDDED_ARTIFACTS Mapping

> **FIELD RENAME WARNING:** feature.json stores wireframes with `mockupFormat` and `mockup` fields.
> The HTML renderer reads `format` and `content`. You MUST rename: `mockupFormat` → `format`, `mockup` → `content`.
> Failure to rename = empty mockup display in the browser.

Build a JSON object containing ALL visual artifacts from feature.json:

```javascript
{
  wireframes: {
    // FOR EACH module: extract all wireframes from module feature.json
    // [moduleCode]: [ {screen, format, content, elements, componentMapping, layout, description} ]
  },
  e2eFlows: [
    // Extract from master consolidation.e2eFlows[]
    // { name, diagram, steps, actors }
  ],
  dependencyGraph: {
    // Extract from master dependencyGraph
    // nodes: [ {id, label, type} ], edges: [ {from, to, description} ]
  }
}
```

### Wireframes Mapping

For EACH module in `master.modules[]`:

```javascript
// Read the module feature.json
const moduleFeature = readModuleFeature(moduleCode);
const wireframes = (moduleFeature.specification?.uiWireframes || []).map(wf => ({
  screen: wf.screen,                                    // e.g. "UM-list", "UM-form"
  section: wf.section,                                  // e.g. "list", "form"
  format: wf.mockupFormat || "ascii",                   // "ascii" | "svg"
  content: wf.mockup,                                   // ASCII art or SVG markup
  description: wf.description || "",
  elements: wf.elements || [],                          // ["DataGrid", "FilterBar", ...]
  actions: wf.actions || [],                            // ["filter", "sort", "create", ...]
  componentMapping: wf.componentMapping || [],          // [{ wireframeElement, reactComponent }]
  layout: wf.layout || null,                            // { type, regions: [...] }
  permissionsRequired: wf.permissionsRequired || []
}));

// Store in artifacts object
EMBEDDED_ARTIFACTS.wireframes[moduleCode] = wireframes;
```

### E2E Flows Mapping

From master consolidation:

```javascript
EMBEDDED_ARTIFACTS.e2eFlows = (master.consolidation?.e2eFlows || []).map(flow => ({
  name: flow.name,
  diagram: generateE2EDiagram(flow),                    // ASCII diagram from flow.steps[]
  steps: flow.steps || [],                              // [{ module, action, permission }]
  actors: Array.from(new Set((flow.steps || [])
    .map(s => s.permission?.split(".")[0])              // Extract role from permission path
    .filter(Boolean))).join(", "),
  modules: Array.from(new Set((flow.steps || [])
    .map(s => s.module))).join(" → ")
}));

function generateE2EDiagram(flow) {
  // Generate ASCII diagram from flow.steps[]
  // Example output:
  // "Customer ──[read]──→ Order ──[create]──→ Invoice ──[send]──→"
  // "  (Orders)           (Orders)            (Invoices)"
  // "  Contributor        Manager             System"

  const stepDiagrams = (flow.steps || []).map(s =>
    `${s.action}(${s.module})`
  ).join(" ──→ ");

  return stepDiagrams;
}
```

### Dependency Graph Mapping

From master dependencyGraph:

```javascript
EMBEDDED_ARTIFACTS.dependencyGraph = {
  nodes: (master.modules || []).map(m => ({
    id: m.code,
    label: m.code,
    type: m.featureType || "data-centric"
  })),
  edges: (master.dependencyGraph?.edges || []).map(e => ({
    from: e.from,
    to: e.to,
    description: e.description || ""
  }))
};
```

---

## Usage in step-05b-deploy

1. Serialize the FEATURE_DATA object as JSON (with 2-space indentation for readability)
2. Serialize the EMBEDDED_ARTIFACTS object as JSON (with 2-space indentation)
3. Replace `{{FEATURE_DATA}}` with the serialized FEATURE_DATA JSON
4. Replace `{{EMBEDDED_ARTIFACTS}}` with the serialized EMBEDDED_ARTIFACTS JSON
5. Replace `{{APPLICATION_NAME}}` → `{application_name}` (still used in `<title>` and header)
6. Replace `{{APPLICATION_ID}}` → `{feature_id}` (still used in `APP_KEY`)
7. Replace `{{VERSION}}` → `{version}`
8. Replace `{{CREATED_AT}}` → `{ISO timestamp}`
