Files
veripath/tools/gbp-snapshot-form.html
T

257 lines
13 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>DOP — GBP Snapshot Intake v1.2</title>
<style>
:root { --bg:#0f1419; --panel:#1a2332; --border:#2d3a4f; --text:#e7ecf3; --muted:#8b9bb4; --accent:#3b82f6; --input:#0d1219; --ok:#22c55e; }
* { box-sizing: border-box; }
body { margin:0; font-family: system-ui, sans-serif; background:var(--bg); color:var(--text); line-height:1.45; }
.wrap { max-width:900px; margin:0 auto; padding:24px 16px 48px; }
h1 { font-size:1.3rem; margin:0 0 4px; }
.sub { color:var(--muted); font-size:0.9rem; margin-bottom:18px; }
.card { background:var(--panel); border:1px solid var(--border); border-radius:12px; padding:16px 16px 8px; margin-bottom:14px; }
.card h2 { font-size:0.8rem; text-transform:uppercase; letter-spacing:0.04em; color:var(--muted); margin:0 0 12px; }
.grid { display:grid; grid-template-columns:1fr 1fr; gap:10px 14px; }
@media (max-width:640px){ .grid { grid-template-columns:1fr; } }
.full { grid-column:1 / -1; }
label { display:block; font-size:0.8rem; color:var(--muted); margin-bottom:4px; }
input, select, textarea { width:100%; background:var(--input); border:1px solid var(--border); border-radius:8px; color:var(--text); padding:9px 11px; font-size:0.95rem; outline:none; }
input:focus, select:focus, textarea:focus { border-color:var(--accent); }
textarea { min-height:64px; resize:vertical; }
.actions { display:flex; flex-wrap:wrap; gap:10px; margin:18px 0 10px; }
button { border:none; border-radius:8px; padding:11px 16px; font-size:0.95rem; font-weight:600; cursor:pointer; }
.btn-primary { background:var(--accent); color:#fff; }
.btn-secondary { background:transparent; color:var(--text); border:1px solid var(--border); }
.status { font-size:0.9rem; color:var(--ok); min-height:1.2rem; }
#output { width:100%; min-height:280px; font-family:ui-monospace,monospace; font-size:0.82rem; background:var(--input); border:1px solid var(--border); border-radius:10px; color:var(--text); padding:12px; white-space:pre; }
.hint { font-size:0.82rem; color:var(--muted); margin-top:8px; }
</style>
</head>
<body>
<div class="wrap">
<h1>DOP — GBP Snapshot Intake v1.2</h1>
<p class="sub">NOT_PRESENT = looked, confirmed absent · NOT_OBSERVED = did not look / could not see. One primary category only. Paste output to Leonard (execution card v1.2).</p>
<form id="gbpForm" autocomplete="off">
<div class="card">
<h2>Snapshot Metadata</h2>
<div class="grid">
<div><label for="client">Client name</label><input id="client" type="text" /></div>
<div><label for="snapshot_dt">Snapshot datetime + TZ (ISO)</label><input id="snapshot_dt" type="text" placeholder="2026-08-03T14:30:00-07:00" /></div>
<div><label for="source">Source</label>
<select id="source">
<option>Human eye on Maps</option>
<option>Places API</option>
<option>Owner dashboard</option>
</select>
</div>
<div><label for="surface">Observation surface</label>
<select id="surface">
<option value="desktop_maps">desktop_maps</option>
<option value="mobile_maps">mobile_maps</option>
<option value="knowledge_panel">knowledge_panel</option>
<option value="other">other</option>
</select>
</div>
<div class="full"><label for="maps_url">Maps or Place URL</label><input id="maps_url" type="url" placeholder="https://www.google.com/maps/place/..." /></div>
<div><label for="place_id">Place ID (blank = agent computes from URL)</label><input id="place_id" type="text" /></div>
</div>
</div>
<div class="card">
<h2>Core</h2>
<div class="grid">
<div><label for="business_name">Business name shown</label><input id="business_name" type="text" /></div>
<div><label for="address">Address shown (exact)</label><input id="address" type="text" /></div>
<div><label for="address_canonical">Address canonical (optional)</label><input id="address_canonical" type="text" placeholder="Suite not #" /></div>
<div><label for="phone">Phone (full digits, no mask)</label><input id="phone" type="text" placeholder="+1 530-929-7120" /></div>
<div><label for="website">Website shown</label><input id="website" type="text" /></div>
<div><label for="primary_category">Primary category (ONE only)</label><input id="primary_category" type="text" placeholder="e.g. Gym" /></div>
<div class="full"><label for="additional_categories">Additional categories (list)</label><input id="additional_categories" type="text" /></div>
</div>
</div>
<div class="card">
<h2>Reputation</h2>
<div class="grid">
<div><label for="star_rating">Star rating</label><input id="star_rating" type="text" /></div>
<div><label for="review_count">Review count</label><input id="review_count" type="text" /></div>
<div><label for="review_responses">Review responses observed</label>
<select id="review_responses"><option>NOT_OBSERVED</option><option>Yes</option><option>No</option></select>
</div>
<div><label for="review_responses_detail">If Yes: N of total with replies + most recent age</label><input id="review_responses_detail" type="text" /></div>
</div>
</div>
<div class="card">
<h2>Ownership / Claim</h2>
<div class="grid">
<div><label for="claim_prompt">Own this business? prompt</label>
<select id="claim_prompt"><option>NOT_OBSERVED</option><option>Yes</option><option>No</option></select>
</div>
<div><label for="claimed">Claimed status</label>
<select id="claimed">
<option>Unknown</option>
<option>Claimed</option>
<option>Unclaimed</option>
<option>Likely Claimed — inferred</option>
</select>
</div>
<div class="full"><label for="claimed_signals">If inferred — signals used</label><input id="claimed_signals" type="text" /></div>
<div><label for="verified">Verified badge</label>
<select id="verified"><option>NOT_OBSERVED</option><option>Yes</option><option>No</option></select>
</div>
</div>
</div>
<div class="card">
<h2>Operations</h2>
<div class="grid">
<div><label for="hours_listed">Hours listed</label>
<select id="hours_listed"><option>No</option><option>Yes</option></select>
</div>
<div><label for="open_now">Open now (requires datetime+TZ)</label>
<select id="open_now"><option>NOT_OBSERVED</option><option>Yes</option><option>No</option></select>
</div>
<div class="full"><label for="hours_text">Hours text</label><input id="hours_text" type="text" /></div>
</div>
</div>
<div class="card">
<h2>Conversion (complete whole row if any button observed)</h2>
<div class="grid">
<div><label for="booking_button">Booking button on GBP</label>
<select id="booking_button"><option>NOT_OBSERVED</option><option>Yes</option><option>No</option></select>
</div>
<div><label for="booking_label">Booking label</label><input id="booking_label" type="text" /></div>
<div><label for="messaging_button">Messaging button</label>
<select id="messaging_button"><option>NOT_OBSERVED</option><option>Yes</option><option>No</option></select>
</div>
<div class="full"><label for="other_buttons">Other action buttons</label><input id="other_buttons" type="text" /></div>
</div>
</div>
<div class="card">
<h2>Content</h2>
<div class="grid">
<div><label for="photos_present">Photos present</label>
<select id="photos_present"><option>No</option><option>Yes</option></select>
</div>
<div><label for="photo_count">Approx photo count</label><input id="photo_count" type="text" /></div>
<div class="full"><label for="services">Services listed (exact)</label><input id="services" type="text" /></div>
<div><label for="posts_section">Posts section present</label>
<select id="posts_section"><option>NOT_OBSERVED</option><option>Yes</option><option>No</option></select>
</div>
<div><label for="post_count">Post count (0 if empty)</label><input id="post_count" type="text" placeholder="0" /></div>
<div><label for="recent_post">Most recent post date</label><input id="recent_post" type="text" /></div>
<div><label for="qa_status">Q&A status</label>
<select id="qa_status">
<option>NOT_OBSERVED</option>
<option>NOT_PRESENT</option>
<option>empty</option>
<option>answers present</option>
</select>
</div>
<div class="full"><label for="attributes">Attributes / highlights</label><input id="attributes" type="text" /></div>
</div>
</div>
<div class="card">
<h2>Related listings</h2>
<div class="grid">
<div><label for="second_listing">Second related listing nearby</label>
<select id="second_listing"><option>Unknown</option><option>Yes</option><option>No</option></select>
</div>
<div><label for="second_names">Related listing name(s)</label><input id="second_names" type="text" /></div>
<div class="full"><label for="second_notes">Related notes (factual)</label><textarea id="second_notes"></textarea></div>
<div class="full"><label for="raw_notes">Raw notes</label><textarea id="raw_notes"></textarea></div>
</div>
</div>
</form>
<div class="actions">
<button type="button" class="btn-primary" id="btnGenerate">Generate for Leonard</button>
<button type="button" class="btn-secondary" id="btnCopy">Copy</button>
<button type="button" class="btn-secondary" id="btnClear">Clear</button>
</div>
<div class="status" id="status"></div>
<label for="output">Leonard-ready output</label>
<textarea id="output" readonly></textarea>
<p class="hint">Follows execution card v1.2 (R1R6). PLACE_ID computed from URL by agent if blank.</p>
</div>
<script>
const $ = (id) => document.getElementById(id);
function val(id) {
const v = ($(id).value || '').trim();
return v === '' ? 'NOT_OBSERVED' : v;
}
function buildOutput() {
return `Follow docs/agents/leonard-gbp-execution-card.md exactly (v1.2).
Map intake into client Data Inventory GBP row.
Apply only threat trigger rules R1R6.
Do not invent fields. Do not add solutions. Do not reason beyond the execution card.
Return: updated GBP section, any threat changes, Changes Made list (with object IDs), Rules Fired list (rule ID, version, inputs, values, output).
--- GBP SNAPSHOT INTAKE ---
CLIENT: ${val('client')}
SNAPSHOT_DATETIME: ${val('snapshot_dt')}
SOURCE: ${val('source')}
OBSERVATION_SURFACE: ${val('surface')}
MAPS_URL: ${val('maps_url')}
PLACE_ID: ${($('place_id').value || '').trim() || 'COMPUTE_FROM_URL'}
BUSINESS_NAME: ${val('business_name')}
ADDRESS: ${val('address')}
ADDRESS_CANONICAL: ${val('address_canonical')}
PHONE: ${val('phone')}
WEBSITE: ${val('website')}
PRIMARY_CATEGORY: ${val('primary_category')}
ADDITIONAL_CATEGORIES: ${val('additional_categories')}
STAR_RATING: ${val('star_rating')}
REVIEW_COUNT: ${val('review_count')}
REVIEW_RESPONSES_OBSERVED: ${val('review_responses')}
REVIEW_RESPONSES_DETAIL: ${val('review_responses_detail')}
CLAIM_PROMPT_VISIBLE: ${val('claim_prompt')}
CLAIMED_STATUS: ${val('claimed')}
CLAIMED_SIGNALS: ${val('claimed_signals')}
VERIFIED_BADGE_VISIBLE: ${val('verified')}
HOURS_LISTED: ${val('hours_listed')}
HOURS_TEXT: ${val('hours_text')}
OPEN_NOW_OBSERVED: ${val('open_now')}
BOOKING_BUTTON_PRESENT_ON_GBP: ${val('booking_button')}
BOOKING_BUTTON_LABEL: ${val('booking_label')}
MESSAGING_BUTTON_PRESENT: ${val('messaging_button')}
OTHER_ACTION_BUTTONS: ${val('other_buttons')}
PHOTOS_PRESENT: ${val('photos_present')}
PHOTO_COUNT_APPROX: ${val('photo_count')}
SERVICES_LISTED: ${val('services')}
POSTS_SECTION_PRESENT: ${val('posts_section')}
POST_COUNT: ${val('post_count')}
MOST_RECENT_POST_DATE: ${val('recent_post')}
QA_STATUS: ${val('qa_status')}
ATTRIBUTES_HIGHLIGHTS: ${val('attributes')}
SECOND_LISTING_NEARBY: ${val('second_listing')}
SECOND_LISTING_NAMES: ${val('second_names')}
SECOND_LISTING_NOTES: ${val('second_notes')}
RAW_NOTES: ${val('raw_notes')}
`;
}
$('btnGenerate').onclick = () => { $('output').value = buildOutput(); $('status').textContent = 'Generated.'; };
$('btnCopy').onclick = async () => {
const t = $('output').value || buildOutput();
$('output').value = t;
try { await navigator.clipboard.writeText(t); $('status').textContent = 'Copied.'; } catch { $('status').textContent = 'Select-all + copy.'; }
};
$('btnClear').onclick = () => { $('gbpForm').reset(); $('output').value = ''; $('status').textContent = 'Cleared.'; };
</script>
</body>
</html>