gbp-snapshot-form v1.2 — surface, datetime, NOT_PRESENT/NOT_OBSERVED, category split, posts count, claimed inference options
This commit is contained in:
+227
-497
@@ -3,524 +3,254 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>DOP — GBP Snapshot Intake v1.1</title>
|
||||
<title>DOP — GBP Snapshot Intake v1.2</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #0f1419;
|
||||
--panel: #1a2332;
|
||||
--border: #2d3a4f;
|
||||
--text: #e7ecf3;
|
||||
--muted: #8b9bb4;
|
||||
--accent: #3b82f6;
|
||||
--accent-hover: #2563eb;
|
||||
--success: #22c55e;
|
||||
--input-bg: #0d1219;
|
||||
}
|
||||
: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: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
line-height: 1.45;
|
||||
}
|
||||
.wrap {
|
||||
max-width: 860px;
|
||||
margin: 0 auto;
|
||||
padding: 24px 16px 48px;
|
||||
}
|
||||
h1 {
|
||||
font-size: 1.35rem;
|
||||
margin: 0 0 4px;
|
||||
font-weight: 650;
|
||||
}
|
||||
.sub {
|
||||
color: var(--muted);
|
||||
font-size: 0.92rem;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.card {
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
padding: 16px 16px 8px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.card h2 {
|
||||
font-size: 0.85rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
color: var(--muted);
|
||||
margin: 0 0 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.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[type="text"],
|
||||
input[type="date"],
|
||||
input[type="url"],
|
||||
input[type="number"],
|
||||
select,
|
||||
textarea {
|
||||
width: 100%;
|
||||
background: var(--input-bg);
|
||||
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: 72px; 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: white;
|
||||
}
|
||||
.btn-primary:hover { background: var(--accent-hover); }
|
||||
.btn-secondary {
|
||||
background: transparent;
|
||||
color: var(--text);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
.btn-secondary:hover { border-color: var(--muted); }
|
||||
.status {
|
||||
font-size: 0.9rem;
|
||||
color: var(--success);
|
||||
min-height: 1.2em;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
#output {
|
||||
width: 100%;
|
||||
min-height: 320px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
font-size: 0.82rem;
|
||||
line-height: 1.4;
|
||||
background: var(--input-bg);
|
||||
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;
|
||||
}
|
||||
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.1</h1>
|
||||
<p class="sub">Improved cold-audit form. Fill once per client → Generate → paste to Leonard. Follows execution card rules only.</p>
|
||||
<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" placeholder="Client name" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="snapshot_date">Snapshot date</label>
|
||||
<input id="snapshot_date" type="date" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="source">Source</label>
|
||||
<select id="source">
|
||||
<option value="Human eye on Maps">Human eye on Maps</option>
|
||||
<option value="Places API">Places API</option>
|
||||
<option value="Owner dashboard">Owner dashboard</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="place_id">Place ID (if known)</label>
|
||||
<input id="place_id" type="text" placeholder="Optional" />
|
||||
</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>
|
||||
<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>
|
||||
|
||||
<div class="card">
|
||||
<h2>Core Fields</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</label>
|
||||
<input id="address" type="text" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="phone">Phone shown</label>
|
||||
<input id="phone" type="text" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="website">Website shown</label>
|
||||
<input id="website" type="text" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="primary_category">Primary category</label>
|
||||
<input id="primary_category" type="text" placeholder="Copy exactly as shown" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="additional_categories">Additional categories</label>
|
||||
<input id="additional_categories" type="text" />
|
||||
</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 class="card">
|
||||
<h2>Reputation</h2>
|
||||
<div class="grid">
|
||||
<div>
|
||||
<label for="star_rating">Star rating</label>
|
||||
<input id="star_rating" type="text" placeholder="e.g. 4.8" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="review_count">Review count</label>
|
||||
<input id="review_count" type="text" placeholder="e.g. 42" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="review_responses">Review responses observed</label>
|
||||
<select id="review_responses">
|
||||
<option value="Unknown">Unknown</option>
|
||||
<option value="Yes">Yes</option>
|
||||
<option value="No">No</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="review_responses_detail">Approx how many / how recent (optional)</label>
|
||||
<input id="review_responses_detail" type="text" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>Ownership / Claim Signals</h2>
|
||||
<div class="grid">
|
||||
<div>
|
||||
<label for="claim_prompt">“Own this business?” prompt visible</label>
|
||||
<select id="claim_prompt">
|
||||
<option value="Unknown">Unknown</option>
|
||||
<option value="Yes">Yes</option>
|
||||
<option value="No">No</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="claimed">Claimed status (your best judgment)</label>
|
||||
<select id="claimed">
|
||||
<option value="Unknown">Unknown</option>
|
||||
<option value="Yes">Yes</option>
|
||||
<option value="No">No</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="verified">Verified badge or equivalent observed</label>
|
||||
<select id="verified">
|
||||
<option value="Unknown">Unknown</option>
|
||||
<option value="Yes">Yes</option>
|
||||
<option value="No">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 value="No">No</option>
|
||||
<option value="Yes">Yes</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="open_now">Open now indicator observed</label>
|
||||
<select id="open_now">
|
||||
<option value="Unknown">Unknown</option>
|
||||
<option value="Yes">Yes</option>
|
||||
<option value="No">No</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="full">
|
||||
<label for="hours_text">Hours text (paste if short)</label>
|
||||
<input id="hours_text" type="text" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>Conversion</h2>
|
||||
<div class="grid">
|
||||
<div>
|
||||
<label for="booking_button">Booking button present <strong>on the GBP</strong> (not the website)</label>
|
||||
<select id="booking_button">
|
||||
<option value="Unknown">Unknown</option>
|
||||
<option value="Yes">Yes</option>
|
||||
<option value="No">No</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="booking_label">Booking button label (if any)</label>
|
||||
<input id="booking_label" type="text" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="messaging_button">Messaging button present</label>
|
||||
<select id="messaging_button">
|
||||
<option value="Unknown">Unknown</option>
|
||||
<option value="Yes">Yes</option>
|
||||
<option value="No">No</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="full">
|
||||
<label for="other_buttons">Other action buttons observed</label>
|
||||
<input id="other_buttons" type="text" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>Content Surface</h2>
|
||||
<div class="grid">
|
||||
<div>
|
||||
<label for="photos_present">Photos present</label>
|
||||
<select id="photos_present">
|
||||
<option value="No">No</option>
|
||||
<option value="Yes">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</label>
|
||||
<input id="services" type="text" placeholder="e.g. strength training only" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="posts_status">Updates / Posts status</label>
|
||||
<select id="posts_status">
|
||||
<option value="Not visible">Not visible</option>
|
||||
<option value="Present but empty">Present but empty</option>
|
||||
<option value="Present with content">Present with content</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="recent_post">Most recent post date (approx, optional)</label>
|
||||
<input id="recent_post" type="text" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="qa_status">Q&A status</label>
|
||||
<select id="qa_status">
|
||||
<option value="Not visible">Not visible</option>
|
||||
<option value="Visible but empty">Visible but empty</option>
|
||||
<option value="Visible + has answers">Visible + has answers</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="full">
|
||||
<label for="attributes">Attributes / highlights visible (optional)</label>
|
||||
<input id="attributes" type="text" placeholder="e.g. women-owned, LGBTQ-friendly" />
|
||||
</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 value="Unknown">Unknown</option>
|
||||
<option value="Yes">Yes</option>
|
||||
<option value="No">No</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="second_listing_names">Related listing name(s)</label>
|
||||
<input id="second_listing_names" type="text" />
|
||||
</div>
|
||||
<div class="full">
|
||||
<label for="second_listing_notes">Related listing notes (factual only)</label>
|
||||
<textarea id="second_listing_notes"></textarea>
|
||||
</div>
|
||||
<div class="full">
|
||||
<label for="raw_notes">Raw notes (optional, factual only)</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 to clipboard</button>
|
||||
<button type="button" class="btn-secondary" id="btnDownload">Download .txt</button>
|
||||
<button type="button" class="btn-secondary" id="btnClear">Clear form</button>
|
||||
</div>
|
||||
<div class="status" id="status"></div>
|
||||
|
||||
<label for="output">Leonard-ready output</label>
|
||||
<textarea id="output" readonly placeholder="Click Generate for Leonard…"></textarea>
|
||||
<p class="hint">Paste the output block directly to Leonard. He should follow the execution card and only fire threat rules R1–R3.</p>
|
||||
<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>
|
||||
|
||||
<script>
|
||||
const $ = (id) => document.getElementById(id);
|
||||
|
||||
function val(id) {
|
||||
const el = $(id);
|
||||
const v = (el.value || "").trim();
|
||||
return v === "" ? "Not observed" : v;
|
||||
}
|
||||
|
||||
function todayISO() {
|
||||
const d = new Date();
|
||||
const m = String(d.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(d.getDate()).padStart(2, "0");
|
||||
return `${d.getFullYear()}-${m}-${day}`;
|
||||
}
|
||||
|
||||
$("snapshot_date").value = todayISO();
|
||||
|
||||
function buildOutput() {
|
||||
return `Follow docs/agents/leonard-gbp-execution-card.md exactly.
|
||||
Map this intake into the client Data Inventory GBP row.
|
||||
Apply only threat trigger rules R1–R3.
|
||||
<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 (R1–R6). 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 R1–R6.
|
||||
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, Rules Fired list.
|
||||
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_DATE: ${val("snapshot_date")}
|
||||
SOURCE: ${val("source")}
|
||||
MAPS_URL: ${val("maps_url")}
|
||||
PLACE_ID: ${val("place_id")}
|
||||
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")}
|
||||
PHONE: ${val("phone")}
|
||||
WEBSITE: ${val("website")}
|
||||
PRIMARY_CATEGORY: ${val("primary_category")}
|
||||
ADDITIONAL_CATEGORIES: ${val("additional_categories")}
|
||||
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")}
|
||||
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")}
|
||||
VERIFIED_BADGE_VISIBLE: ${val("verified")}
|
||||
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")}
|
||||
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")}
|
||||
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")}
|
||||
UPDATES_POSTS_STATUS: ${val("posts_status")}
|
||||
MOST_RECENT_POST_DATE: ${val("recent_post")}
|
||||
QA_STATUS: ${val("qa_status")}
|
||||
ATTRIBUTES_HIGHLIGHTS: ${val("attributes")}
|
||||
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_listing_names")}
|
||||
SECOND_LISTING_NOTES: ${val("second_listing_notes")}
|
||||
|
||||
RAW_NOTES: ${val("raw_notes")}
|
||||
SECOND_LISTING_NEARBY: ${val('second_listing')}
|
||||
SECOND_LISTING_NAMES: ${val('second_names')}
|
||||
SECOND_LISTING_NOTES: ${val('second_notes')}
|
||||
RAW_NOTES: ${val('raw_notes')}
|
||||
`;
|
||||
}
|
||||
|
||||
function setStatus(msg) {
|
||||
$("status").textContent = msg;
|
||||
if (msg) setTimeout(() => {
|
||||
if ($("status").textContent === msg) $("status").textContent = "";
|
||||
}, 2500);
|
||||
}
|
||||
|
||||
$("btnGenerate").addEventListener("click", () => {
|
||||
$("output").value = buildOutput();
|
||||
setStatus("Generated.");
|
||||
});
|
||||
|
||||
$("btnCopy").addEventListener("click", async () => {
|
||||
const text = $("output").value || buildOutput();
|
||||
$("output").value = text;
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
setStatus("Copied to clipboard.");
|
||||
} catch (e) {
|
||||
$("output").focus();
|
||||
$("output").select();
|
||||
setStatus("Select-all ready — press Ctrl/Cmd+C.");
|
||||
}
|
||||
});
|
||||
|
||||
$("btnDownload").addEventListener("click", () => {
|
||||
const text = $("output").value || buildOutput();
|
||||
$("output").value = text;
|
||||
const client = ($("client").value || "client").trim().replace(/[^\w\-]+/g, "_") || "client";
|
||||
const date = $("snapshot_date").value || todayISO();
|
||||
const blob = new Blob([text], { type: "text/plain;charset=utf-8" });
|
||||
const a = document.createElement("a");
|
||||
a.href = URL.createObjectURL(blob);
|
||||
a.download = `gbp-intake-${client}-${date}.txt`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
URL.revokeObjectURL(a.href);
|
||||
setStatus("Download started.");
|
||||
});
|
||||
|
||||
$("btnClear").addEventListener("click", () => {
|
||||
$("gbpForm").reset();
|
||||
$("snapshot_date").value = todayISO();
|
||||
$("output").value = "";
|
||||
setStatus("Form cleared.");
|
||||
});
|
||||
</script>
|
||||
}
|
||||
$('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>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user