PageSourceSearch

https://vistaview.ai/assets/vv-role-dashboard.js?v=20260919T223300Z

js vistaview.ai collected 2026-10-05 13:14:08 UTC 28,701 bytes, 516 lines download raw bytes

1/* vv-role-dashboard WaveAD 20260919T223300Z — BLACK-DASH FIX: never park #root / never set vv-role-dash-on */
2(function () {
3  if (window.__vvRoleDash && window.__vvRoleDash.v === '20260919T223300Z') return;
4  var VER = '20260919T223300Z';
5  window.__vvRoleDash = { v: VER };
6
7  function path() { return String(location.pathname || ''); }
8  function isAnyDash(p) {
9    p = String(p || path());
10    return /^\/v3\/(my-dashboard|customer-dashboard|agent-dashboard|agent-dashboard-pro|broker-dashboard|vendor-dashboard|vendor-hub|homebuyer-hub|agent-hub)(\/|$)/.test(p);
11  }
12  function esc(s) {
13    return String(s == null ? '' : s)
14      .replace(/&/g, '&amp;').replace(/</g, '&lt;')
15      .replace(/>/g, '&gt;').replace(/"/g, '&quot;');
16  }
17  function money(n) {
18    var x = Number(n);
19    if (!isFinite(x)) return '—';
20    try { return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 }).format(x); }
21    catch (e) { return '$' + x; }
22  }
23  function cookieLogged() {
24    try {
25      return document.cookie.split(';').some(function (c) {
26        return /^\s*vv_logged_in=(true|1)/i.test(c) || /^\s*vv_session=/.test(c);
27      });
28    } catch (e) { return false; }
29  }
30  async function jget(url) {
31    try {
32      var r = await fetch(url, { credentials: 'include', headers: { Accept: 'application/json' } });
33      var data = null;
34      try { data = await r.json(); } catch (_) {}
35      return { ok: r.ok, status: r.status, data: data };
36    } catch (e) {
37      return { ok: false, status: 0, data: null };
38    }
39  }
40  function roleFromMe(me) {
41    if (!me) return 'customer';
42    var u = me.user || me;
43    var r = String(u.role || u.user_type || u.account_type || me.role || 'customer').toLowerCase();
44    if (/broker|brokerage/.test(r)) return 'broker';
45    if (/agent|realtor|loan_officer/.test(r)) return 'agent';
46    if (/vendor|pro|contractor|provider|builder/.test(r)) return 'vendor';
47    return 'customer';
48  }
49  function nukeStub() {
50    /* BLACK-DASH FIX: NEVER add vv-role-dash-on — CSS used to park #root at left:-9999px */
51    try { document.documentElement.classList.remove('vv-role-dash-on'); } catch (e0) {}
52    var hostOk = document.getElementById('vv-role-dash-root');
53    if (!hostOk || !hostOk.children || !hostOk.children.length) {
54      /* no visible replacement — leave React alone */
55      return;
56    }
57    var root = document.querySelector('#root') || document.body;
58    var walk = root.querySelectorAll('h1,h2,h3,h4,p,button,a,span,div,li');
59    for (var i = 0; i < walk.length; i++) {
60      var el = walk[i];
61      if (el.closest && el.closest('#vv-role-dash-root')) continue;
62      if (el.closest && el.closest('[data-vv-my-dashboard-gold="1"]')) continue;
63      var t = (el.textContent || '').trim();
64      if (!t || t.length > 120) continue;
65      if (/Italian Leather|Leather Sofa|Innovation Lab|Upgrade to Platinum|Video Call|PRE-LAUNCH|Coming Soon|Demo Mode|demo sofa|Marketplace Theatre|KAFZourL/i.test(t)) {
66        var card = el.closest('section,article,[class*="card"],[class*="Card"],[class*="panel"],[class*="grid"] > div');
67        (card || el).style.setProperty('display', 'none', 'important');
68      }
69    }
70  }
71  function panel(title, bodyHtml, emptyMsg) {
72    var inner = bodyHtml || ('<p class="vrd-empty">' + esc(emptyMsg || 'Nothing here yet — honest empty, not a demo.') + '</p>');
73    return '<section class="vrd-panel"><header class="vrd-panel__h"><h2>' + esc(title) + '</h2></header><div class="vrd-panel__b">' + inner + '</div></section>';
74  }
75  function kv(rows) {
76    return rows.map(function (r) {
77      return '<div class="vrd-kv"><span>' + esc(r[0]) + '</span><strong>' + esc(r[1]) + '</strong></div>';
78    }).join('');
79  }
80  function listRows(items, mapFn) {
81    if (!items || !items.length) return '';
82    return '<ul class="vrd-list">' + items.map(mapFn).join('') + '</ul>';
83  }
84  function linksPanel(title, pairs) {
85    return '<section class="vrd-panel"><header class="vrd-panel__h"><h2>' + esc(title) + '</h2></header><div class="vrd-panel__b vrd-links">' +
86      pairs.map(function (p) { return '<a href="' + esc(p[1]) + '">' + esc(p[0]) + '</a>'; }).join('') +
87      '</div></section>';
88  }
89
90  function customerView(pack) {
91    var hub = (pack.hub && pack.hub.data) || {};
92    var me = (pack.me && pack.me.data) || {};
93    var u = me.user || me || {};
94    var acct = hub.account || {};
95    var stats = hub.stats || {};
96    var name = acct.name || acct.full_name || u.name || u.full_name ||
97      [u.first_name, u.last_name].filter(Boolean).join(' ') || u.email || 'Customer';
98    var orders = hub.order_history || hub.orders || hub.recent_orders || [];
99    if (!orders.length && Array.isArray(hub.recent_orders)) orders = hub.recent_orders;
100    var cards = hub.payment_methods || [];
101    var pays = hub.payments || [];
102    var warranties = hub.warranties || [];
103    var insurance = hub.insurance || [];
104    var saved = hub.saved_properties || [];
105    if (!saved.length) {
106      var sd = pack.saved && pack.saved.data;
107      saved = (sd && (sd.properties || sd.saved_properties || sd.items)) || [];
108    }
109    if (!saved.length) {
110      var pd = pack.props && pack.props.data;
111      saved = (pd && (pd.properties || pd.items)) || [];
112    }
113
114    var hero =
115      '<div class="vrd-hero"><p class="vrd-eye">VistaView · Customer desk · ' + esc(VER) + '</p>' +
116      '<h1>Welcome, ' + esc(String(name).split(' ')[0] || 'there') + '</h1>' +
117      '<p class="vrd-sub">Homes you saved, orders &amp; receipts, cards on file, warranties, insurance — live from your VistaView account. Empty means empty, never a fake sofa.</p></div>';
118
119    var facts =
120      '<div class="vrd-facts">' +
121      '<div><span>Orders</span><strong>' + esc(stats.totalOrders != null ? stats.totalOrders : orders.length) + '</strong></div>' +
122      '<div><span>Spent</span><strong>' + esc(money(stats.totalSpent || 0)) + '</strong></div>' +
123      '<div><span>Saved homes</span><strong>' + esc(stats.savedProperties != null ? stats.savedProperties : saved.length) + '</strong></div>' +
124      '<div><span>Warranties</span><strong>' + esc(stats.activeWarranties != null ? stats.activeWarranties : warranties.length) + '</strong></div>' +
125      '<div><span>Insurance</span><strong>' + esc(stats.insuranceQuotes != null ? stats.insuranceQuotes : insurance.length) + '</strong></div>' +
126      '</div>';
127
128    var acctP = panel('Account', kv([
129      ['Name', name],
130      ['Email', acct.email || u.email || '—'],
131      ['Phone', acct.phone || u.phone || '—'],
132      ['Member since', acct.member_since || acct.created_at
133        ? new Date(acct.member_since || acct.created_at).toLocaleDateString()
134        : '—']
135    ]));
136
137    var homeP = panel('Saved homes', listRows(saved.slice(0, 12), function (h) {
138      var addr = h.address || h.street || h.full_address || h.title || 'Property';
139      var price = h.price != null ? money(h.price) : (h.list_price != null ? money(h.list_price) : '');
140      return '<li><div class="vrd-row"><div><strong>' + esc(addr) + '</strong>' +
141        '<div class="vrd-muted">' + esc([h.city, h.state].filter(Boolean).join(', ')) + '</div></div>' +
142        '<strong>' + esc(price) + '</strong></div></li>';
143    }), 'No saved homes yet — heart a listing on Super Map.');
144
145    var ordP = panel('Orders & receipts', listRows(orders.slice(0, 20), function (o) {
146      return '<li><div class="vrd-row"><div><strong>' + esc(o.order_number || o.id) + '</strong>' +
147        '<div class="vrd-muted">' + esc(o.item || o.product_name || o.description || 'Order') + '</div></div>' +
148        '<div class="vrd-right"><strong>' + esc(money(o.total != null ? o.total : o.total_amount)) + '</strong>' +
149        '<div class="vrd-muted">' + esc(o.payment_status || o.status || '') + '</div></div></div></li>';
150    }), 'No orders yet — browse marketplace when you’re ready.');
151
152    var cardP = panel('Payment methods', listRows(cards, function (c) {
153      return '<li><div class="vrd-row"><strong>' + esc(String(c.brand || c.card_brand || 'Card').toUpperCase()) +
154        ' •••• ' + esc(c.last4 || c.last_4 || '????') + '</strong>' +
155        '<span class="vrd-muted">Exp ' + esc(c.exp_month || c.expMonth || '—') + '/' + esc(c.exp_year || c.expYear || '—') + '</span></div></li>';
156    }), 'No cards on file.');
157
158    var payP = panel('Payments', listRows(pays.slice(0, 15), function (p) {
159      return '<li><div class="vrd-row"><strong>' + esc(p.order_number || p.id) + '</strong>' +
160        '<span>' + esc(money(p.amount)) + ' · ' + esc(p.payment_status || p.status || '') + '</span></div></li>';
161    }), 'No payments yet.');
162
163    var warP = panel('Warranties', listRows(warranties.slice(0, 10), function (w) {
164      return '<li><div class="vrd-row"><strong>' + esc(w.product_name || w.title || w.id || 'Warranty') + '</strong>' +
165        '<span class="vrd-muted">' + esc(w.status || w.coverage || '') + '</span></div></li>';
166    }), 'No active warranties.');
167
168    var insP = panel('Insurance quotes', listRows(insurance.slice(0, 10), function (q) {
169      return '<li><div class="vrd-row"><strong>' + esc(q.carrier || q.provider || q.id || 'Quote') + '</strong>' +
170        '<span class="vrd-muted">' + esc(q.status || money(q.premium) || '') + '</span></div></li>';
171    }), 'No insurance quotes yet.');
172
173    var go = linksPanel('Go do something', [
174      ['Super Map', '/v3/map-search'],
175      ['Wishlist', '/v3/wishlist'],
176      ['All orders', '/v3/orders'],
177      ['VistaSign', '/v3/vista-sign'],
178      ['Hire a pro', '/v3/services'],
179      ['Settings', '/v3/settings']
180    ]);
181
182    var authNote = hub.authenticated || pack.me.ok
183      ? '<p class="vrd-note">Signed in · hub ' + esc(hub.code || 'OK') + '</p>'
184      : '<p class="vrd-note">Browsing signed-out soft desk · hub ' + esc(hub.code || 'AUTH_OPTIONAL_EMPTY') + ' — TopBar login still loads your rows.</p>';
185
186    
187    var goldCard =
188      '<div class="vrd-gold-card" aria-label="VistaView account card">' +
189      '<div class="vrd-shield">V</div>' +
190      '<div><strong>' + esc(name) + '</strong>' +
191      '<span>VistaView · Customer License · ' + esc((acct.email || u.email || 'member').split('@')[0]) + '</span></div>' +
192      '<div class="vrd-serial">VV-CUS</div></div>';
193    return hero + goldCard + facts + authNote +
194      '<div class="vrd-grid">' + acctP + homeP + '</div>' +
195      '<div class="vrd-grid">' + ordP + cardP + '</div>' +
196      '<div class="vrd-grid">' + payP + warP + '</div>' +
197      insP + go;
198  }
199
200  function agentView(pack) {
201    var me = (pack.me && pack.me.data) || {};
202    var u = me.user || me || {};
203    var name = u.name || u.full_name || u.email || 'Agent';
204    var dash = (pack.agentDash && pack.agentDash.data) || {};
205    var kpis = dash.kpis || {};
206    var kpiData = dash.kpiData || [];
207    var leads = dash.leads || (pack.leads && pack.leads.data && (pack.leads.data.leads || pack.leads.data.clients || pack.leads.data.items)) || [];
208    if (!Array.isArray(leads)) leads = [];
209    var listings = dash.listings || (pack.listings && pack.listings.data && (pack.listings.data.listings || pack.listings.data.items)) || [];
210    if (!Array.isArray(listings)) listings = [];
211    var showings = dash.showings || (pack.showings && pack.showings.data && (pack.showings.data.showings || pack.showings.data.items)) || [];
212    if (!Array.isArray(showings)) showings = [];
213    var commissions = dash.commissions || (pack.commissions && pack.commissions.data && (pack.commissions.data.commissions || pack.commissions.data.items)) || [];
214    if (!Array.isArray(commissions)) commissions = [];
215    var clients = (pack.clients && pack.clients.data && pack.clients.data.clients) || [];
216    if (!Array.isArray(clients)) clients = [];
217    // Never trust is_demo theatre lists
218    if (pack.clients && pack.clients.data && pack.clients.data.is_demo) clients = [];
219
220    var activeListings = kpis.activeListings != null ? kpis.activeListings : listings.length;
221    var pendingDeals = kpis.pendingDeals != null ? kpis.pendingDeals : 0;
222    var commYTD = kpis.commissionYTD != null ? kpis.commissionYTD : 0;
223    var totalClients = kpis.totalClients != null ? kpis.totalClients : clients.length;
224    var openLeads = kpis.openLeads != null ? kpis.openLeads : leads.length;
225
226    var hero =
227      '<div class="vrd-hero"><p class="vrd-eye">VistaView · Agent desk · Wave 2</p>' +
228      '<h1>' + esc(String(name).split(' ')[0]) + '\u2019s book</h1>' +
229      '<p class="vrd-sub">Leads, listings, showings, commissions \u2014 live hub data. Empty means empty.</p></div>';
230
231    var facts =
232      '<div class="vrd-facts">' +
233      '<div><span>Active listings</span><strong>' + esc(activeListings) + '</strong></div>' +
234      '<div><span>Open leads</span><strong>' + esc(openLeads) + '</strong></div>' +
235      '<div><span>Pending deals</span><strong>' + esc(pendingDeals) + '</strong></div>' +
236      '<div><span>Clients</span><strong>' + esc(totalClients) + '</strong></div>' +
237      '<div><span>Commission YTD</span><strong>' + esc(typeof commYTD === 'number' ? money(commYTD) : String(commYTD || '$0')) + '</strong></div></div>';
238
239    var leadP = panel('Lead pipeline', listRows(leads.slice(0, 20), function (L) {
240      return '<li><div class="vrd-row"><div><strong>' + esc(L.name || L.full_name || L.email || 'Lead') + '</strong>' +
241        '<div class="vrd-muted">' + esc(L.status || L.stage || L.source || '') + '</div></div>' +
242        '<span class="vrd-muted">' + esc(L.phone || L.email || '') + '</span></div></li>';
243    }), 'No leads yet \u2014 capture from Super Map or share a listing.');
244
245    var listP = panel('Listings', listRows(listings.slice(0, 15), function (L) {
246      var title = L.name || L.address || L.address_line1 || ('Listing #' + (L.id || ''));
247      var price = L.price != null ? money(L.price) : '';
248      return '<li><div class="vrd-row"><div><strong>' + esc(title) + '</strong>' +
249        '<div class="vrd-muted">' + esc([L.city, L.state].filter(Boolean).join(', ') + (L.status ? ' · ' + L.status : '')) + '</div></div>' +
250        '<strong>' + esc(price) + '</strong></div></li>';
251    }), 'No listings on your book yet.');
252
253    var showP = panel('Showings', listRows(showings.slice(0, 15), function (S) {
254      return '<li><div class="vrd-row"><div><strong>' + esc(S.property || S.property_address || S.address || 'Showing') + '</strong>' +
255        '<div class="vrd-muted">' + esc(S.client || S.buyer_name || '') + '</div></div>' +
256        '<span class="vrd-muted">' + esc(S.date || S.scheduled_at || S.status || '') + '</span></div></li>';
257    }), 'No showings scheduled.');
258
259    var commP = panel('Commissions', listRows(commissions.slice(0, 15), function (C) {
260      return '<li><div class="vrd-row"><div><strong>' + esc(C.property || C.property_address || ('Deal #' + (C.id || ''))) + '</strong>' +
261        '<div class="vrd-muted">' + esc(C.status || '') + '</div></div>' +
262        '<strong>' + esc(money(C.commissionAmt != null ? C.commissionAmt : C.commission_amount)) + '</strong></div></li>';
263    }), 'No commissions logged yet.');
264
265    var clientP = panel('Clients', listRows(clients.slice(0, 15), function (C) {
266      return '<li><div class="vrd-row"><div><strong>' + esc(C.name || C.email || 'Client') + '</strong>' +
267        '<div class="vrd-muted">' + esc(C.stage || C.role || '') + '</div></div>' +
268        '<span class="vrd-muted">' + esc(C.phone || C.email || '') + '</span></div></li>';
269    }), 'No clients assigned yet.');
270
271    var go = linksPanel('Agent tools', [
272      ['Agent Hub', '/v3/agent-hub'],
273      ['Lead capture', '/v3/lead-capture'],
274      ['Calendar', '/v3/calendar'],
275      ['Map / listings', '/v3/map-search'],
276      ['Data intel', '/v3/data-intelligence'],
277      ['VistaSign', '/v3/vista-sign']
278    ]);
279
280    var authNote = (dash.authenticated || (pack.me && pack.me.ok))
281      ? '<p class="vrd-note">Signed in · agent hub ' + esc(dash.code || 'OK') + '</p>'
282      : '<p class="vrd-note">Soft desk · ' + esc(dash.code || 'AUTH_OPTIONAL_EMPTY') + ' \u2014 sign in via TopBar to load your book.</p>';
283
284    return hero + facts + authNote +
285      '<div class="vrd-grid">' + leadP + listP + '</div>' +
286      '<div class="vrd-grid">
286' + showP + commP + '</div>' +
287      clientP + go;
288  }
289
290  function brokerView(pack) {
291    var me = (pack.me && pack.me.data) || {};
292    var u = me.user || me || {};
293    var name = u.name || u.full_name || 'Broker';
294    var pulse = (pack.brokerPulse && pack.brokerPulse.data) || {};
295    var report = (pack.brokerReport && pack.brokerReport.data) || {};
296    var isDemo = !!(pulse.is_demo || pulse.isDemo);
297    var agents = pulse.roster || pulse.agents || pulse.team || [];
298    if (!Array.isArray(agents)) agents = [];
299    var matrix = pulse.matrix || pulse.production || [];
300    if (!Array.isArray(matrix)) matrix = [];
301    var stats = pulse.stats || (report.report && report.report.stats) || report.stats || {};
302    var kpis = pulse.kpis || pulse.kpi || report.kpis || {
303      agentCount: pulse.roster_count != null ? pulse.roster_count : agents.length,
304      openDeals: stats.contact_clicks_30d != null ? stats.contact_clicks_30d : stats.views_30d,
305      volumeYTD: null,
306      closings: stats.leads_30d,
307      compliance: (pulse.brokerage && pulse.brokerage.name) || '—'
308    };
309    var hero =
310      '<div class="vrd-hero"><p class="vrd-eye">VistaView · Brokerage · Wave 3' + (isDemo ? ' · sample firm' : '') + '</p>' +
311      '<h1>' + esc(String(name).split(' ')[0]) + ' · Office</h1>' +
312      '<p class="vrd-sub">Team production, compliance pulse, payouts. Sample slug only when signed out — your firm loads when you are in.</p></div>';
313    var facts =
314      '<div class="vrd-facts">' +
315      '<div><span>Agents</span><strong>' + esc(kpis.agentCount != null ? kpis.agentCount : agents.length) + '</strong></div>' +
316      '<div><span>Open pipeline</span><strong>' + esc(kpis.openDeals != null ? kpis.openDeals : (kpis.pipeline || '—')) + '</strong></div>' +
317      '<div><span>Volume YTD</span><strong>' + esc(kpis.volumeYTD != null ? money(kpis.volumeYTD) : (kpis.volume || '—')) + '</strong></div>' +
318      '<div><span>Closings</span><strong>' + esc(kpis.closings != null ? kpis.closings : '—') + '</strong></div>' +
319      '<div><span>Compliance</span><strong>' + esc(kpis.compliance || '—') + '</strong></div></div>';
320    var teamP = panel('Team roster', listRows(agents.slice(0, 20), function (A) {
321      return '<li><div class="vrd-row"><div><strong>' + esc(A.name || A.full_name || A.email || 'Agent') + '</strong>' +
322        '<div class="vrd-muted">' + esc(A.role || A.status || '') + '</div></div>' +
323        '<span class="vrd-muted">' + esc(A.ytd_volume != null ? money(A.ytd_volume) : (A.volume || A.deals || '')) + '</span></div></li>';
324    }), isDemo ? 'Sample firm — sign in as brokerage admin for your roster.' : 'No agents on roster yet.');
325    var pipeP = panel('Office pipeline', listRows(matrix.slice(0, 15), function (R) {
326      return '<li><div class="vrd-row"><strong>' + esc(R.agent || R.name || R.label || 'Row') + '</strong>' +
327        '<span class="vrd-muted">' + esc(R.deals != null ? R.deals + ' deals' : (R.value != null ? money(R.value) : (R.status || ''))) + '</span></div></li>';
328    }), 'No pipeline rows yet.');
329    var payP = panel('Payouts / success fees', '', 'Wire next from brokerage billing-status when firm is linked.');
330    var go = linksPanel('Broker tools', [
331      ['Command center', '/v3/command-center'],
332      ['Team chat', '/v3/team-chat'],
333      ['Reports', '/v3/reports'],
334      ['HR', '/v3/hr'],
335      ['Billing', '/v3/brokerage-billing']
336    ]);
337    var note = isDemo
338      ? '<p class="vrd-note">Showing sample brokerage pulse (slug=remax&sample=1). Real firm data needs brokerage login.</p>'
339      : '<p class="vrd-note">Brokerage pulse ' + esc(pulse.code || report.code || 'OK') + '</p>';
340    return hero + facts + note + '<div class="vrd-grid">' + teamP + pipeP + '</div>' + payP + go;
341  }
342
343  function vendorView(pack) {
344    var me = (pack.me && pack.me.data) || {};
345    var u = me.user || me || {};
346    var name = u.name || u.business_name || u.full_name || 'Pro';
347    var dash = (pack.vendorDash && pack.vendorDash.data) || {};
348    var kpi = dash.kpi || dash.kpis || {};
349    var products = dash.products || [];
350    if (!Array.isArray(products)) products = [];
351    var orders = dash.orders || (pack.vendorOrders && pack.vendorOrders.data && (pack.vendorOrders.data.orders || pack.vendorOrders.data.items)) || [];
352    if (!Array.isArray(orders)) orders = [];
353    var payouts = (pack.vendorPayouts && pack.vendorPayouts.data && (pack.vendorPayouts.data.payouts || pack.vendorPayouts.data.items)) || [];
354    if (!Array.isArray(payouts)) payouts = [];
355    var proDash = (pack.proDash && pack.proDash.data) || {};
356    var earnings = (pack.proEarn && pack.proEarn.data) || {};
357    var jobs = proDash.jobs || proDash.recent_jobs || earnings.recent_jobs || [];
358    if (!Array.isArray(jobs)) jobs = [];
359
360    var hero =
361      '<div class="vrd-hero"><p class="vrd-eye">VistaView · Pro / Vendor · Wave 4</p>' +
362      '<h1>' + esc(String(name).split(' ')[0]) + ' · Shop & jobs</h1>' +
363      '<p class="vrd-sub">Catalog orders, payouts, Hire-Me jobs — scoped to you. Empty when signed out or new.</p></div>';
364    var facts =
365      '<div class="vrd-facts">' +
366      '<div><span>Products</span><strong>' + esc(kpi.totalProducts != null ? kpi.totalProducts : products.length) + '</strong></div>' +
367      '<div><span>Orders</span><strong>' + esc(kpi.totalOrders != null ? kpi.totalOrders : orders.length) + '</strong></div>' +
368      '<div><span>Revenue</span><strong>' + esc(kpi.totalRevenue != null ? money(kpi.totalRevenue) : '—') + '</strong></div>' +
369      '<div><span>Open jobs</span><strong>' + esc(jobs.length) + '</strong></div>' +
370      '<div><span>Payouts</span><strong>' + esc(payouts.length) + '</strong></div></div>';
371
372    var ordP = panel('Orders', listRows(orders.slice(0, 15), function (O) {
373      return '<li><div class="vrd-row"><div><strong>' + esc(O.order_number || O.id || 'Order') + '</strong>' +
374        '<div class="vrd-muted">' + esc(O.status || O.payment_status || '') + '</div></div>' +
375        '<strong>' + esc(money(O.total_amount != null ? O.total_amount : O.total)) + '</strong></div></li>';
376    }), 'No vendor orders yet.');
377    var payP = panel('Payouts', listRows(payouts.slice(0, 15), function (P) {
378      return '<li><div class="vrd-row"><strong>' + esc(P.id || P.payout_id || 'Payout') + '</strong>' +
379        '<span>' + esc(money(P.amount)) + ' · ' + esc(P.status || '') + '</span></div></li>';
380    }), 'No payouts yet.');
381    var jobP = panel('Hire-Me jobs', listRows(jobs.slice(0, 15), function (J) {
382      return '<li><div class="vrd-row"><strong>' + esc(J.title || J.id || 'Job') + '</strong>' +
383        '<span class="vrd-muted">' + esc(J.status || '') + '</span></div></li>';
384    }), 'No Hire-Me jobs — turn on Join Pro.');
385    var prodP = panel('Catalog snapshot', listRows(products.slice(0, 10), function (P) {
386      return '<li><div class="vrd-row"><strong>' + esc(P.name || P.title || P.id) + '</strong>' +
387        '<span>' + esc(money(P.price)) + '</span></div></li>';
388    }), 'No products in your catalog yet.');
389    var go = linksPanel('Pro tools', [
390      ['Vendor Hub', '/v3/vendor-hub'],
391      ['Service map', '/v3/services'],
392      ['Invoices', '/v3/invoices'],
393      ['Join Pro card', '/v3/join-pro'],
394      ['Payouts', '/v3/vendor-payouts']
395    ]);
396    var note = '<p class="vrd-note">Vendor hub ' + esc(dash.code || 'OK') + ' · Hire-Me ' + esc(proDash.code || earnings.code || '—') + '</p>';
397    return hero + facts + note +
398      '<div class="vrd-grid">' + ordP + payP + '</div>' +
399      '<div class="vrd-grid">' + jobP + prodP + '</div>' + go;
400  }
401
402  async function loadPack(role) {
403    var me = await jget('/api/auth/sso/me');
404    if (!me.ok) me = await jget('/api/auth/me');
405    var detected = roleFromMe(me.data);
406    if (!role || role === 'auto') role = detected;
407
408    var pack = { me: me, role: role, hub: null, saved: null, props: null, leads: null, vendor: null };
409    if (role === 'customer') {
410      pack.hub = await jget('/api/hub/customer/dashboard');
411      pack.saved = await jget('/api/hub/buyer/saved-properties');
412      pack.props = await jget('/api/hub/customer/properties');
413    } else if (role === 'agent') {
414      pack.agentDash = await jget('/api/hub/agent/dashboard');
415      pack.listings = await jget('/api/hub/agent/listings');
416      pack.showings = await jget('/api/hub/agent/showings');
417      pack.commissions = await jget('/api/hub/agent/commissions');
418      pack.clients = await jget('/api/hub/agent/clients');
419      pack.leads = await jget('/api/hub/agent/crm-summary');
420      if (!pack.leads.ok) pack.leads = await jget('/api/leads/list');
421    } else if (role === 'broker') {
422      pack.brokerPulse = await jget('/api/hub/brokerage/pulse?slug=remax&sample=1');
423      pack.brokerReport = await jget('/api/hub/brokerage/report-30d');
424      pack.brokerBilling = await jget('/api/hub/brokerage/billing-status');
425    } else if (role === 'vendor') {
426      pack.vendorDash = await jget('/api/hub/vendor/dashboard');
427      pack.vendorOrders = await jget('/api/hub/vendor/orders');
428      pack.vendorPayouts = await jget('/api/hub/vendor/payouts');
429      pack.proDash = await jget('/api/service-marketplace/provider/dashboard');
430      pack.proEarn = await jget('/api/service-marketplace/provider/earnings');
431    }
432    return pack;
433  }
434
435  function render(host, pack) {
436    var role = pack.role || 'customer';
437    var html;
438    if (role === 'agent') html = agentView(pack);
439    else if (role === 'broker') html = brokerView(pack);
440    else if (role === 'vendor') html = vendorView(pack);
441    else html = customerView(pack);
442
443    var switcher =
444      '<div class="vrd-switch" role="navigation" aria-label="Role preview">' +
445      ['customer', 'agent', 'broker', 'vendor'].map(function (r) {
446        return '<button type="button" data-vrd-role="' + r + '" class="' + (r === role ? 'on' : '') + '">' + r + '</button>';
447      }).join('') + '</div>';
448
449    host.innerHTML = '<div class="vrd-shell" data-v="' + VER + '">' + switcher + html + '</div>';
450    host.querySelectorAll('[data-vrd-role]').forEach(function (btn) {
451      btn.addEventListener('click', function () { mount(btn.getAttribute('data-vrd-role')); });
452    });
453  }
454
455  async function mount(forceRole) {
456    if (!isAnyDash()) {
457      var old = document.getElementById('vv-role-dash-root');
458      if (old) old.remove();
459      document.documentElement.classList.remove('vv-role-dash-on');
460      return;
461    }
462    var host = document.getElementById('vv-role-dash-root');
463    if (!host) {
464      host = document.createElement('div');
465      host.id = 'vv-role-dash-root';
466      /* Append as body sibling — never nest inside #root child that CSS used to park */
467      document.body.appendChild(host);
468    }
469    host.innerHTML = '<div class="vrd-shell"><div class="vrd-hero"><p class="vrd-eye">VistaView</p><h1>Loading your desk…</h1></div></div>';
470    var pack = await loadPack(forceRole || 'auto');
471    if (!pack.me.ok && !cookieLogged()) {
472      pack.role = forceRole && forceRole !== 'auto' ? forceRole : 'customer';
473      if (!pack.hub) {
474        pack.hub = {
475          ok: true,
476          data: {
477            authenticated: false,
478            stats: { totalOrders: 0, totalSpent: 0, savedProperties: 0, activeWarranties: 0, insuranceQuotes: 0 },
479            account: null, orders: [], order_history: [], recent_orders: [],
480            saved_properties: [], payment_methods: [], payments: [],
481            warranties: [], insurance: [], code: 'AUTH_OPTIONAL_EMPTY'
482          }
483        };
484      }
485    }
486    render(host, pack);
487    /* Only after host has children; never sets vv-role-dash-on */
488    nukeStub();
489  }
490
491  function boot() {
492    mount('auto');
493    var n = 0;
494    var t = setInterval(function () {
495      n++;
496      /* remount if path ok but host missing; never park #root */
497      if (isAnyDash() && !document.getElementById('vv-role-dash-root')) mount('auto');
498      else if (isAnyDash()) nukeStub();
499      if (n > 60) clearInterval(t);
500    }, 400);
501    window.addEventListener('popstate', function () { mount('auto'); });
502    var _ps = history.pushState, _rs = history.replaceState;
503    history.pushState = function () {
504      var r = _ps.apply(this, arguments);
505      setTimeout(function () { mount('auto'); }, 0);
506      return r;
507    };
508    history.replaceState = function () {
509      var r = _rs.apply(this, arguments);
510      setTimeout(function () { mount('auto'); }, 0);
511      return r;
512    };
513  }
514  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot);
515  else boot();
516})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.