Consistency pass over the parallel-agent work: - Replace 4 divergent inline header wrappers (flex/inline-flex, gap 10/12px, margin-top present/absent across 8 tool pages) with one shared .dt-tool-header-actions class; strip the now-redundant per-button margin-top:0. Every tool header now aligns the local-first pill + Help button identically. - Reconcile downloads row: reorder to the page's exceptions-first order (Review, Unmatched left, Unmatched right, Matched) to match the tabs and metric strip, and drop the lone competing primary — the four are parallel exports of equal weight. Audited and confirmed already-consistent: compact intake banner, privacy pill markup, .dt-next-step strips, the three coming-soon stubs, primary CTAs, and the 3-download CSV/audit/config pattern. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
222 lines
13 KiB
HTML
222 lines
13 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Layout review — Map Columns</title>
|
|
<link rel="stylesheet" href="app.css">
|
|
</head>
|
|
<body data-page="05_column_mapper">
|
|
<div class="dt-app">
|
|
<aside class="dt-sidebar" id="dt-sidebar"></aside>
|
|
<main class="dt-main">
|
|
<div class="dt-review-banner">
|
|
<span class="dt-mi">visibility</span>
|
|
<span>Static layout preview of <strong>Map Columns</strong>, shown with a file imported, an interactive target schema + mapping configured, and a completed run (results + mapped preview). <a href="index.html">All pages →</a></span>
|
|
</div>
|
|
<div class="dt-main-inner">
|
|
|
|
<!-- Tool header -->
|
|
<div class="dt-tool-header">
|
|
<h1>Map Columns</h1>
|
|
<div class="dt-tool-header-actions">
|
|
<span class="dt-privacy-pill">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
<rect x="4" y="11" width="16" height="10" rx="2"/>
|
|
<path d="M8 11V7a4 4 0 018 0v4"/>
|
|
</svg>
|
|
Runs 100% locally
|
|
</span>
|
|
<button class="dt-help-btn"><span class="dt-mi">help_outline</span> Help</button>
|
|
</div>
|
|
</div>
|
|
<p class="dt-tool-caption">Rename columns, change their order, and set each one as text, number, or date.</p>
|
|
|
|
<div class="dt-spacer"></div>
|
|
|
|
<!-- File pickup banner (using file from upload screen) -->
|
|
<div class="dt-alert info">
|
|
<span class="dt-mi">description</span>
|
|
<span>Using <strong>crm_contacts_raw.csv</strong> from the upload screen.</span>
|
|
</div>
|
|
<button class="dt-btn" style="margin-bottom:4px">Use a different file</button>
|
|
|
|
<!-- Preview expander (collapsed after a result exists) -->
|
|
<details class="dt-expander">
|
|
<summary>Preview: crm_contacts_raw.csv</summary>
|
|
<div class="dt-expander-body">
|
|
<p class="dt-caption">4,210 rows, 6 columns</p>
|
|
<div class="dt-table-wrap">
|
|
<table class="dt-table">
|
|
<thead><tr><th class="idx"></th><th>Full Name</th><th>EmailAddr</th><th>Phone #</th><th>Signup</th><th>Amount Spent</th><th>Notes</th></tr></thead>
|
|
<tbody>
|
|
<tr><td class="idx">0</td><td>Jane Doe</td><td>jane@acme.io</td><td>512-555-0190</td><td>01/04/2024</td><td>$1,204.50</td><td>VIP</td></tr>
|
|
<tr><td class="idx">1</td><td>Bob Smith</td><td>bob@globex.com</td><td>720-555-7781</td><td>02/11/2024</td><td>$88.00</td><td></td></tr>
|
|
<tr><td class="idx">2</td><td>Carla Reyes</td><td>carla@initech.net</td><td>415-555-3322</td><td>03/02/2024</td><td>$612.10</td><td>renewal</td></tr>
|
|
<tr><td class="idx">3</td><td>Dev Patel</td><td>dev@umbrella.co</td><td>206-555-9043</td><td>03/19/2024</td><td>$0.00</td><td></td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</details>
|
|
|
|
<hr class="dt-divider">
|
|
|
|
<!-- Options expander (open — heart of the tool) -->
|
|
<details class="dt-expander" open>
|
|
<summary>Options</summary>
|
|
<div class="dt-expander-body">
|
|
|
|
<!-- ===== Target schema ===== -->
|
|
<h3 style="margin-top:0">Target schema</h3>
|
|
<div class="dt-field">
|
|
<label class="dt-label">How would you like to define the target schema?</label>
|
|
<div class="dt-radio-row" style="flex-direction:column; gap:8px">
|
|
<span class="dt-radio on"><span class="dot"></span> Build interactively (start from current columns)</span>
|
|
<span class="dt-radio"><span class="dot"></span> Import schema JSON</span>
|
|
<span class="dt-radio"><span class="dot"></span> Skip (rename / convert types only — no schema)</span>
|
|
</div>
|
|
<div class="dt-help-text">An interactive build is fastest for one-off cleanup. Import a JSON when you have a fixed contract (a CRM import format, db schema). Skip when you only want to rename or convert the type of specific columns.</div>
|
|
</div>
|
|
|
|
<p class="dt-caption">Edit the table to define your target schema. Add rows for fields the input doesn't have yet (with a default), or remove rows for columns you want to drop.</p>
|
|
|
|
<!-- Schema editor (st.data_editor, num_rows=dynamic) -->
|
|
<div class="dt-table-wrap">
|
|
<table class="dt-table">
|
|
<thead><tr><th>Target name</th><th>Type</th><th>Required</th><th>Default (for added cols)</th><th>Aliases (comma-sep, helps fuzzy-match)</th></tr></thead>
|
|
<tbody>
|
|
<tr><td>full_name</td><td>string</td><td>✗</td><td></td><td>Full Name, name</td></tr>
|
|
<tr><td>email</td><td>string</td><td>✓</td><td></td><td>EmailAddr, email_address</td></tr>
|
|
<tr><td>phone</td><td>string</td><td>✗</td><td></td><td>Phone #, tel</td></tr>
|
|
<tr><td>signup_date</td><td>date</td><td>✗</td><td></td><td>Signup</td></tr>
|
|
<tr><td>amount_spent</td><td>float</td><td>✗</td><td>0.0</td><td>Amount Spent</td></tr>
|
|
<tr><td>source</td><td>string</td><td>✗</td><td>crm-import</td><td></td></tr>
|
|
<tr><td style="color:var(--ink-tertiary)"><span class="dt-mi" style="font-size:16px;vertical-align:-3px">add</span> add row</td><td></td><td></td><td></td><td></td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<p class="dt-caption">6 target fields · 1 added field (<code>source</code>) not present in the input.</p>
|
|
|
|
<hr class="dt-divider">
|
|
|
|
<!-- ===== Mapping ===== -->
|
|
<!-- Mapping follows the schema directly: define the schema, then map sources onto it. -->
|
|
<h3>Mapping</h3>
|
|
<!-- schema is set → source→target selectbox editor with auto-suggested flag -->
|
|
<div class="dt-table-wrap">
|
|
<table class="dt-table">
|
|
<thead><tr><th>Source</th><th>Target</th><th>Auto-suggested</th></tr></thead>
|
|
<tbody>
|
|
<tr><td>Full Name</td><td>full_name</td><td>✓</td></tr>
|
|
<tr><td>EmailAddr</td><td>email</td><td>✓</td></tr>
|
|
<tr><td>Phone #</td><td>phone</td><td>✓</td></tr>
|
|
<tr><td>Signup</td><td>signup_date</td><td>✓</td></tr>
|
|
<tr><td>Amount Spent</td><td>amount_spent</td><td>✓</td></tr>
|
|
<tr><td>Notes</td><td>(unmapped)</td><td>✗</td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<p class="dt-caption">Pick a target for each source column. <code>Notes</code> stays unmapped — with the keep-extras strategy it is kept as-is. <code>source</code> is added from the schema default.</p>
|
|
|
|
<hr class="dt-divider">
|
|
|
|
<!-- ===== Strategy ===== -->
|
|
<!-- Strategy is a modifier on the mapping above (strictness: keep/drop extras, coerce, reorder), so it comes after the user can see what it acts on. -->
|
|
<h3>Strategy</h3>
|
|
<div class="dt-field">
|
|
<label class="dt-label">Preset</label>
|
|
<div class="dt-radio-row" style="flex-direction:column; gap:8px">
|
|
<span class="dt-radio"><span class="dot"></span> rename-only (just rename, leave types alone, keep extras)</span>
|
|
<span class="dt-radio"><span class="dot"></span> lenient-schema (rename + convert types + reorder, keep extras)</span>
|
|
<span class="dt-radio"><span class="dot"></span> strict-schema (rename + convert types + reorder, drop extras) <span class="dt-count-pill info" style="margin-left:4px">base</span></span>
|
|
<span class="dt-radio on"><span class="dot"></span> Custom — based on <strong>strict-schema</strong>, 1 control changed <span class="dt-count-pill warn" style="margin-left:4px">modified</span></span>
|
|
</div>
|
|
<div class="dt-precedence" style="margin-top:10px">
|
|
<span class="dt-mi">rule</span>
|
|
<span>Individual Advanced controls win over the preset. You started from <strong>strict-schema</strong>, then changed <strong>Unmapped source columns</strong> to <strong>keep</strong> below — so the preset is now <strong>Custom</strong>. The controls' current values are what actually run.</span>
|
|
</div>
|
|
<div class="dt-help-text">Pick a strategy as the baseline. Every Advanced toggle below is still individually overridable; overriding any one switches the preset to Custom.</div>
|
|
</div>
|
|
|
|
<!-- Advanced options expander -->
|
|
<details class="dt-expander" open>
|
|
<summary>Advanced options</summary>
|
|
<div class="dt-expander-body">
|
|
<div class="dt-cols-2">
|
|
<div>
|
|
<div class="dt-field">
|
|
<label class="dt-label">Unmapped source columns <span class="dt-count-pill warn" style="margin-left:4px">changed</span></label>
|
|
<div class="dt-select">keep</div>
|
|
<div class="dt-help-text">Winning value: <strong>keep</strong>. Overrides the strict-schema base (drop) — so <code>Notes</code> survives into the output.</div>
|
|
</div>
|
|
<div class="dt-check on" title="coerce types per schema"><span class="box"><span class="dt-mi">check</span></span> Convert each column to the right type</div>
|
|
<div class="dt-check on"><span class="box"><span class="dt-mi">check</span></span> Reorder to schema order</div>
|
|
</div>
|
|
<div>
|
|
<div class="dt-check on"><span class="box"><span class="dt-mi">check</span></span> Auto-infer mapping (fuzzy match)</div>
|
|
<div class="dt-field">
|
|
<label class="dt-label">Fuzzy match threshold</label>
|
|
<div class="dt-slider"><div class="track"><div class="fill" style="width:80%"></div><div class="knob" style="left:80%"></div></div><div class="val">0.80</div></div>
|
|
</div>
|
|
<div class="dt-check on"><span class="box"><span class="dt-mi">check</span></span> Enforce required fields</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</details>
|
|
|
|
</div>
|
|
</details>
|
|
|
|
<hr class="dt-divider">
|
|
<button class="dt-btn dt-btn-primary dt-btn-block">Apply Column Mapping</button>
|
|
|
|
<hr class="dt-divider">
|
|
|
|
<!-- ===== Results ===== -->
|
|
<div id="colmap-results-anchor" style="height:1px"></div>
|
|
<h2>Results</h2>
|
|
<div class="dt-metrics">
|
|
<div class="dt-metric"><div class="label">Renamed</div><div class="value">5</div></div>
|
|
<div class="dt-metric"><div class="label">Dropped</div><div class="value">0</div></div>
|
|
<div class="dt-metric"><div class="label">Added</div><div class="value">1</div></div>
|
|
<div class="dt-metric"><div class="label">Coerce fails</div><div class="value">3</div></div>
|
|
</div>
|
|
|
|
<div class="dt-alert info"><span class="dt-mi">info</span><span>Added (with defaults): <code>source</code></span></div>
|
|
<div class="dt-alert warn"><span class="dt-mi">warning</span><span>Some cells could not be coerced and were left as NaN: amount_spent (3)</span></div>
|
|
|
|
<p><strong>Mapped preview (first 10 rows)</strong></p>
|
|
<div class="dt-table-wrap">
|
|
<table class="dt-table">
|
|
<thead><tr><th class="idx"></th><th class="dt-cell-add">full_name</th><th>email</th><th>phone</th><th>signup_date</th><th>amount_spent</th><th class="dt-cell-add">source</th><th>Notes</th></tr></thead>
|
|
<tbody>
|
|
<tr><td class="idx">0</td><td>Jane Doe</td><td>jane@acme.io</td><td>512-555-0190</td><td>2024-01-04</td><td>1204.5</td><td>crm-import</td><td>VIP</td></tr>
|
|
<tr><td class="idx">1</td><td>Bob Smith</td><td>bob@globex.com</td><td>720-555-7781</td><td>2024-02-11</td><td>88.0</td><td>crm-import</td><td></td></tr>
|
|
<tr><td class="idx">2</td><td>Carla Reyes</td><td>carla@initech.net</td><td>415-555-3322</td><td>2024-03-02</td><td>612.1</td><td>crm-import</td><td>renewal</td></tr>
|
|
<tr><td class="idx">3</td><td>Dev Patel</td><td>dev@umbrella.co</td><td>206-555-9043</td><td>2024-03-19</td><td>0.0</td><td>crm-import</td><td></td></tr>
|
|
<tr><td class="idx">4</td><td>Mei Lin</td><td>mei@hooli.com</td><td>503-555-1188</td><td>2024-04-07</td><td class="dt-cell-flag">NaN</td><td>crm-import</td><td>trial</td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<hr class="dt-divider">
|
|
|
|
<!-- Downloads (3 columns) -->
|
|
<div class="dt-cols-3">
|
|
<button class="dt-btn dt-btn-primary">Download mapped CSV</button>
|
|
<button class="dt-btn">Download mapping audit</button>
|
|
<button class="dt-btn">Download config JSON</button>
|
|
</div>
|
|
|
|
<!-- Next-step suggestion -->
|
|
<div class="dt-next-step"><span class="dt-mi">arrow_forward</span><span>Columns mapped. <a href="home.html">Run the recommended clean →</a></span><button class="dt-next-step-dismiss" title="Dismiss">✕</button></div>
|
|
|
|
</div>
|
|
</main>
|
|
</div>
|
|
<footer class="dt-footer" id="dt-footer"></footer>
|
|
<script src="shell.js"></script>
|
|
</body>
|
|
</html>
|