Files

450 lines
19 KiB
HTML
Raw Permalink Normal View History

<title>Analytics Feature Survey</title>
<style>
:root {
--paper: #FAFAF7;
--ink: #1C1C1A;
--muted: #6B6B63;
--accent: #2F5D50;
--accent-soft: #E4EDE9;
--good: #3F7A5C;
--good-soft: #E6F0EA;
--warn: #B8863B;
--warn-soft: #F5ECDC;
--miss: #A14B3B;
--miss-soft: #F5E5E0;
--hairline: #E4E2DB;
--card: #FFFFFF;
}
:root:not([data-theme="light"]) {
@media (prefers-color-scheme: dark) {
--paper: #17181A;
--ink: #EDEBE4;
--muted: #9B9A90;
--accent: #7FBFA8;
--accent-soft: #1E2C27;
--good: #6FBF97;
--good-soft: #1B2A22;
--warn: #D9A85C;
--warn-soft: #2C2418;
--miss: #D97C68;
--miss-soft: #2E1E1A;
--hairline: #2C2D2E;
--card: #1E1F21;
}
}
:root[data-theme="dark"] {
--paper: #17181A;
--ink: #EDEBE4;
--muted: #9B9A90;
--accent: #7FBFA8;
--accent-soft: #1E2C27;
--good: #6FBF97;
--good-soft: #1B2A22;
--warn: #D9A85C;
--warn-soft: #2C2418;
--miss: #D97C68;
--miss-soft: #2E1E1A;
--hairline: #2C2D2E;
--card: #1E1F21;
}
* { box-sizing: border-box; }
body {
background: var(--paper);
color: var(--ink);
font-family: "IBM Plex Sans", ui-sans-serif, system-ui, sans-serif;
font-size: 15.5px;
line-height: 1.55;
margin: 0;
padding: 4.5rem 1.5rem 6rem;
}
.wrap {
max-width: 780px;
margin: 0 auto;
}
header.page {
margin-bottom: 3.25rem;
}
.eyebrow {
font-family: "IBM Plex Mono", ui-monospace, monospace;
font-size: 0.72rem;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--accent);
margin-bottom: 0.9rem;
}
h1 {
font-family: "Fraunces", Georgia, serif;
font-weight: 560;
font-size: clamp(2.1rem, 4.5vw, 2.65rem);
line-height: 1.08;
letter-spacing: -0.01em;
margin: 0 0 0.9rem;
text-wrap: balance;
}
.dek {
color: var(--muted);
max-width: 60ch;
font-size: 1.02rem;
}
.dek strong {
color: var(--ink);
font-weight: 600;
}
.legend {
display: flex;
flex-wrap: wrap;
gap: 0.6rem;
margin-top: 1.6rem;
}
.chip {
display: inline-flex;
align-items: center;
gap: 0.4rem;
font-family: "IBM Plex Mono", ui-monospace, monospace;
font-size: 0.72rem;
letter-spacing: 0.04em;
padding: 0.28rem 0.6rem;
border-radius: 3px;
}
.chip.have { background: var(--good-soft); color: var(--good); }
.chip.partial { background: var(--warn-soft); color: var(--warn); }
.chip.missing { background: var(--miss-soft); color: var(--miss); }
section.category {
margin-top: 3rem;
}
.cat-head {
display: flex;
align-items: baseline;
gap: 0.85rem;
border-bottom: 1px solid var(--hairline);
padding-bottom: 0.7rem;
margin-bottom: 1.1rem;
}
.cat-num {
font-family: "Fraunces", Georgia, serif;
font-size: 1.05rem;
color: var(--accent);
font-variant-numeric: tabular-nums;
min-width: 1.6rem;
}
.cat-head h2 {
font-family: "Fraunces", Georgia, serif;
font-weight: 500;
font-size: 1.28rem;
margin: 0;
letter-spacing: -0.005em;
}
.cat-note {
color: var(--muted);
font-size: 0.86rem;
margin: 0 0 1.2rem;
max-width: 62ch;
}
.feature {
display: grid;
grid-template-columns: 1fr auto;
gap: 0.3rem 1rem;
padding: 1.05rem 0;
border-bottom: 1px solid var(--hairline);
align-items: start;
}
.feature:last-child { border-bottom: none; }
.f-name {
font-weight: 600;
font-size: 0.98rem;
}
.f-status {
font-family: "IBM Plex Mono", ui-monospace, monospace;
font-size: 0.68rem;
letter-spacing: 0.06em;
text-transform: uppercase;
padding: 0.22rem 0.55rem;
border-radius: 3px;
white-space: nowrap;
height: fit-content;
}
.f-status.have { background: var(--good-soft); color: var(--good); }
.f-status.partial { background: var(--warn-soft); color: var(--warn); }
.f-status.missing { background: var(--miss-soft); color: var(--miss); }
.f-note {
grid-column: 1 / -1;
color: var(--muted);
font-size: 0.87rem;
margin-top: 0.15rem;
max-width: 66ch;
}
.f-note code {
font-family: "IBM Plex Mono", ui-monospace, monospace;
font-size: 0.82em;
background: var(--accent-soft);
color: var(--accent);
padding: 0.08em 0.35em;
border-radius: 3px;
}
footer.page {
margin-top: 4rem;
padding-top: 1.5rem;
border-top: 1px solid var(--hairline);
color: var(--muted);
font-size: 0.82rem;
display: flex;
justify-content: space-between;
gap: 1rem;
flex-wrap: wrap;
}
footer.page a { color: var(--accent); }
@media (max-width: 560px) {
body { padding: 3rem 1.1rem 4rem; }
.feature { grid-template-columns: 1fr; }
.f-status { justify-self: start; }
}
</style>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,400..600&family=IBM+Plex+Sans:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap">
<div class="wrap">
<header class="page">
<div class="eyebrow">boboko / analytics · competitive survey</div>
<h1>What analytics elsewhere can do that boboko can't yet</h1>
<p class="dek">
A feature-by-feature pass across Shopify, WooCommerce Analytics, and PrestaShop's
stats modules — sourced, not recalled from memory — checked against what
<strong>Lunar's admin <code>Dashboard</code></strong> actually ships today and what
raw data already sits in <code>lunar_orders</code>/<code>lunar_carts</code> unused.
This is genuinely new territory for boboko — most rows below land on partial or
missing, and that's an honest read, not an undersell.
</p>
<div class="legend">
<span class="chip have">● have</span>
<span class="chip partial">◐ partial</span>
<span class="chip missing">○ missing</span>
</div>
</header>
<section class="category">
<div class="cat-head">
<span class="cat-num">01</span>
<h2>Sales &amp; revenue dashboard</h2>
</div>
<p class="cat-note">What loads the moment staff open the admin panel — this is the one area where Lunar ships more than expected.</p>
<div class="feature">
<div class="f-name">Revenue / order-count stat cards with period-over-period trend</div>
<span class="f-status have">have</span>
<div class="f-note">Verified from source: <code>OrderStatsOverview</code> widget — today vs. yesterday, last 7 vs. prior 7, last 30 vs. prior 30 days, both order count and sub-total, with up/down trend icons. Registered by default on Lunar's <code>Dashboard</code> page, and boboko's panel (<code>3dealer/app/Providers/PanelServiceProvider.php</code>) registers the stock panel with no <code>pages()</code>/<code>Dashboard</code> override — this ships as-is.</div>
</div>
<div class="feature">
<div class="f-name">Sales-over-time chart (revenue + order count, 12-month trend)</div>
<span class="f-status have">have</span>
<div class="f-note"><code>OrdersSalesChart</code> — ApexCharts area chart, monthly buckets over the trailing year, dual y-axis (order count / sub-total). Same "no override" reasoning as above applies to every widget on this page.</div>
</div>
<div class="feature">
<div class="f-name">Average order value (AOV) trend, segmented by customer group</div>
<span class="f-status have">have</span>
<div class="f-note"><code>AverageOrderValueChart</code> — one series per <code>CustomerGroup</code> plus a synthetic guest series, monthly average of <code>sub_total</code> over the trailing year.</div>
</div>
<div class="feature">
<div class="f-name">New vs. returning customer split</div>
<span class="f-status have">have</span>
<div class="f-note"><code>NewVsReturningCustomersChart</code> reads <code>Order::new_customer</code>, a real boolean column set by <code>Lunar\Jobs\Orders\MarkAsNewCustomer</code> (true when no prior order existed for that customer at placement time) — not a cosmetic flag.</div>
</div>
<div class="feature">
<div class="f-name">Live/latest-orders feed on the dashboard</div>
<span class="f-status have">have</span>
<div class="f-note"><code>LatestOrdersTable</code> — last 10 placed orders, 60s polling, reuses <code>OrderResource</code>'s own table columns.</div>
</div>
<div class="feature">
<div class="f-name">Real-time dashboard vs. scheduled email reports</div>
<span class="f-status partial">partial</span>
<div class="f-note">The dashboard widgets above poll every 60s (near-real-time, pull-based) — there is no scheduled/emailed report anywhere in Lunar or boboko-core. Industry pattern researched: real-time suits operational checks, scheduled digest suits weekly/monthly strategic review — boboko only has the first half.</div>
</div>
</section>
<section class="category">
<div class="cat-head">
<span class="cat-num">02</span>
<h2>Product &amp; catalog performance</h2>
</div>
<p class="cat-note">Which products are actually selling, and what's about to run out.</p>
<div class="feature">
<div class="f-name">Best-sellers / top-products report</div>
<span class="f-status have">have</span>
<div class="f-note"><code>PopularProductsTable</code> — groups <code>lunar_order_lines</code> by product identifier over the trailing 12 months, ranked by quantity sold, with revenue (<code>sub_total</code>) alongside. Physical products only (<code>whereType('physical')</code>).</div>
</div>
<div class="feature">
<div class="f-name">Per-product detail stats (views, conversion, revenue for one SKU)</div>
<span class="f-status missing">missing</span>
<div class="f-note">PrestaShop's <code>statsproduct</code> module was researched as the comparison point (per-product page-view + sales detail) — boboko has no page-view capture at all (see 04), so even the sales half of this can't be built without the traffic half.</div>
</div>
<div class="feature">
<div class="f-name">Catalog-wide statistics (active/inactive counts, category breakdown)</div>
<span class="f-status missing">missing</span>
<div class="f-note">PrestaShop's <code>statscatalog</code> module researched as the reference. No equivalent surface in Lunar or boboko-core — would be a straightforward aggregate over <code>lunar_products</code>/<code>lunar_collections</code>, just not built.</div>
</div>
<div class="feature">
<div class="f-name">Inventory / stock-turnover report</div>
<span class="f-status missing">missing</span>
<div class="f-note"><code>ProductVariant::$stock</code> is a plain point-in-time integer column — no stock-movement ledger or history table exists in <code>lunarphp/core</code> (grepped the models and migrations directories). Turnover reporting needs a time series of stock levels or receipts/sales deltas; today's schema only has "current stock," so there's nothing to compute turnover from yet, not just a missing report.</div>
</div>
<div class="feature">
<div class="f-name">Low-stock / reorder alerting surfaced in a report</div>
<span class="f-status missing">missing</span>
<div class="f-note">The Cart survey already noted <code>ProductIndexer</code>'s <code>in_stock</code> field exists for search/listing purposes — nothing aggregates it into a "low stock" admin view or report.</div>
</div>
</section>
<section class="category">
<div class="cat-head">
<span class="cat-num">03</span>
<h2>Customer analytics</h2>
</div>
<p class="cat-note">Value and behavior at the level of one shopper, or a group of them.</p>
<div class="feature">
<div class="f-name">Per-customer order count / average spend / lifetime spend</div>
<span class="f-status have">have</span>
<div class="f-note">Verified from source: <code>CustomerStatsOverviewWidget</code> on the customer view page — total orders, average spend, and total spend, computed live from <code>orders()->sum()/average()</code>. This is per-customer lookup, not an aggregate report across all customers.</div>
</div>
<div class="feature">
<div class="f-name">Customer Lifetime Value (CLV) as a store-wide metric/segment</div>
<span class="f-status partial">partial</span>
<div class="f-note">The per-customer total-spend figure above is the raw ingredient, but there's no store-wide CLV report, no ranking of customers by CLV, and no predictive/forward-looking CLV — WooCommerce Analytics' Customer Analytics extension (researched) computes this plus churn and RFM segments, none of which exist here.</div>
</div>
<div class="feature">
<div class="f-name">Cohort retention analysis</div>
<span class="f-status missing">missing</span>
<div class="f-note">Researched as a WooCommerce/Metorik feature (retention rate by signup-month cohort). No cohort concept, table, or query exists anywhere in Lunar or boboko-core.</div>
</div>
</section>
<section class="category">
<div class="cat-head">
<span class="cat-num">04</span>
<h2>Behavioral &amp; funnel tracking</h2>
</div>
<p class="cat-note">What happens before an order exists — the storefront side neither repo instruments at all.</p>
<div class="feature">
<div class="f-name">Page-view / product-view event capture</div>
<span class="f-status missing">missing</span>
<div class="f-note">Grepped both repos for <code>gtag</code>/<code>dataLayer</code>/GA4/any client-side event tracker — zero hits. No storefront event of any kind is dispatched, captured, or stored anywhere.</div>
</div>
<div class="feature">
<div class="f-name">Conversion funnel (view → add to cart → checkout → purchase)</div>
<span class="f-status missing">missing</span>
<div class="f-note">Shopify's funnel report (researched) needs a session-scoped event stream across all four stages. boboko has only the last stage as durable data (a placed <code>Order</code>) — no view or add-to-cart events exist to build the earlier steps from, consistent with the Cart survey's finding that Lunar dispatches zero cart events.</div>
</div>
<div class="feature">
<div class="f-name">Abandoned-cart aggregate value/rate reporting</div>
<span class="f-status partial">partial</span>
<div class="f-note">Distinct from the Cart survey's per-cart admin lookup (<code>CartResource</code>, already shipped) — this is a rolled-up metric: total abandoned value this week, abandonment rate as a percentage of carts started. The underlying rows exist in <code>lunar_carts</code>/<code>lunar_cart_lines</code> (same query <code>CartResource</code>'s Abandoned tab already runs), but nothing aggregates them into a rate or a trend — it's list-only today.</div>
</div>
<div class="feature">
<div class="f-name">Traffic-source / campaign attribution (UTM-based)</div>
<span class="f-status missing">missing</span>
<div class="f-note">No UTM capture, no marketing/session table anywhere in either repo. Researched as the backbone of Shopify's/GA4's acquisition reporting — would need a session table capturing <code>utm_source</code>/<code>medium</code>/<code>campaign</code> at first touch, tied forward to the eventual order.</div>
</div>
</section>
<section class="category">
<div class="cat-head">
<span class="cat-num">05</span>
<h2>Tax, accounting &amp; export</h2>
</div>
<p class="cat-note">Getting numbers out of boboko and into someone else's books.</p>
<div class="feature">
<div class="f-name">Tax / VAT breakdown captured per order</div>
<span class="f-status have">have</span>
<div class="f-note">Verified from source: <code>lunar_orders</code> migration stores both <code>tax_breakdown</code> (JSON, per-rate detail) and <code>tax_total</code> as real columns on every placed order — this is genuine underlying data, not inferred.</div>
</div>
<div class="feature">
<div class="f-name">Tax / VAT report for accounting (e.g. by tax zone, by period)</div>
<span class="f-status partial">partial</span>
<div class="f-note">The per-order data above is complete enough to build this from, but nothing aggregates <code>tax_breakdown</code>/<code>tax_total</code> across orders into a filing-ready report by <code>TaxZone</code> or period — no such widget, page, or query exists in Lunar or boboko-core.</div>
</div>
<div class="feature">
<div class="f-name">CSV / accounting-software export of orders or sales data</div>
<span class="f-status missing">missing</span>
<div class="f-note">Grepped for <code>Exporter</code>/<code>ExportAction</code>/<code>Excel::</code> across <code>lunarphp/lunar</code> and boboko-core's <code>src</code> — no hits. Filament ships export actions as a first-party feature elsewhere in the ecosystem; nothing here wires one up for orders.</div>
</div>
<div class="feature">
<div class="f-name">Sales by channel</div>
<span class="f-status partial">partial</span>
<div class="f-note"><code>Order::channel_id</code> is a real, always-populated foreign key (verified in the <code>lunar_orders</code> migration) — every order already knows its channel. No report groups by it; the dashboard's charts are all channel-blind.</div>
</div>
</section>
<section class="category">
<div class="cat-head">
<span class="cat-num">06</span>
<h2>Audit trail vs. analytics</h2>
</div>
<p class="cat-note">A distinction worth being explicit about, since it's easy to mistake one for the other.</p>
<div class="feature">
<div class="f-name">Activity log (Spatie activitylog) on core models</div>
<span class="f-status have">have</span>
<div class="f-note">Verified from source and <code>docs/lunar.md</code>'s Activity Logging section: <code>Lunar\Base\Traits\LogsActivity</code> covers Order, Cart, Product, Customer, and 15 other models, recording only dirty attributes per change under the <code>lunar</code> log name.</div>
</div>
<div class="feature">
<div class="f-name">This counts as analytics</div>
<span class="f-status missing">missing</span>
<div class="f-note">It doesn't, and isn't listed as "have" anywhere above for that reason — activity log is a per-record change history for compliance/support ("who edited this order's shipping address"), not aggregate reporting ("how much revenue this month"). No row in this survey is satisfied by activity-log data.</div>
</div>
</section>
<footer class="page">
<span>Compiled 2026-08-28 — sources cited inline: <code>docs/lunar.md</code> §Filament Panel Integration and §Activity Logging plus direct reads of <code>vendor/lunarphp/lunar/src/Filament/Widgets/Dashboard</code>, <code>vendor/lunarphp/core</code> models/migrations, and <code>3dealer/app/Providers/PanelServiceProvider.php</code> are repo-verified; Shopify/WooCommerce/PrestaShop feature claims are from web research, not repo reads.</span>
<span>boboko-core / docs</span>
</footer>
</div>