diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 804538a3..b41e6b5d 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -55,7 +55,7 @@ module.exports = { { group: ['../pages/**', '../../pages/**', '../../../pages/**'], message: 'Use @/pages/* instead of relative paths' - } + }, ] }], }, @@ -84,7 +84,7 @@ module.exports = { { "group": ["../db/**", "../../db/**"], "message": "Architecture: No direct DB imports from components. Import a hook/API instead." - } + }, ] }] } @@ -128,6 +128,19 @@ module.exports = { ] }] } + }, + { + files: ["src/pages/**/*.{ts,tsx}", "src/components/**/*.{ts,tsx}"], + rules: { + "no-restricted-imports": ["error", { + "patterns": [ + { + "group": ["@/components/ui/button"], + "message": "Pages should use AnalyticsButton from @/components/ui/analytics-button instead of Button to ensure proper user action tracking" + } + ] + }] + } } ], } diff --git a/src/api/hooks/useKanbanBoard.ts b/src/api/hooks/useKanbanBoard.ts index 94cb9310..e40474be 100644 --- a/src/api/hooks/useKanbanBoard.ts +++ b/src/api/hooks/useKanbanBoard.ts @@ -4,6 +4,7 @@ import { DragEndEvent } from '@dnd-kit/core' import { AppsWithTime } from '@/api/monitorApi/monitorApi' // Adjust path as needed for types import { ActivityRating } from '@/lib/app-directory/apps-types' // Adjust path as needed for types import { useAppUsage, useTags, useUpdateAppRatingMutation } from '@/api/hooks/useAppUsage' +import { AnalyticsService } from '@/lib/analytics' // --- Type Definitions for Kanban Board --- @@ -101,6 +102,7 @@ export const useKanbanBoard = ({ rangeMode, date }: { rangeMode: 'day' | 'week' updatedColumns[targetColumnId].push(app) updatedColumns[targetColumnId].sort((a, b) => b.duration - a.duration) } + AnalyticsService.trackEvent('app_dragged') return updatedColumns }) updateAppRatingMutation.mutate({ appTagId, rating: rating as ActivityRating }) diff --git a/src/components/ActiveDevicesSettings.tsx b/src/components/ActiveDevicesSettings.tsx index f39352f0..145c1165 100644 --- a/src/components/ActiveDevicesSettings.tsx +++ b/src/components/ActiveDevicesSettings.tsx @@ -1,5 +1,5 @@ import { Badge } from '@/components/ui/badge' -import { Button } from '@/components/ui/button' +import { AnalyticsButton } from '@/components/ui/analytics-button' import { useEffect, useState } from 'react' import { hostname } from '@tauri-apps/plugin-os' import { User } from '@supabase/supabase-js' @@ -149,13 +149,18 @@ export function ActiveDevicesSettings({ user, maxDevices }: ActiveDevicesSetting This Device ) : ( - + )} diff --git a/src/components/AppSelector.tsx b/src/components/AppSelector.tsx index 130bb4f8..6ecab48e 100644 --- a/src/components/AppSelector.tsx +++ b/src/components/AppSelector.tsx @@ -6,7 +6,7 @@ import { AppCategory, categoryEmojis } from '@/lib/app-directory/apps-types' import { useEffect, useRef, useState } from 'react' import { MonitorApi , App, Tag } from '@/api/monitorApi/monitorApi' import { AppIcon } from '@/components/AppIcon' -import { Button } from '@/components/ui/button' +import { AnalyticsButton } from '@/components/ui/analytics-button' import { DifficultySelector } from '@/components/difficulty-selector' import { CategoryTooltip } from '@/components/CategoryTooltip' import { PaywallDialog } from '@/components/PaywallDialog' @@ -514,7 +514,7 @@ export function AppSelector({ {onIsAllowListChange && (
- + {!canUseAllowList ? ( - + ) : ( - + )}
)} diff --git a/src/components/FriendsComparisonCard.tsx b/src/components/FriendsComparisonCard.tsx index 30792020..d94fb181 100644 --- a/src/components/FriendsComparisonCard.tsx +++ b/src/components/FriendsComparisonCard.tsx @@ -1,6 +1,6 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Progress } from '@/components/ui/progress' -import { Button } from '@/components/ui/button' +import { AnalyticsButton } from '@/components/ui/analytics-button' import { Badge } from '@/components/ui/badge' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { Users, UserPlus, Mail, Send, Check, X } from 'lucide-react' @@ -173,7 +173,8 @@ const PendingInvitesTab = () => {
Email: {invite.from_auth_user_email || invite.to_email}
- - +
))} {receivedRequests.length > visibleReceived && (
- +
)} @@ -232,14 +235,15 @@ const PendingInvitesTab = () => { {sentRequests.length > visibleSent && (
- +
)} @@ -295,9 +299,16 @@ const EmptyFriendsState = () => { required disabled={isInviting} /> - + @@ -358,10 +369,16 @@ export const FriendsComparisonCard = ({ }

- + @@ -429,17 +446,23 @@ export const FriendsComparisonCard = ({ />
- - +
diff --git a/src/components/ModeToggle.tsx b/src/components/ModeToggle.tsx index eae74038..6585eac9 100644 --- a/src/components/ModeToggle.tsx +++ b/src/components/ModeToggle.tsx @@ -1,6 +1,6 @@ import { Moon, Sun } from 'lucide-react' -import { Button } from '@/components/ui/button' +import { AnalyticsButton } from '@/components/ui/analytics-button' import { DropdownMenu, DropdownMenuContent, @@ -15,11 +15,11 @@ export function ModeToggle() { return ( - + Toggle theme + setTheme('light')}> diff --git a/src/components/NotificationBanner.tsx b/src/components/NotificationBanner.tsx index e0edf513..d8ba9fa2 100644 --- a/src/components/NotificationBanner.tsx +++ b/src/components/NotificationBanner.tsx @@ -2,7 +2,7 @@ import * as React from 'react' import { useGetActiveNotification, useUpdateNotificationStatus } from '@/api/hooks/useNotifications' import { useEffect } from 'react' import { useNavigate } from 'react-router-dom' -import { Button } from '@/components/ui/button' +import { AnalyticsButton } from './ui/analytics-button' interface NotificationAction { label: string @@ -51,14 +51,15 @@ export const NotificationBanner: React.FC = () => { {activeNotification.content}
{notificationAction && ( - + )}
)} - + ) } diff --git a/src/components/NotificationPanel/NotificationPanel.tsx b/src/components/NotificationPanel/NotificationPanel.tsx index 2ef2405f..bab394fc 100644 --- a/src/components/NotificationPanel/NotificationPanel.tsx +++ b/src/components/NotificationPanel/NotificationPanel.tsx @@ -14,8 +14,9 @@ import { EbbListen } from '@/lib/ebbListen' import { useShortcutStore } from '@/lib/stores/shortcutStore' import { useShortcutKeyDetection } from '../../hooks/useShortcutKeyDetection' import { EbbWorker } from '../../lib/ebbWorker' +import { AnalyticsEvent } from '../../lib/analytics' -type NotificationType = 'session-start' | 'quick-start' | 'smart-start-suggestion' | 'doomscroll-start-suggestion' | 'blocked-app' | 'blocked-app-hard' | 'session-end' | 'session-warning' | 'end-session' | 'scheduled-session-reminder' | 'scheduled-session-start' +export type NotificationType = 'session-start' | 'quick-start' | 'smart-start-suggestion' | 'doomscroll-start-suggestion' | 'blocked-app' | 'blocked-app-hard' | 'session-end' | 'session-warning' | 'end-session' | 'scheduled-session-reminder' | 'scheduled-session-start' interface NotificationPayload { timeCreating?: number @@ -485,6 +486,7 @@ export const NotificationPanel = () => { shortcutParts={shortcutParts} pressedKeys={pressedKeys} onClick={handleButtonClick} + analyticsEvent={`notification_${notificationType}_button_clicked` as AnalyticsEvent} /> )} diff --git a/src/components/RangeModeSelector.tsx b/src/components/RangeModeSelector.tsx index 6aef78ea..6ad6b7c9 100644 --- a/src/components/RangeModeSelector.tsx +++ b/src/components/RangeModeSelector.tsx @@ -3,7 +3,7 @@ import { PopoverContent, PopoverTrigger, } from '@/components/ui/popover' -import { Button } from '@/components/ui/button' +import { AnalyticsButton } from '@/components/ui/analytics-button' import { cn } from '@/lib/utils/tailwind.util' export type RangeMode = 'day' | 'week' | 'month' @@ -31,7 +31,8 @@ export function RangeModeSelector({ value, onChange, className }: RangeModeSelec return ( - +
{rangeOptions.map((option) => ( - + ))}
diff --git a/src/components/ScheduleSessionModal.tsx b/src/components/ScheduleSessionModal.tsx index 41d604e7..ac61eb04 100644 --- a/src/components/ScheduleSessionModal.tsx +++ b/src/components/ScheduleSessionModal.tsx @@ -1,5 +1,5 @@ import { useState, useEffect } from 'react' -import { Button } from '@/components/ui/button' +import { AnalyticsButton } from '@/components/ui/analytics-button' import { DialogFooter } from '@/components/ui/dialog' import { Input } from '@/components/ui/input' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' @@ -8,6 +8,7 @@ import { useGetWorkflows } from '@/api/hooks/useWorkflow' import { RecurrenceSettings } from '@/api/ebbApi/focusScheduleApi' import { cn } from '@/lib/utils/tailwind.util' import { Workflow } from '@/api/ebbApi/workflowApi' +import { NoAnalyticsButton } from './ui/no-analytics-button' interface ScheduleSessionModalProps { scheduleId?: string @@ -151,7 +152,7 @@ export function ScheduleSessionModal({ scheduleId, onClose }: ScheduleSessionMod
- - +
{recurrenceType === 'weekly' && ( @@ -180,7 +181,7 @@ export function ScheduleSessionModal({ scheduleId, onClose }: ScheduleSessionMod
{dayNames.map((day, index) => ( - + ))}
@@ -207,19 +208,20 @@ export function ScheduleSessionModal({ scheduleId, onClose }: ScheduleSessionMod - - + ) diff --git a/src/components/ShortcutInput.tsx b/src/components/ShortcutInput.tsx index 5250ee0c..c90c6a49 100644 --- a/src/components/ShortcutInput.tsx +++ b/src/components/ShortcutInput.tsx @@ -1,5 +1,5 @@ import { useState, useEffect } from 'react' -import { Button } from '@/components/ui/button' +import { AnalyticsButton } from '@/components/ui/analytics-button' import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover' import { Hotkey } from '@/components/ui/hotkey' import { X, Check } from 'lucide-react' @@ -166,7 +166,8 @@ export function ShortcutInput({ popoverAlign = 'center' }: ShortcutInputProps) { return ( - + {snapshot?.isClosing && ( diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 139d2a29..8f31bbe1 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -1,4 +1,4 @@ -import { Button } from '@/components/ui/button' +import { NoAnalyticsButton } from '@/components/ui/no-analytics-button' import { Link, useLocation } from 'react-router-dom' import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' import { SettingsGearIcon } from '@/components/icons/GearIcon' @@ -29,7 +29,7 @@ export function Sidebar() { {/* Category dashboard link */} - + Today @@ -46,7 +46,7 @@ export function Sidebar() { {isFocusScheduleFeatureEnabled(user?.email) && ( - + Focus Schedule )} - + Categories - + Friends @@ -98,7 +98,7 @@ export function Sidebar() { - + Community @@ -116,9 +116,9 @@ export function Sidebar() { {!hasProAccess && (
- +
)} @@ -126,7 +126,7 @@ export function Sidebar() {
- + Settings diff --git a/src/components/SlackFocusToggle.tsx b/src/components/SlackFocusToggle.tsx index 377124b5..0ad173fe 100644 --- a/src/components/SlackFocusToggle.tsx +++ b/src/components/SlackFocusToggle.tsx @@ -1,7 +1,7 @@ import { useState, useEffect } from 'react' import { SlackIcon } from '@/components/icons/SlackIcon' import { Switch } from '@/components/ui/switch' -import { Button } from '@/components/ui/button' +import { AnalyticsButton } from '@/components/ui/analytics-button' import { Input } from '@/components/ui/input' import { Dialog, @@ -195,24 +195,26 @@ export function SlackFocusToggle({ slackSettings, onSlackSettingsChange }: Slack
- +
- +
diff --git a/src/components/SocialStatusSummary.tsx b/src/components/SocialStatusSummary.tsx index b802b5df..b8acd5e1 100644 --- a/src/components/SocialStatusSummary.tsx +++ b/src/components/SocialStatusSummary.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from 'react' import { useNavigate } from 'react-router-dom' -import { Button } from '@/components/ui/button' +import { AnalyticsButton } from '@/components/ui/analytics-button' import { Skeleton } from '@/components/ui/skeleton' import { ConnectIcon } from '@/components/icons/ConnectIcon' import { useConnectedStore } from '@/lib/stores/connectedStore' @@ -82,7 +82,8 @@ const StatusButton = ()=> { if(!connected) { return (
- +
) diff --git a/src/components/TimeSelector.tsx b/src/components/TimeSelector.tsx index 4c4f16cb..66f41957 100644 --- a/src/components/TimeSelector.tsx +++ b/src/components/TimeSelector.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import { Check, ChevronsUpDown } from 'lucide-react' -import { Button } from '@/components/ui/button' +import { AnalyticsButton } from '@/components/ui/analytics-button' import { Command, CommandEmpty, @@ -195,7 +195,8 @@ export function TimeSelector({ value: externalValue, onChange }: TimeSelectorPro return ( - + diff --git a/src/components/TopNav.tsx b/src/components/TopNav.tsx index bb791f31..bb4efc56 100644 --- a/src/components/TopNav.tsx +++ b/src/components/TopNav.tsx @@ -1,4 +1,4 @@ -import { Button } from '@/components/ui/button' +import { AnalyticsButton } from '@/components/ui/analytics-button' import { X } from 'lucide-react' import { Link, useNavigate } from 'react-router-dom' import { Logo } from '@/components/ui/logo' @@ -8,6 +8,7 @@ import { useShortcutStore } from '@/lib/stores/shortcutStore' import { CircleHelpIcon } from './icons/CircleHelpIcon' import { Tooltip, TooltipContent , TooltipTrigger } from './ui/tooltip' import { SocialStatusSummary } from './SocialStatusSummary' +import { NoAnalyticsButton } from './ui/no-analytics-button' interface TopNavProps { variant?: 'default' | 'modal' @@ -44,7 +45,8 @@ export function TopNav({ variant = 'default' }: TopNavProps) {
- + Feedback {variant === 'default' ? ( -
)} - + ) : ( - + )} diff --git a/src/components/TypewriterModeToggle.tsx b/src/components/TypewriterModeToggle.tsx deleted file mode 100644 index 02f91c15..00000000 --- a/src/components/TypewriterModeToggle.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { useState } from 'react' -import { TypeOutline, KeyRound } from 'lucide-react' -import { Button } from '@/components/ui/button' -import { Badge } from '@/components/ui/badge' -import { - Popover, - PopoverContent, - PopoverTrigger, -} from '@/components/ui/popover' -import { PaywallDialog } from '@/components/PaywallDialog' -import { usePermissions } from '@/hooks/usePermissions' - -interface TypewriterModeToggleProps { - typewriterMode: boolean - onToggle: (value: boolean) => void -} - -export function TypewriterModeToggle({ typewriterMode, onToggle }: TypewriterModeToggleProps) { - const [isPopoverOpen, setIsPopoverOpen] = useState(false) - const { canUseTypewriter } = usePermissions() - - return ( -
- - -
setIsPopoverOpen(true)} - onMouseLeave={() => setIsPopoverOpen(false)} - > - {canUseTypewriter ? ( - - ) : ( - - - - )} -
-
- -
-

Typewriter Mode

- {canUseTypewriter ? ( - - {typewriterMode ? 'On' : 'Off'} - - ) : ( - - )} -
-

De-emphasize everything but the active window

-
-
-
- ) -} diff --git a/src/components/UsageSummary.tsx b/src/components/UsageSummary.tsx index 6b683c3a..a75b2fdf 100644 --- a/src/components/UsageSummary.tsx +++ b/src/components/UsageSummary.tsx @@ -16,7 +16,7 @@ import { import { Skeleton } from './ui/skeleton' import { GraphableTimeByHourBlock, AppsWithTime } from '@/api/monitorApi/monitorApi' import { AppIcon } from '@/components/AppIcon' -import { Button } from '@/components/ui/button' +import { AnalyticsButton } from '@/components/ui/analytics-button' import { useRef, useEffect, useState } from 'react' import { Switch } from '@/components/ui/switch' import { useCreateNotification, useGetNotificationBySentId } from '@/api/hooks/useNotifications' @@ -236,14 +236,15 @@ export const UsageSummary = ({
{sortedAppUsage.slice(0, 3).map((app, index) => ( - + ))}
diff --git a/src/components/UserProfileSettings.tsx b/src/components/UserProfileSettings.tsx index 77d6f3ba..2677da7a 100644 --- a/src/components/UserProfileSettings.tsx +++ b/src/components/UserProfileSettings.tsx @@ -1,4 +1,4 @@ -import { Button } from '@/components/ui/button' +import { AnalyticsButton } from '@/components/ui/analytics-button' import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip' import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar' import { PaywallDialog } from '@/components/PaywallDialog' @@ -99,7 +99,8 @@ export function UserProfileSettings({ user }: UserProfileSettingsProps) {
- +
@@ -157,7 +158,13 @@ export function UserProfileSettings({ user }: UserProfileSettingsProps) { {license?.licenseType &&

Type: {license.licenseType}

} {license?.expirationDate &&

Updates until: {format(new Date(license.expirationDate), 'PP')}

} {license?.licenseType === 'subscription' && ( - + Manage Subscription )} @@ -181,10 +188,15 @@ export function UserProfileSettings({ user }: UserProfileSettingsProps) {
- +
diff --git a/src/components/developer/ResetAppData.tsx b/src/components/developer/ResetAppData.tsx index ac990ce8..0f4cf39b 100644 --- a/src/components/developer/ResetAppData.tsx +++ b/src/components/developer/ResetAppData.tsx @@ -1,9 +1,9 @@ import { useState } from 'react' -import { Button } from '@/components/ui/button' import { invoke } from '@tauri-apps/api/core' import { StorageUtils } from '@/lib/utils/storage.util' import { logAndToastError } from '@/lib/utils/ebbError.util' import { useAuth } from '../../hooks/useAuth' +import { NoAnalyticsButton } from '../ui/no-analytics-button' export const ResetAppData = () => { const [isResetting, setIsResetting] = useState(false) @@ -52,21 +52,21 @@ export const ResetAppData = () => { This will reset all app data to simulate a first-time experience. Your existing data will be backed up.

- + - +
) diff --git a/src/components/difficulty-selector/DifficultyButton.tsx b/src/components/difficulty-selector/DifficultyButton.tsx index 320e36af..3db1dda3 100644 --- a/src/components/difficulty-selector/DifficultyButton.tsx +++ b/src/components/difficulty-selector/DifficultyButton.tsx @@ -1,4 +1,4 @@ -import { Button } from '@/components/ui/button' +import { AnalyticsButton } from '@/components/ui/analytics-button' import { cn } from '@/lib/utils/tailwind.util' import { KeyRound } from 'lucide-react' import { PaywallDialog } from '../PaywallDialog' @@ -23,7 +23,8 @@ export function DifficultyButton({ key={difficulty.value} className="relative" > - + {difficulty.value === 'hard' && isDisabled && (
- +
)} diff --git a/src/components/difficulty-selector/DifficultySelector.tsx b/src/components/difficulty-selector/DifficultySelector.tsx index 31c22e61..8b7e9af5 100644 --- a/src/components/difficulty-selector/DifficultySelector.tsx +++ b/src/components/difficulty-selector/DifficultySelector.tsx @@ -3,7 +3,7 @@ import { PopoverContent, PopoverTrigger, } from '@/components/ui/popover' -import { Button } from '@/components/ui/button' +import { AnalyticsButton } from '@/components/ui/analytics-button' import { cn } from '@/lib/utils/tailwind.util' import { DifficultyButton } from './DifficultyButton' import { SignalBars } from './SignalBars' @@ -47,7 +47,8 @@ export function DifficultySelector({ value, onChange, className, disabledOptions return ( - + ( + ({ analyticsEvent, analyticsProperties, onClick, ...props }, ref) => { + const handleClick = (e: React.MouseEvent) => { + console.log('AnalyticsButton clicked:', { analyticsEvent, analyticsProperties }) + // Track the analytics event + AnalyticsService.trackEvent(analyticsEvent, analyticsProperties) + + // Call the original onClick handler + if (onClick) { + onClick(e) + } + } + + return ( + +
{(permissionStatus === 'checking' || permissionStatus === 'not_granted') && ( diff --git a/src/pages/CategoryDashboardPage.tsx b/src/pages/CategoryDashboardPage.tsx index e0b37a95..2b78c381 100644 --- a/src/pages/CategoryDashboardPage.tsx +++ b/src/pages/CategoryDashboardPage.tsx @@ -1,6 +1,6 @@ import { CSSProperties, useEffect, useState } from 'react' import { Layout } from '@/components/Layout' -import { Button } from '@/components/ui/button' +import { AnalyticsButton } from '@/components/ui/analytics-button' import { Progress } from '@/components/ui/progress' import { Calendar } from '@/components/ui/calendar' import { formatTime } from '@/components/UsageSummary' @@ -215,9 +215,14 @@ export default function CategoryDashboardPage () { - + - + + upgrade to Ebb Pro + {' '} for up to 3 macOS devices. The first registered device is always active and free. diff --git a/src/pages/FeedbackPage.tsx b/src/pages/FeedbackPage.tsx index c20afa68..183e2d5e 100644 --- a/src/pages/FeedbackPage.tsx +++ b/src/pages/FeedbackPage.tsx @@ -1,5 +1,5 @@ import { useEffect, useState, useRef } from 'react' -import { Button } from '@/components/ui/button' +import { AnalyticsButton } from '@/components/ui/analytics-button' import { Card } from '@/components/ui/card' import { Layout } from '@/components/Layout' import { CommunityCard } from '@/components/CommunityCard' @@ -99,9 +99,14 @@ export default function FeedbackPage() { style={{ resize: 'none', overflow: 'hidden' }} />
- +
)} diff --git a/src/pages/FlowPage/FlowPage.tsx b/src/pages/FlowPage/FlowPage.tsx index 6e477a5c..034c4e00 100644 --- a/src/pages/FlowPage/FlowPage.tsx +++ b/src/pages/FlowPage/FlowPage.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from 'react' import { useNavigate } from 'react-router-dom' -import { Button } from '@/components/ui/button' +import { AnalyticsButton } from '@/components/ui/analytics-button' import { FlowSession } from '@/db/ebb/flowSessionRepo' import { DateTime, Duration } from 'luxon' import { FlowSessionApi } from '@/api/ebbApi/flowSessionApi' @@ -399,33 +399,36 @@ export const FlowPage = () => {
- - - +
) diff --git a/src/pages/FlowPage/Timer.tsx b/src/pages/FlowPage/Timer.tsx index 0bc5a4d3..8df65842 100644 --- a/src/pages/FlowPage/Timer.tsx +++ b/src/pages/FlowPage/Timer.tsx @@ -5,7 +5,7 @@ import { useState, useEffect } from 'react' import { FlowSession } from '@/db/ebb/flowSessionRepo' import { useFlowTimer } from '@/lib/stores/flowTimer' import { logAndToastError } from '@/lib/utils/ebbError.util' -import { Button } from '@/components/ui/button' +import { AnalyticsButton } from '@/components/ui/analytics-button' const getDurationFormatFromSeconds = (seconds: number) => { const duration = Duration.fromMillis(seconds * 1000) @@ -95,12 +95,17 @@ export const Timer = ({ flowSession }: { flowSession: FlowSession | null }) => { {time} {flowSession?.duration && ( - + )} ) diff --git a/src/pages/FocusSchedulePage.tsx b/src/pages/FocusSchedulePage.tsx index 4120dee0..299dfbeb 100644 --- a/src/pages/FocusSchedulePage.tsx +++ b/src/pages/FocusSchedulePage.tsx @@ -1,6 +1,6 @@ import { useState } from 'react' import { Layout } from '@/components/Layout' -import { Button } from '@/components/ui/button' +import { AnalyticsButton } from '@/components/ui/analytics-button' import { Card, CardHeader, CardTitle } from '@/components/ui/card' import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from '@/components/ui/dialog' import { useFocusSchedulesWithWorkflow, useDeleteFocusSchedule } from '@/api/hooks/useFocusSchedule' @@ -76,10 +76,18 @@ export default function FocusSchedulePage() { Schedule focus sessions to protect your most productive times

- + )}
@@ -104,20 +112,30 @@ export default function FocusSchedulePage() {
- - +
@@ -130,10 +148,16 @@ export default function FocusSchedulePage() {

Create your first focus schedule to start protecting your productive time.

- + )} @@ -164,8 +188,28 @@ export default function FocusSchedulePage() { - - + setShowDeleteDialog(false)} + analyticsEvent="delete_schedule_clicked" + analyticsProperties={{ + button_location: 'delete_dialog', + context: 'cancel' + }} + > + Cancel + + + Delete + diff --git a/src/pages/FriendsAnalyticsPage/FriendsAnalyticsPreview.tsx b/src/pages/FriendsAnalyticsPage/FriendsAnalyticsPreview.tsx index 872e3538..fcdc0eba 100644 --- a/src/pages/FriendsAnalyticsPage/FriendsAnalyticsPreview.tsx +++ b/src/pages/FriendsAnalyticsPage/FriendsAnalyticsPreview.tsx @@ -8,7 +8,7 @@ import { useNetworkStore } from '@/lib/stores/networkStore' import { useUpdateProfileLocation, useProfile } from '@/api/hooks/useProfile' import { logAndToastError } from '@/lib/utils/ebbError.util' import { ConnectIcon } from '@/components/icons/ConnectIcon' -import { Button } from '@/components/ui/button' +import { AnalyticsButton } from '@/components/ui/analytics-button' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { DateTime } from 'luxon' @@ -349,15 +349,20 @@ export const FriendsAnalyticsPreview = () => {

Check your internet connection to connect with friends and see detailed analytics.

- + ) : ( <> @@ -365,15 +370,20 @@ export const FriendsAnalyticsPreview = () => {

Connect with the community to unlock detailed analytics, compare with friends, and track your progress!

- + )} diff --git a/src/pages/HomePage.tsx b/src/pages/HomePage.tsx index dce42934..58e32f3e 100644 --- a/src/pages/HomePage.tsx +++ b/src/pages/HomePage.tsx @@ -1,5 +1,5 @@ import { Layout } from '@/components/Layout' -import { Button } from '@/components/ui/button' +import { AnalyticsButton } from '@/components/ui/analytics-button' import { ChevronDown } from 'lucide-react' import { DateTime } from 'luxon' import { @@ -55,9 +55,14 @@ export const HomePage = () => { - + {

Focus starts here.

{error &&

{error}

} - + - +
diff --git a/src/pages/SettingsPage/Integrations/IntegrationSettings.tsx b/src/pages/SettingsPage/Integrations/IntegrationSettings.tsx index 16ec56cd..b633eb32 100644 --- a/src/pages/SettingsPage/Integrations/IntegrationSettings.tsx +++ b/src/pages/SettingsPage/Integrations/IntegrationSettings.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from 'react' import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' import { SpotifyIcon } from '@/components/icons/SpotifyIcon' -import { Button } from '@/components/ui/button' +import { AnalyticsButton } from '@/components/ui/analytics-button' import { SpotifyAuthService } from '@/lib/integrations/spotify/spotifyAuth' import { SpotifyApiService } from '@/lib/integrations/spotify/spotifyApi' import { logAndToastError } from '@/lib/utils/ebbError.util' @@ -121,25 +121,35 @@ export const IntegrationSettings = () => {
{activeService === 'spotify' ? ( - + ) : ( - + {activeService === 'apple' && ( @@ -165,8 +175,28 @@ export const IntegrationSettings = () => { - - + setShowUnlinkDialog(false)} + analyticsEvent="music_disconnect_clicked_canceled" + analyticsProperties={{ + destination: 'cancel_disconnect', + source: 'integration_settings' + }} + > + Cancel + + + Disconnect + diff --git a/src/pages/SettingsPage/Integrations/SlackSettings.tsx b/src/pages/SettingsPage/Integrations/SlackSettings.tsx index c696c426..a3e8891a 100644 --- a/src/pages/SettingsPage/Integrations/SlackSettings.tsx +++ b/src/pages/SettingsPage/Integrations/SlackSettings.tsx @@ -1,7 +1,7 @@ import { useState } from 'react' import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' import { SlackIcon } from '@/components/icons/SlackIcon' -import { Button } from '@/components/ui/button' +import { AnalyticsButton } from '@/components/ui/analytics-button' import { SlackDisconnectModal } from '@/components/SlackDisconnectModal' import { useSlackStatus } from '@/api/hooks/useSlack' @@ -73,26 +73,36 @@ export const SlackSettings = () => {
{slackStatus?.workspaces?.length || 0 > 0 ? ( - + ) : ( - + diff --git a/src/pages/SettingsPage/SettingsPage.tsx b/src/pages/SettingsPage/SettingsPage.tsx index 334c3735..26ee787f 100644 --- a/src/pages/SettingsPage/SettingsPage.tsx +++ b/src/pages/SettingsPage/SettingsPage.tsx @@ -1,7 +1,7 @@ import { Layout } from '@/components/Layout' import { ModeToggle } from '@/components/ModeToggle' import { useState, useEffect } from 'react' -import { Button } from '@/components/ui/button' +import { AnalyticsButton } from '@/components/ui/analytics-button' import { Dialog, @@ -198,12 +198,14 @@ export function SettingsPage() {
- +
@@ -255,20 +257,24 @@ export function SettingsPage() { - - + diff --git a/src/pages/ShortcutTutorialPage.tsx b/src/pages/ShortcutTutorialPage.tsx index a60be939..2c1dd0db 100644 --- a/src/pages/ShortcutTutorialPage.tsx +++ b/src/pages/ShortcutTutorialPage.tsx @@ -1,4 +1,4 @@ -import { Button } from '@/components/ui/button' +import { AnalyticsButton } from '@/components/ui/analytics-button' import { useNavigate } from 'react-router-dom' import { OnboardingUtils } from '@/lib/utils/onboarding.util' import { ShortcutInput } from '@/components/ShortcutInput' @@ -52,13 +52,18 @@ export const ShortcutTutorialPage = () => {

- + ) } diff --git a/src/pages/SlackOnboardingPage.tsx b/src/pages/SlackOnboardingPage.tsx index 406e0c7c..24edbee0 100644 --- a/src/pages/SlackOnboardingPage.tsx +++ b/src/pages/SlackOnboardingPage.tsx @@ -1,4 +1,4 @@ -import { Button } from '@/components/ui/button' +import { AnalyticsButton } from '@/components/ui/analytics-button' import { useNavigate } from 'react-router-dom' import { SlackIcon } from '@/components/icons/SlackIcon' import { OnboardingUtils } from '@/lib/utils/onboarding.util' @@ -35,22 +35,32 @@ export const SlackOnboardingPage = () => {
- + - +
{!user && ( diff --git a/src/pages/StartFlowPage/StartFlowPage.tsx b/src/pages/StartFlowPage/StartFlowPage.tsx index 4166fd2f..a66e8d7e 100644 --- a/src/pages/StartFlowPage/StartFlowPage.tsx +++ b/src/pages/StartFlowPage/StartFlowPage.tsx @@ -1,6 +1,6 @@ import { useState, useEffect, useCallback } from 'react' import { motion } from 'framer-motion' -import { Button } from '@/components/ui/button' +import { AnalyticsButton } from '@/components/ui/analytics-button' import { Card, CardContent } from '@/components/ui/card' import { TopNav } from '@/components/TopNav' import { TimeSelector } from '@/components/TimeSelector' @@ -17,6 +17,7 @@ import { logAndToastError } from '@/lib/utils/ebbError.util' import { error as logError } from '@tauri-apps/plugin-log' import { BlockingPreferenceApi } from '@/api/ebbApi/blockingPreferenceApi' import { usePostHog } from 'posthog-js/react' +import { AnalyticsService } from '@/lib/analytics' import { Input } from '@/components/ui/input' import { FlowSessionApi } from '@/api/ebbApi/flowSessionApi' import { SmartFocusSelector } from '@/pages/StartFlowPage/SmartFocusSelector' @@ -274,13 +275,20 @@ export const StartFlowPage = () => { const handleKeyDown = (e: KeyboardEvent) => { if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') { e.preventDefault() + // Track keyboard shortcut usage + AnalyticsService.trackEvent('start_focus_clicked', { + workflow_id: selectedWorkflowId || undefined, + workflow_name: selectedWorkflow?.name, + button_location: 'start_flow_page', + keyboard_shortcut_used: true, + }) handleBegin() } } window.addEventListener('keydown', handleKeyDown) return () => window.removeEventListener('keydown', handleKeyDown) - }, [handleBegin]) + }, [handleBegin, selectedWorkflowId, selectedWorkflow?.name]) return (
@@ -373,16 +381,23 @@ export const StartFlowPage = () => {
- +