mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-15 17:12:59 -04:00
restyling andalytics
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -63,7 +63,7 @@
|
||||
|
||||
window.roverAnalytics = {
|
||||
track: function (name, data) {
|
||||
enqueue('track', [name, data || {}]);
|
||||
enqueue('track', typeof data === 'undefined' ? [name] : [name, data]);
|
||||
},
|
||||
identify: function (data) {
|
||||
enqueue('identify', [data || {}]);
|
||||
@@ -78,8 +78,8 @@
|
||||
<script defer src="https://analytics.otter.land/script.js" data-website-id="82dd56a5-db44-4279-bd1e-a4d9fee39af7" data-domains="rover.otter.land"></script>
|
||||
<script defer src="https://analytics.otter.land/recorder.js" data-website-id="82dd56a5-db44-4279-bd1e-a4d9fee39af7" data-domains="rover.otter.land" data-sample-rate="0.15" data-mask-level="moderate" data-max-duration="300000"></script>
|
||||
<title>Roomba Rover</title>
|
||||
<script type="module" crossorigin src="/assets/index-DZGFVNgK.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-C6VExJdW.css">
|
||||
<script type="module" crossorigin src="/assets/index-Do2lWMxe.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-C-OVNLHz.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -63,6 +63,7 @@ export function trackAnalyticsEvent(name, payload = {}) {
|
||||
if (!eventName) return;
|
||||
const adapter = getAdapter();
|
||||
if (!adapter || typeof adapter.track !== 'function') return;
|
||||
const cleanPayload = normalizePayload(payload);
|
||||
|
||||
try {
|
||||
/*
|
||||
@@ -70,7 +71,16 @@ export function trackAnalyticsEvent(name, payload = {}) {
|
||||
broken or blocked third-party script from interfering with rover driving,
|
||||
scanner input, chat, or any other live-control UI.
|
||||
*/
|
||||
adapter.track(eventName, normalizePayload(payload));
|
||||
/*
|
||||
Umami accepts an event name without a data object. Avoid forwarding `{}` so
|
||||
events that only need counting do not create noisy empty-property requests
|
||||
in the dashboard or the network payload.
|
||||
*/
|
||||
if (Object.keys(cleanPayload).length) {
|
||||
adapter.track(eventName, cleanPayload);
|
||||
} else {
|
||||
adapter.track(eventName);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Analytics event failed', error);
|
||||
}
|
||||
|
||||
@@ -47,10 +47,11 @@ function getGameTheme(themeColor) {
|
||||
selectedButtonStyle: {
|
||||
borderColor: rgba(rgb, 0.86),
|
||||
borderLeftColor: rgba(rgb, 1),
|
||||
backgroundColor: rgba(rgb, 0.16),
|
||||
backgroundColor: rgba(rgb, 0.14),
|
||||
},
|
||||
titleBoxStyle: {
|
||||
borderLeftColor: rgba(rgb, 0.88),
|
||||
backgroundColor: rgba(rgb, 0.06),
|
||||
},
|
||||
boxStyle: {
|
||||
borderColor: rgba(rgb, 0.62),
|
||||
@@ -88,7 +89,7 @@ function GameChoice({ game, disabled, onVote }) {
|
||||
type="button"
|
||||
disabled={disabled || game.active}
|
||||
onClick={() => onVote(game.id)}
|
||||
className="button-dark flex min-h-[4.25rem] flex-col items-start justify-between border-l-4 px-1.5 py-1 text-left disabled:opacity-70"
|
||||
className="button-dark flex min-h-[3.5rem] flex-col items-start justify-between border-l-4 border-neutral-600 bg-neutral-800 px-1 py-0.75 text-left hover:bg-neutral-700 disabled:opacity-70"
|
||||
style={style}
|
||||
>
|
||||
<span className="flex w-full items-start justify-between gap-1">
|
||||
@@ -106,9 +107,9 @@ function GameChoice({ game, disabled, onVote }) {
|
||||
function StatGrid({ stats, theme }) {
|
||||
if (!Array.isArray(stats) || !stats.length) return null;
|
||||
return (
|
||||
<div className="grid gap-1 sm:grid-cols-3">
|
||||
<div className="grid gap-0.5 sm:grid-cols-3">
|
||||
{stats.slice(0, 6).map((stat) => (
|
||||
<div key={stat.label} className="border border-neutral-700 px-1.5 py-1" style={theme.boxStyle}>
|
||||
<div key={stat.label} className="surface border border-neutral-700 px-1 py-0.75" style={theme.boxStyle}>
|
||||
<p className="truncate text-[0.68rem] text-neutral-400">{stat.label}</p>
|
||||
<p className="truncate text-sm font-semibold text-neutral-100">{stat.value}</p>
|
||||
</div>
|
||||
@@ -127,9 +128,9 @@ function ParticipantsBlock({ participants, theme }) {
|
||||
.filter(Boolean);
|
||||
|
||||
return (
|
||||
<div className="min-w-0 border border-neutral-700 px-2 py-1.5" style={theme.boxStyle}>
|
||||
<div className="surface min-w-0 border border-neutral-700 px-1 py-0.75" style={theme.boxStyle}>
|
||||
<p className="text-sm font-semibold text-neutral-400">Participants</p>
|
||||
<p className="font-mono text-2xl font-semibold leading-tight text-neutral-50">
|
||||
<p className="font-mono text-xl font-semibold leading-tight text-neutral-50">
|
||||
{knownParticipants.length}
|
||||
</p>
|
||||
<p className="truncate text-sm leading-tight text-neutral-300">
|
||||
@@ -160,15 +161,15 @@ function Leaderboard({ players, ownPlayer }) {
|
||||
const hasOwnPoints = ownPlayer && Number.isFinite(ownPlayer.totalPoints);
|
||||
return (
|
||||
<div className="space-y-1">
|
||||
<div className="grid gap-1 sm:grid-cols-[minmax(0,0.8fr)_minmax(0,1.2fr)]">
|
||||
<div className="border border-neutral-700 px-1.5 py-1">
|
||||
<div className="grid gap-0.5 sm:grid-cols-[minmax(0,0.8fr)_minmax(0,1.2fr)]">
|
||||
<div className="surface border border-neutral-700 px-1 py-0.75">
|
||||
<p className="text-xs text-neutral-400">Your points</p>
|
||||
<p className="text-lg font-semibold leading-tight text-neutral-50">
|
||||
{hasOwnPoints ? ownPlayer.totalPoints : 0}
|
||||
</p>
|
||||
{ownPlayer?.rank ? <p className="text-[0.7rem] text-neutral-400">Rank {ownPlayer.rank}</p> : null}
|
||||
</div>
|
||||
<div className="border border-neutral-700 px-1.5 py-1">
|
||||
<div className="surface border border-neutral-700 px-1 py-0.75">
|
||||
<p className="mb-0.5 text-xs font-semibold text-neutral-300">Leaderboard</p>
|
||||
{Array.isArray(players) && players.length ? (
|
||||
<div className="space-y-0.5">
|
||||
@@ -284,8 +285,8 @@ export default function BarcodeGamesPanel() {
|
||||
};
|
||||
|
||||
return (
|
||||
<CardFrame title="Barcode games" bodyClassName="space-y-2 p-1.5 text-sm">
|
||||
<div className="grid gap-1 sm:grid-cols-2">
|
||||
<CardFrame title="Barcode games" bodyClassName="space-y-1 p-0.5 text-sm">
|
||||
<div className="grid gap-0.5 sm:grid-cols-2">
|
||||
{games.map((game) => (
|
||||
<GameChoice
|
||||
key={game.id}
|
||||
@@ -296,25 +297,25 @@ export default function BarcodeGamesPanel() {
|
||||
))}
|
||||
</div>
|
||||
|
||||
<section className="space-y-2 border-t border-neutral-700 pt-2">
|
||||
<div className="grid items-start gap-2 lg:grid-cols-[minmax(0,1fr)_15rem_15rem]">
|
||||
<div className="min-w-0 border-l-4 border-neutral-700 pl-2" style={activeTheme.titleBoxStyle}>
|
||||
<p className="break-words text-2xl font-bold leading-tight text-neutral-50" style={activeTheme.textStyle}>
|
||||
<section className="space-y-1 border-t border-neutral-700 pt-1">
|
||||
<div className="grid items-stretch gap-0.5 lg:grid-cols-[minmax(0,1fr)_9rem_10rem]">
|
||||
<div className="surface min-w-0 border-l-4 border-neutral-700 px-1 py-0.75" style={activeTheme.titleBoxStyle}>
|
||||
<p className="break-words text-xl font-bold leading-tight text-neutral-50" style={activeTheme.textStyle}>
|
||||
{display.title || activeGame?.title || 'No game active'}
|
||||
</p>
|
||||
<p className="mt-0.5 break-words text-lg font-semibold leading-tight text-neutral-200">
|
||||
<p className="mt-0.5 break-words text-sm font-semibold leading-tight text-neutral-200">
|
||||
{display.primary || 'Vote for a game to start'}
|
||||
</p>
|
||||
{display.secondary ? (
|
||||
<p className="mt-1 break-words text-base leading-snug text-neutral-300">{display.secondary}</p>
|
||||
<p className="mt-0.5 break-words text-xs leading-snug text-neutral-300">{display.secondary}</p>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="border border-neutral-700 px-2 py-1.5 text-left lg:text-right" style={activeTheme.boxStyle}>
|
||||
<p className="text-sm font-semibold text-neutral-400">{display.timer?.label || 'Time'}</p>
|
||||
<p className="font-mono text-2xl font-semibold leading-tight text-neutral-50">
|
||||
<div className="surface border border-neutral-700 px-1 py-0.75 text-left lg:text-right" style={activeTheme.boxStyle}>
|
||||
<p className="text-xs font-semibold text-neutral-400">{display.timer?.label || 'Time'}</p>
|
||||
<p className="font-mono text-xl font-semibold leading-tight text-neutral-50">
|
||||
{timerText || '--'}
|
||||
</p>
|
||||
<p className="text-sm leading-tight text-neutral-500">
|
||||
<p className="text-xs leading-tight text-neutral-500">
|
||||
{timerText ? 'active timer' : 'no timer'}
|
||||
</p>
|
||||
</div>
|
||||
@@ -325,10 +326,14 @@ export default function BarcodeGamesPanel() {
|
||||
|
||||
<Leaderboard players={state.leaderboard} ownPlayer={state.ownPlayer} />
|
||||
|
||||
<div className="grid gap-2 border-t border-neutral-700 pt-1.5 sm:grid-cols-2">
|
||||
<div className="grid gap-0.5 border-t border-neutral-700 pt-1 sm:grid-cols-2">
|
||||
<div className="surface px-1 py-0.75">
|
||||
<CounterList title="Most scanned objects" entries={state.counters?.objects} />
|
||||
</div>
|
||||
<div className="surface px-1 py-0.75">
|
||||
<CounterList title="Most scanned rovers" entries={state.counters?.rovers} />
|
||||
</div>
|
||||
</div>
|
||||
</CardFrame>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ export default function SocketConnectionPill() {
|
||||
setLastReason('');
|
||||
show();
|
||||
scheduleFade();
|
||||
trackAnalyticsEvent('socket_connect');
|
||||
trackAnalyticsEvent('socket_connect', { connected: true });
|
||||
};
|
||||
|
||||
const onDisconnect = (reason) => {
|
||||
@@ -70,7 +70,11 @@ export default function SocketConnectionPill() {
|
||||
const onReconnectAttempt = () => {
|
||||
setConnected(false);
|
||||
show();
|
||||
trackAnalyticsEventThrottled('socket_reconnect_attempt', {}, { key: 'socket_reconnect_attempt', throttleMs: 30 * 1000 });
|
||||
trackAnalyticsEventThrottled(
|
||||
'socket_reconnect_attempt',
|
||||
{ connected: false },
|
||||
{ key: 'socket_reconnect_attempt', throttleMs: 30 * 1000 },
|
||||
);
|
||||
};
|
||||
|
||||
socket.on('connect', onConnect);
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
window.roverAnalytics = {
|
||||
track: function (name, data) {
|
||||
enqueue('track', [name, data || {}]);
|
||||
enqueue('track', typeof data === 'undefined' ? [name] : [name, data]);
|
||||
},
|
||||
identify: function (data) {
|
||||
enqueue('identify', [data || {}]);
|
||||
|
||||
Reference in New Issue
Block a user