SyncMadeEasy/force-app/main/default/lwc/shopifySyncDashboard/shopifySyncDashboard.html

837 lines
53 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.

<template>
<!-- LWS-safe download anchor — rendered in template (not document.body) -->
<a class="hidden-download-anchor" style="display:none;" aria-hidden="true"></a>
<div class={hostClass} onclick={handleBodyClick}>
<!-- ============== Toasts ============== -->
<template lwc:if={toasts.length}>
<div class="toast-container">
<template for:each={toasts} for:item="t">
<div key={t.id} class={t.cssClass}>
<span class="toast-icon">{t.iconLabel}</span>
<div class="toast-body">
<div class="toast-title">{t.title}</div>
<div class="toast-message">{t.message}</div>
</div>
<button class="toast-close" data-id={t.id} onclick={handleToastClose} aria-label="Dismiss">×</button>
</div>
</template>
</div>
</template>
<!-- ============== Refresh-in-progress banner ============== -->
<template lwc:if={refreshBannerVisible}>
<div class={refreshBannerClass}>
<span class="dot-blink"></span>
<span class="banner-text">{refreshCurrentObjectLabel}</span>
<div class="banner-progress-track">
<div class="banner-progress-bar" style={refreshProgressStyle}></div>
</div>
</div>
</template>
<!-- ============== SIDEBAR ============== -->
<aside class="sidebar">
<div class="brand">
<div class="brand-logo">
<img src={syncLogoUrl} alt="Sync Made Easy" class="brand-logo-img" />
</div>
<div class="brand-text">
<div class="t1"><b>Sync </b> <span class="italic">made easy</span></div>
<div class="t2">Your Shopify sync with Salesforce made easy</div>
</div>
</div>
<div class="store-selector" onclick={toggleStoreMenu}>
<div class="store-icon">
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2">
<path d="M6 2L3 6v14a2 2 0 002 2h14a2 2 0 002-2V6l-3-4H6zM3 6h18M16 10a4 4 0 01-8 0"></path>
</svg>
</div>
<div class="meta">
<div class="l1">{currentStoreLabel}</div>
<div class="l2">{storeNamesPreview}</div>
</div>
<div class="caret">▾</div>
<div class={storeMenuClass}>
<template for:each={storeOptions} for:item="s">
<div key={s.id} class={s.rowClass} data-store-id={s.id} onclick={handleStoreSelect}>
<span class="store-dot"></span>
<div class="store-meta">
<div class="store-name">{s.name}</div>
</div>
<template lwc:if={s.selected}><span class="store-status">✓</span></template>
</div>
</template>
</div>
</div>
<div class="nav-label">OBJECTS</div>
<div class="obj-list">
<template for:each={sidebarItems} for:item="obj">
<div key={obj.id} class={obj.rowClass} data-id={obj.id} onclick={handleObjectClick}>
<div class="obj-text">
<span class="obj-icon">
<lightning-icon icon-name={obj.icon} size="x-small" variant="inverse"></lightning-icon>
</span>
<div class="obj-name">{obj.name}</div>
</div>
<div class="counts">
<template lwc:if={obj.showShopify}>
<span class="count-pill">
<span class="logo-stack" title="Shopify">
<span class="logo logo-shopify">
<svg viewBox="0 0 32 32" width="18" height="18" aria-hidden="true">
<path fill="#95BF47" d="M22.5 7.6c-.1-.7-.7-.8-1-.8l-1.5-.1-1-1c-.5-.5-.7-.4-.8-.4l-.7.2c-.4-1.1-1-2.2-2.3-2.2h-.2C14.7 3 14.4 3 14 3c-1.5 0-3 1.5-3.7 4.5l-3 .9c-1 .3-1 .3-1.1 1.3L4.5 26l13.3 2.5L25 27l-2.5-19.4zM15.5 5.8c-.3 0-.5.1-.8.2v-.3c0-1 .1-1.8.4-2.4.5.1.9.4 1.2 1-.4.5-.7 1-.8 1.5zm-1.9-1.6c-.5.6-.9 1.6-1 3v.2l-2.2.7c.4-1.4 1.3-3.6 3.2-3.9zm.5 7.5l-.6 1.7c-.3-.2-.7-.4-1.5-.4-1.6 0-1.7 1-1.7 1.3 0 1.5 3.4 2.1 3.4 5.2 0 2.5-1.5 4-3.5 4-2.3 0-3.5-1.5-3.5-1.5l.7-2.4s1.2 1.1 2.2 1.1c.6 0 .9-.5.9-.9 0-2-2.8-2-2.8-4.9 0-2.4 1.6-4.7 4.7-4.7 1.3 0 1.7.4 1.7.4z"></path>
</svg>
</span>
<span class="logo-name">Shopify</span>
</span>
<span class="count-val">{obj.shopify}</span>
</span>
</template>
<span class="count-pill">
<span class="logo-stack" title="Salesforce">
<span class="logo logo-salesforce">
<svg viewBox="0 0 32 22" width="20" height="14" aria-hidden="true">
<path fill="#00A1E0" d="M13.5 2.3a5 5 0 014 2 6 6 0 014-1.5c3.4 0 6 2.6 6 6a6 6 0 01-.2 1.5c1.4.7 2.5 2 2.5 3.7 0 2-1.5 3.6-3.4 3.8a4 4 0 01-3.4 2 4 4 0 01-2-.5 5 5 0 01-4.5 2.8 5 5 0 01-4-1.9 4 4 0 01-2 .4 4 4 0 01-4-3.4c-.6 0-1.1.2-1.6.2-1.8 0-3.3-1.5-3.3-3.3 0-1.3.7-2.4 1.8-3-.3-.6-.5-1.3-.5-2 0-2.8 2.4-5 5.2-5 1.5 0 2.9.6 3.8 1.6.4-1.5 1.7-3.4 4-3.4z"></path>
</svg>
</span>
<span class="logo-name">Salesforce</span>
</span>
<span class="count-val">{obj.sf}</span>
</span>
</div>
</div>
</template>
</div>
<!-- Sync Health panel — title is fixed, stores list scrolls
internally if there are more than ~3 connected stores. -->
<div class="sync-health">
<div class="sync-health-title">SYNC HEALTH</div>
<div class="sync-health-stores">
<template for:each={storeHealthList} for:item="s">
<div key={s.id} class="sync-health-store">
<div class="sync-health-row">
<span class={s.statusClass} aria-hidden="true"></span>
<span class="sync-health-label">{s.name}</span>
<span class="sync-health-value">{s.statusLabel}</span>
</div>
<div class="sync-health-last">Last order: {s.lastOrderLabel}</div>
</div>
</template>
</div>
</div>
<div class="sidebar-footer">
Made with <span class="footer-heart">❤</span>&nbsp;by&nbsp;<a href="https://syncmadeasy.com/" target="_blank" rel="noopener">Sync made easy</a>
</div>
</aside>
<!-- ============== MAIN ============== -->
<section class="main">
<div class="topbar">
<!-- Filters now live in the top bar (replaced the breadcrumb) -->
<div class="topbar-filters">
<span class="filter-label">
<svg class="filter-funnel" viewBox="0 0 16 16" width="13" height="13" aria-hidden="true">
<path d="M2 3h12a0.5 0.5 0 0 1 0.4 0.8L10 9.2v4l-4 1.8V9.2L1.6 3.8A0.5 0.5 0 0 1 2 3z" fill="currentColor"></path>
</svg>
<span class="filter-label-text">Filter by</span>
</span>
<!-- Date Range -->
<div class="menu-wrap">
<div class="select" onclick={toggleDateRangeMenu}>
<span>{filterRangeValue}</span> <span class="caret">▾</span>
</div>
<div class={dateRangeMenuClass} onclick={stopMenuPropagation}>
<template for:each={dateRangeOptions} for:item="opt">
<div key={opt.key} class={opt.cssClass}
data-label={opt.label}
onclick={handleDateRangeSelect}>
{opt.label}
</div>
</template>
</div>
</div>
<!-- Sort By -->
<div class="menu-wrap" style="color: var(--brand-gradient) !important; ">
<div class="select sort-by" onclick={toggleSortByMenu}>
<span style="color: var(--brand-gradient);">{sortByLabel}</span> <span class="caret">▾</span>
</div>
<div class={sortByMenuClass} onclick={stopMenuPropagation}>
<template for:each={sortByOptions} for:item="opt">
<div key={opt.key} class={opt.cssClass}
data-field={opt.field} data-dir={opt.dir}
onclick={handleSortBySelect}>
{opt.label}
</div>
</template>
</div>
</div>
</div>
<div class="topbar-actions">
<button class="icon-btn" title={themeButtonTitle} onclick={toggleTheme}>
<span class="theme-icon">{themeIconLabel}</span>
</button>
<div class="menu-wrap">
<button class="btn" onclick={toggleExportMenu}>⬇ Export ▾</button>
<div class={exportMenuClass}>
<div class="menu-item" data-fmt="excel" onclick={handleExport}>
<span class="menu-icon">📊</span> Excel (.xls)
</div>
<div class="menu-item" data-fmt="pdf" onclick={handleExport}>
<span class="menu-icon">📄</span> PDF
</div>
<div class="menu-item" data-fmt="word" onclick={handleExport}>
<span class="menu-icon">📝</span> Word (.doc)
</div>
<div class="menu-sep"></div>
<div class="menu-item" data-fmt="csv" onclick={handleExport}>
<span class="menu-icon">🧾</span> CSV
</div>
<div class="menu-item" data-fmt="json" onclick={handleExport}>
<span class="menu-icon">🧩</span> JSON
</div>
</div>
</div>
<button class="btn" onclick={openShareModal}>↗ Share</button>
<!-- Refresh: tooltip shows next-refresh countdown; respects 4hr cooldown.
Force Refresh is offered only inside the cooldown modal — keeps the
topbar clean and signals Force is an escape hatch, not a primary action. -->
<button class="btn" onclick={handleRefresh} title={refreshButtonTitle}>⟳ Refresh</button>
<button class="btn primary" onclick={handleSchedule}>+ Schedule Report</button>
</div>
</div>
<div class="content">
<div class="section-title">{activeObject.name} Overview</div>
<div class="kpi-row">
<template for:each={kpis} for:item="k">
<div key={k.id} class={k.cssClass}>
<div class="label">{k.label}</div>
<div class="value">{k.value}</div>
<div class="sub">{k.sub}</div>
<div class={k.deltaClass}>{k.delta}</div>
</div>
</template>
</div>
<div class="section-title">{activeObject.name} Dashboards</div>
<div class={dashRowClass}>
<template for:each={dashboards} for:item="d">
<div key={d.id} class="panel dash-panel">
<div class="panel-head">
<div class="title">{d.title}</div>
<div class="chip-group">
<button class={d.btnLineClass} data-dash-id={d.id} data-type="line" onclick={handleChartType} title="Line">📈</button>
<button class={d.btnBarClass} data-dash-id={d.id} data-type="bar" onclick={handleChartType} title="Bar">📊</button>
<button class={d.btnDonutClass} data-dash-id={d.id} data-type="donut" onclick={handleChartType} title="Donut">◐</button>
</div>
</div>
<div class="chart-body">
<template lwc:if={d.isLine}>
<svg viewBox="0 0 540 300" preserveAspectRatio="xMidYMid meet" class="svg-line">
<defs>
<linearGradient id="lineGrad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#95BE45" stop-opacity="0.30"></stop>
<stop offset="100%" stop-color="#95BE45" stop-opacity="0"></stop>
</linearGradient>
</defs>
<template for:each={d.lineSvg.yTicks} for:item="y">
<g key={y.key}>
<line x1="44" x2={d.lineSvg.gridX2} y1={y.y} y2={y.y} stroke="#eef0ee" stroke-width="1"></line>
<text x="8" y={y.y} font-size="13" fill="#6b7280" dominant-baseline="middle">{y.label}</text>
</g>
</template>
<path d={d.lineSvg.areaPath} fill="url(#lineGrad)" stroke="none"></path>
<path d={d.lineSvg.linePath} fill="none" stroke="#95BE45" stroke-width="3"></path>
<template for:each={d.lineSvg.points} for:item="pt">
<g key={pt.key}>
<circle cx={pt.cx} cy={pt.cy} r="5" fill="#95BE45"></circle>
<circle cx={pt.cx} cy={pt.cy} r="14" fill="transparent" class="hover-target">
<title>{pt.tooltip}</title>
</circle>
</g>
</template>
<template for:each={d.lineSvg.xLabels} for:item="x">
<text key={x.key} x={x.x} y={d.lineSvg.labelY} font-size="13" fill="#6b7280" text-anchor="middle">{x.label}</text>
</template>
</svg>
</template>
<template lwc:elseif={d.isBar}>
<svg viewBox="0 0 540 300" preserveAspectRatio="xMidYMid meet" class="svg-bar">
<template for:each={d.barSvg.yTicks} for:item="y">
<g key={y.key}>
<line x1="44" x2={d.barSvg.gridX2} y1={y.y} y2={y.y} stroke="#eef0ee" stroke-width="1"></line>
<text x="8" y={y.y} font-size="13" fill="#6b7280" dominant-baseline="middle">{y.label}</text>
</g>
</template>
<template for:each={d.barSvg.bars} for:item="b">
<g key={b.key}>
<rect x={b.x} y={b.y} width={b.width} height={b.height} fill={b.fill} rx="4">
<title>{b.tooltip}</title>
</rect>
<text x={b.labelX} y={b.labelY} font-size="12" fill="#6b7280" text-anchor="middle">{b.label}</text>
</g>
</template>
</svg>
</template>
<template lwc:elseif={d.isDonut}>
<div class="donut-wrap">
<svg viewBox="0 0 160 160" class="svg-donut">
<circle cx="80" cy="80" r="60" fill="none" stroke="#eef0ee" stroke-width="22"></circle>
<template for:each={d.donutSvg.segments} for:item="s">
<circle key={s.key}
cx="80" cy="80" r="60"
fill="none"
stroke={s.color}
stroke-width="22"
stroke-dasharray={s.dasharray}
stroke-dashoffset={s.dashoffset}
transform="rotate(-90 80 80)">
<title>{s.tooltip}</title>
</circle>
</template>
</svg>
<div class="legend">
<template for:each={d.legend} for:item="l">
<div key={l.key} class="legend-row">
<span class="sw" style={l.swatchStyle}></span>
<span class="legend-name">{l.label}</span>
<span class="legend-value">{l.value}</span>
</div>
</template>
</div>
</div>
</template>
</div>
</div>
</template>
</div>
<div class="section-title report-title-row">
<span class="records-title">
{activeObject.name} Record Details
<span class="records-info" onclick={openFilterInfo} aria-label="Show query filter">ⓘ</span>
</span>
<div class="report-actions">
<div class="menu-wrap">
<button class="btn small" onclick={toggleFieldMenu}>⚙ Select fields</button>
<div class={fieldMenuClass} onclick={stopMenuPropagation}>
<div class="menu-title">Select fields to show — drag column headers to reorder</div>
<div class="field-search">
<input type="text" placeholder="Filter fields…" value={fieldFilterText} onkeyup={handleFieldSearch} oninput={handleFieldSearch} />
</div>
<div class="field-list">
<template for:each={fieldOptions} for:item="f">
<div key={f.key} class={f.wrapperClass}>
<span class="check-box" data-api={f.api} onclick={toggleField}>{f.visibleMark}</span>
<div class="field-meta">
<div class="field-name">{f.label}</div>
<div class="field-api">{f.api}</div>
</div>
<div class="field-reorder">
<button class="reorder-btn" data-api={f.api} onclick={moveFieldUp} disabled={f.disableMoveUp} title="Move up">▲</button>
<button class="reorder-btn" data-api={f.api} onclick={moveFieldDown} disabled={f.disableMoveDown} title="Move down">▼</button>
</div>
</div>
</template>
</div>
</div>
</div>
</div>
</div>
<div class="panel report-panel">
<div class="table-scroll">
<table class={reportTableClass}>
<thead>
<tr>
<template for:each={reportColumns} for:item="c">
<th key={c.key} class={c.headerClass} data-field={c.api}
draggable={c.draggable}
ondragstart={handleColDragStart}
ondragover={handleColDragOver}
ondrop={handleColDrop}
ondragend={handleColDragEnd}
onclick={handleSort}
title={c.headerTitle}>
<template lwc:if={c.showPin}>
<span class={c.pinClass} title={c.pinTitle} data-field={c.api} onclick={togglePin}>📌</span>
</template>
<template lwc:if={c.showGrip}>
<span class="col-grip" aria-hidden="true">⋮⋮</span>
</template>
<span>{c.label}</span>
<span class="sort-ind">{c.sortIndicator}</span>
</th>
</template>
</tr>
</thead>
<tbody>
<template for:each={reportVisibleRows} for:item="row">
<tr key={row.key}>
<template for:each={row.cells} for:item="cell">
<td key={cell.key}>
<template lwc:if={cell.isLink}>
<span class="record-link"
data-row-index={cell.rowIndex}
data-field-api={cell.key}
onclick={openRecordDetail}>{cell.value}</span>
</template>
<template lwc:else>{cell.value}</template>
</td>
</template>
</tr>
</template>
</tbody>
</table>
</div>
<div class="panel-footer">
<span>{reportRangeLabel}</span>
<div class="pager">
<template lwc:if={showPrevButton}>
<span class="nav" onclick={handlePagePrev}>‹ Previous</span>
</template>
<template for:each={pageButtons} for:item="p">
<span key={p.key} class={p.cssClass} data-page={p.num} onclick={handlePageGo}>{p.num}</span>
</template>
<template lwc:if={showNextButton}>
<span class="nav" onclick={handlePageNext}>Next ›</span>
</template>
</div>
</div>
</div>
</div>
<div class="footer-copy">
Copyright 2026 Product of <a href="https://hicglobalsolutions.com" target="_blank" rel="noopener">HIC Global Solutions</a> All Rights Reserved
</div>
</section>
<!-- ============== Refresh cooldown modal ============== -->
<template lwc:if={showCooldownModal}>
<div class="modal-backdrop">
<div class="modal-card">
<div class="modal-title">Refresh on cooldown</div>
<div class="modal-body">
<p>This dashboard refreshes automatically every <strong>4 hours</strong> to protect Salesforce and Shopify API limits.</p>
<p>Next automatic refresh in:</p>
<div class="cooldown">{cooldownRemainingLabel}</div>
</div>
<div class="modal-actions">
<button class="btn btn-force" onclick={handleForceRefresh}
title="Force a refresh now (bypasses 4hr cooldown). The next 4hr countdown will start from this point.">
⚡ Force refresh now
</button>
<button class="btn primary" onclick={closeCooldownModal}>Got it</button>
</div>
</div>
</div>
</template>
<!-- ============== Schedule Report modal ============== -->
<template lwc:if={showScheduleModal}>
<div class="modal-backdrop">
<div class="modal-card schedule-card">
<div class="modal-title">Schedule Report</div>
<div class="modal-body scrollbar-hidden">
<!-- Existing schedules (queried from CronTrigger — survives page reloads) -->
<template lwc:if={schedJobsLoading}>
<div class="sched-existing-loading">Loading existing schedules…</div>
</template>
<template lwc:elseif={schedHasExistingJobs}>
<div class="sched-existing">
<div class="sched-existing-title">EXISTING SCHEDULES</div>
<template for:each={schedExistingJobs} for:item="j">
<div key={j.id} class="sched-existing-row">
<div class="sched-existing-meta">
<div class="sched-existing-name" title={j.jobName}>{j.reportName}</div>
<div class="sched-existing-sub">Next run: {j.nextRunLabel} · {j.state}</div>
</div>
<button class="btn small sched-delete-btn"
data-job-id={j.id}
onclick={handleDeleteSchedule}
disabled={j.deleting}
title="Delete this scheduled report">
<template lwc:if={j.deleting}>Deleting…</template>
<template lwc:else>🗑 Delete</template>
</button>
</div>
</template>
</div>
</template>
<div class="sched-form">
<label class="sched-row">
<span class="sched-label">Object</span>
<input type="text" class="sched-input sched-input-readonly"
value={activeObject.name}
readonly
tabindex="-1" />
<span class="sched-hint">The dashboard you're scheduling — switch tabs in the sidebar to change.</span>
</label>
<label class="sched-row">
<span class="sched-label">Report</span>
<template lwc:if={schedReportsLoading}>
<input type="text" class="sched-input sched-input-readonly"
value="Loading reports…" readonly tabindex="-1" />
</template>
<template lwc:elseif={schedReportsEmpty}>
<input type="text" class="sched-input sched-input-readonly"
value="No reports available" readonly tabindex="-1" />
</template>
<template lwc:else>
<select class="sched-input" onchange={onSchedReportNameChange}>
<template for:each={schedReportOptions} for:item="o">
<option key={o.value} value={o.value} selected={o.selected}>{o.label}</option>
</template>
</select>
</template>
<template lwc:if={schedReportsError}>
<span class="sched-error">{schedReportsError}</span>
</template>
<span class="sched-hint">Sync Made Easy reports for this object — pick the one to run &amp; email.</span>
</label>
<label class="sched-row">
<span class="sched-label">Frequency</span>
<select class="sched-input" value={schedFrequency} onchange={onSchedFrequencyChange}>
<template for:each={schedFrequencyOptions} for:item="o">
<option key={o.value} value={o.value}>{o.label}</option>
</template>
</select>
</label>
<template lwc:if={schedShowWeeklyDay}>
<label class="sched-row">
<span class="sched-label">Day of Week</span>
<select class="sched-input" value={schedDayOfWeek} onchange={onSchedDayOfWeekChange}>
<template for:each={schedDayOfWeekOptions} for:item="o">
<option key={o.value} value={o.value}>{o.label}</option>
</template>
</select>
</label>
</template>
<template lwc:if={schedShowYearlyMonth}>
<label class="sched-row">
<span class="sched-label">Month</span>
<select class="sched-input" value={schedMonth} onchange={onSchedMonthChange}>
<template for:each={schedMonthOptions} for:item="o">
<option key={o.value} value={o.value}>{o.label}</option>
</template>
</select>
</label>
</template>
<template lwc:if={schedShowMonthlyDay}>
<label class="sched-row">
<span class="sched-label">Day of Month</span>
<select class="sched-input" value={schedDayOfMonth} onchange={onSchedDayOfMonthChange}>
<template for:each={schedDayOfMonthOptions} for:item="o">
<option key={o.value} value={o.value}>{o.label}</option>
</template>
</select>
</label>
</template>
<label class="sched-row">
<span class="sched-label">Time (24h)</span>
<input type="time" class="sched-input"
value={schedTime} onchange={onSchedTimeChange} />
</label>
<label class="sched-row">
<span class="sched-label">Format</span>
<select class="sched-input" value={schedFormat} onchange={onSchedFormatChange}>
<template for:each={schedFormatOptions} for:item="o">
<option key={o.value} value={o.value}>{o.label}</option>
</template>
</select>
</label>
<label class="sched-row">
<span class="sched-label">Recipients <span class="sched-label-hint">(comma or semicolon separated, max 5)</span></span>
<textarea class="sched-input sched-textarea"
rows="2"
placeholder="alice@syncmadeasy.com, bob@syncmadeasy.com; carol@syncmadeasy.com"
oninput={onSchedRecipientsChange}></textarea>
<div class="rcpt-summary">{schedRecipientSummary}</div>
<template lwc:if={schedRecipientPills.length}>
<div class="rcpt-pills">
<template for:each={schedRecipientPills} for:item="r">
<span key={r.key} class={r.cssClass} title={r.email}>
<span class="rcpt-icon">{r.icon}</span> {r.email}
</span>
</template>
</div>
</template>
</label>
<template lwc:if={schedError}>
<div class="sched-error">{schedError}</div>
</template>
</div>
</div>
<div class="modal-actions">
<button class="btn" onclick={closeScheduleModal} disabled={schedSubmitting}>Cancel</button>
<button class="btn primary" onclick={submitSchedule} disabled={schedSubmitting}>
<template lwc:if={schedSubmitting}>Scheduling…</template>
<template lwc:else>Schedule</template>
</button>
</div>
</div>
</div>
</template>
<!-- ============== Share modal ============== -->
<template lwc:if={showShareModal}>
<div class="modal-backdrop">
<div class="modal-card share-card">
<div class="modal-title">{shareTitle}</div>
<template lwc:if={shareStep1}>
<div class="share-grid">
<div class="share-tile" data-fmt="pdf" onclick={selectShareFormat}>
<span class="share-tile-icon">📄</span>
<span class="share-tile-label">PDF</span>
</div>
<div class="share-tile" data-fmt="excel" onclick={selectShareFormat}>
<span class="share-tile-icon">📊</span>
<span class="share-tile-label">Excel</span>
</div>
<div class="share-tile" data-fmt="csv" onclick={selectShareFormat}>
<span class="share-tile-icon">🧾</span>
<span class="share-tile-label">CSV</span>
</div>
</div>
</template>
<template lwc:elseif={shareStep2}>
<div class="share-format-pill">Sharing as <strong>{shareFormatLabel}</strong></div>
<div class="share-grid">
<div class="share-tile" data-dest="WhatsApp" onclick={selectShareDestination}>
<span class="share-tile-icon">
<svg viewBox="0 0 48 48" width="40" height="40" aria-hidden="true">
<path fill="#25D366" d="M4.868 43.303l2.694-9.835C5.9 30.59 5.026 27.324 5.027 23.979 5.032 13.514 13.548 5 24.014 5c5.079.002 9.845 1.979 13.43 5.566 3.584 3.588 5.558 8.356 5.556 13.428C42.994 34.467 34.478 43 24.014 43c-3.174 0-6.293-.86-9.007-2.473L4.868 43.303z"/>
<path fill="#fff" d="M24.014 7C14.65 7 7.032 14.618 7.032 23.983c0 3.27.87 6.451 2.52 9.231l.382.645-1.62 5.914 6.074-1.594.624.37A16.89 16.89 0 0024.014 41c9.365 0 16.983-7.617 16.983-16.983 0-4.535-1.766-8.796-4.97-12.001C32.822 8.81 28.561 7.044 24.014 7z"/>
<path fill="#25D366" d="M19.268 15.916c-.36-.8-.739-.816-1.082-.83-.28-.012-.601-.011-.921-.011s-.841.12-1.281.601c-.441.48-1.682 1.642-1.682 4.003s1.722 4.644 1.962 4.964c.24.32 3.322 5.324 8.204 7.252 4.06 1.603 4.882 1.283 5.763 1.203.881-.08 2.843-1.162 3.243-2.284.401-1.122.401-2.084.281-2.284-.12-.2-.441-.32-.921-.561-.48-.24-2.843-1.402-3.283-1.562-.441-.16-.761-.24-1.082.24-.32.48-1.241 1.562-1.521 1.882-.281.32-.561.36-1.041.12-.48-.24-2.027-.747-3.862-2.382-1.428-1.273-2.392-2.845-2.672-3.325-.28-.48-.03-.74.21-.98.217-.216.48-.561.721-.841.24-.28.32-.48.48-.801.16-.32.08-.601-.04-.841-.12-.24-1.062-2.604-1.476-3.561z"/>
</svg>
</span>
<span class="share-tile-label">WhatsApp</span>
</div>
<div class="share-tile" data-dest="Outlook" onclick={selectShareDestination}>
<span class="share-tile-icon">
<svg viewBox="0 0 48 48" width="40" height="40" aria-hidden="true">
<path fill="#1976D2" d="M28 13h14.533C43.343 13 44 13.657 44 14.467v19.066C44 34.343 43.343 35 42.533 35H28V13z"/>
<path fill="#2196F3" d="M28 6H42.533C43.343 6 44 6.657 44 7.467V14H28V6z"/>
<path fill="#1E88E5" d="M28 34h16v5.533C44 40.343 43.343 41 42.533 41H28V34z"/>
<path fill="#1565C0" d="M28 13v22L4 32V16l24-3z"/>
<path fill="#fff" d="M15 19.5c-3.584 0-6 2.692-6 5.5s2.416 5.5 6 5.5 6-2.692 6-5.5-2.416-5.5-6-5.5zm0 8.5c-1.88 0-3.5-1.343-3.5-3s1.62-3 3.5-3 3.5 1.343 3.5 3-1.62 3-3.5 3z"/>
<path fill="#E3F2FD" d="M38 15.5h-8v3h8v-3zm0 5h-8v3h8v-3zm0 5h-8v3h8v-3z"/>
</svg>
</span>
<span class="share-tile-label">Outlook</span>
</div>
<div class="share-tile" data-dest="Gmail" onclick={selectShareDestination}>
<span class="share-tile-icon">
<svg viewBox="0 0 48 48" width="40" height="40" aria-hidden="true">
<path fill="#4caf50" d="M45 16.2l-5 2.75-8 4.75V40h11c1.1 0 2-.9 2-2V16.2z"/>
<path fill="#1e88e5" d="M3 16.2l5 2.75 8 4.75V40H5c-1.1 0-2-.9-2-2V16.2z"/>
<path fill="#e53935" d="M35 11.2L24 19.45 13 11.2 8 14.2V24l8 4.75L24 24l8 4.75 8-4.75V14.2z"/>
<path fill="#c62828" d="M3 7.38v8.82l5-2.75L13 11.2V8c0-1.1.9-2 2-2h1.45L24 12l7.55-6H33c1.1 0 2 .9 2 2v3.2l5 2.25 3-1.84V7.38C43 5.51 40.98 4 38.5 4h-29C7.02 4 5 5.51 3 7.38z"/>
<path fill="#fbc02d" d="M45 16.2l-10-5V40h8c1.1 0 2-.9 2-2V16.2z"/>
</svg>
</span>
<span class="share-tile-label">Gmail</span>
</div>
<div class="share-tile" data-dest="Slack" onclick={selectShareDestination}>
<span class="share-tile-icon">
<svg viewBox="0 0 48 48" width="40" height="40" aria-hidden="true">
<path fill="#e01e5a" d="M13 28a3 3 0 110-6h3v3a3 3 0 01-3 3zm1.5 0a3 3 0 016 0v7.5a3 3 0 01-6 0V28z"/>
<path fill="#36c5f0" d="M20 13a3 3 0 116 0v3h-3a3 3 0 01-3-3zm0 1.5a3 3 0 010 6h-7.5a3 3 0 010-6H20z"/>
<path fill="#2eb67d" d="M35 20a3 3 0 11 0 6h-3v-3a3 3 0 013-3zm-1.5 0a3 3 0 01-6 0v-7.5a3 3 0 016 0V20z"/>
<path fill="#ecb22e" d="M28 35a3 3 0 11-6 0v-3h3a3 3 0 013 3zm0-1.5a3 3 0 010-6h7.5a3 3 0 010 6H28z"/>
</svg>
</span>
<span class="share-tile-label">Slack</span>
</div>
<div class="share-tile" data-dest="Teams" onclick={selectShareDestination}>
<span class="share-tile-icon">
<svg viewBox="0 0 48 48" width="40" height="40" aria-hidden="true">
<path fill="#5059c9" d="M44 22v10c0 3.314-2.686 6-6 6s-6-2.686-6-6V20h10c1.105 0 2 .895 2 2z"/>
<circle fill="#5059c9" cx="38" cy="10" r="4"/>
<circle fill="#7b83eb" cx="26" cy="10" r="5"/>
<path fill="#7b83eb" d="M34 20v13c0 4.971-4.029 9-9 9s-9-4.029-9-9V20h18z"/>
<path fill="#4b53bc" d="M28 20v13.5c0 1.933-1.567 3.5-3.5 3.5H12.031C12.011 36.672 12 36.338 12 36V20H2v-4c0-1.105.895-2 2-2h24c1.105 0 2 .895 2 2v4z" opacity=".8"/>
<path fill="#fff" d="M19 17h-3v11h-3V17H10v-3h9v3z"/>
</svg>
</span>
<span class="share-tile-label">Teams</span>
</div>
<div class="share-tile" data-dest="More" onclick={selectShareDestination}>
<span class="share-tile-icon">
<svg viewBox="0 0 32 32" width="40" height="40" aria-hidden="true">
<rect width="32" height="32" rx="6" fill="#475569"/>
<circle cx="9.5" cy="16" r="2" fill="#fff"/>
<circle cx="16" cy="16" r="2" fill="#fff"/>
<circle cx="22.5" cy="16" r="2" fill="#fff"/>
</svg>
</span>
<span class="share-tile-label">More</span>
</div>
</div>
</template>
<div class="modal-actions">
<template lwc:if={shareStep2}>
<button class="btn" onclick={backToShareStep1}>← Back</button>
</template>
<button class="btn" onclick={closeShareModal}>Cancel</button>
</div>
</div>
</div>
</template>
<!-- ============== More share options sub-modal ============== -->
<template lwc:if={showMoreShareModal}>
<div class="modal-backdrop">
<div class="modal-card share-card">
<div class="modal-title">More share options</div>
<div class="share-format-pill">Sharing as <strong>{shareFormatLabel}</strong></div>
<p class="more-share-hint">
Save the file to your downloads, then open it with any app on your device.
</p>
<div class="share-grid">
<div class="share-tile" data-channel="copy" onclick={handleMoreShareOption}>
<span class="share-tile-icon">
<svg viewBox="0 0 32 32" width="40" height="40" aria-hidden="true">
<rect width="32" height="32" rx="6" fill="#0ea5e9"/>
<rect x="8" y="6" width="14" height="18" rx="2" fill="#fff"/>
<rect x="10" y="9" width="10" height="12" rx="1" fill="#0ea5e9"/>
<rect x="6" y="9" width="14" height="18" rx="2" fill="#fff" opacity="0.85"/>
</svg>
</span>
<span class="share-tile-label">Copy Download Link</span>
</div>
<div class="share-tile" data-channel="saveFile" onclick={handleMoreShareOption}>
<span class="share-tile-icon">
<svg viewBox="0 0 32 32" width="40" height="40" aria-hidden="true">
<rect width="32" height="32" rx="6" fill="#95BE45"/>
<path d="M16 6v12m-5-5l5 5 5-5" fill="none" stroke="#fff" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6 22v3a2 2 0 002 2h16a2 2 0 002-2v-3" fill="none" stroke="#fff" stroke-width="2.5" stroke-linecap="round"/>
</svg>
</span>
<span class="share-tile-label">Save File</span>
</div>
<!-- <div class="share-tile" data-channel="email" onclick={handleMoreShareOption}>
<span class="share-tile-icon">
<svg viewBox="0 0 32 32" width="40" height="40" aria-hidden="true">
<rect width="32" height="32" rx="6" fill="#6366f1"/>
<rect x="5" y="9" width="22" height="14" rx="2" fill="#fff"/>
<path d="M5 11l11 7 11-7" fill="none" stroke="#6366f1" stroke-width="1.5" stroke-linejoin="round"/>
</svg>
</span>
<span class="share-tile-label">Email</span>
</div> -->
<div class="share-tile" data-channel="print" onclick={handleMoreShareOption}>
<span class="share-tile-icon">
<svg viewBox="0 0 32 32" width="40" height="40" aria-hidden="true">
<rect width="32" height="32" rx="6" fill="#64748b"/>
<rect x="8" y="5" width="16" height="8" rx="1" fill="#fff"/>
<rect x="5" y="12" width="22" height="10" rx="2" fill="#fff"/>
<rect x="9" y="19" width="14" height="8" rx="1" fill="#fff" stroke="#64748b" stroke-width="0.8"/>
<circle cx="22" cy="16" r="1" fill="#64748b"/>
</svg>
</span>
<span class="share-tile-label">Print</span>
</div>
</div>
<div class="modal-actions">
<button class="btn" onclick={backFromMoreShare}>← Back</button>
<button class="btn" onclick={closeMoreShareModal}>Cancel</button>
</div>
</div>
</div>
</template>
<!-- ============== Filter info modal (click ⓘ on Records header) ============== -->
<template lwc:if={showFilterInfoModal}>
<div class="modal-backdrop">
<div class="modal-card filter-info-card">
<div class="modal-title">Active filter on this report</div>
<div class="modal-body">
<ul class="filter-info-list">
<template for:each={activeObjectFilterLines} for:item="line">
<li key={line.key} class="filter-info-row">{line.value}</li>
</template>
</ul>
</div>
<div class="modal-actions">
<button class="btn primary" onclick={closeFilterInfo}>Got it</button>
</div>
</div>
</div>
</template>
<!-- ============== Record Detail modal ============== -->
<template lwc:if={showRecordDetailModal}>
<div class="modal-backdrop">
<div class="modal-card record-detail-card">
<div class="modal-title">{recordDetailTitle}</div>
<div class="modal-body record-detail-body">
<template lwc:if={recordDetailLoading}>
<div class="record-detail-loading">Loading related record…</div>
</template>
<template lwc:else>
<div class="record-detail-field record-detail-id-row">
<span class="record-detail-label">Record ID</span>
<a class="record-detail-value record-id-link"
href={recordDetailUrl}
target="_blank"
rel="noopener"
title="Open this record in Salesforce">{recordDetailId} ↗</a>
</div>
<template for:each={recordDetailFields} for:item="f">
<div key={f.key} class="record-detail-field">
<span class="record-detail-label">{f.label}</span>
<span class="record-detail-value">{f.value}</span>
</div>
</template>
</template>
</div>
<div class="modal-actions">
<button class="btn" onclick={closeRecordDetail}>Close</button>
<button class="btn primary" onclick={openFullRecordPage} disabled={recordDetailLoading}>Open Full Record ↗</button>
</div>
</div>
</div>
</template>
</div>
</template>