Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ module.exports = {
{
group: ['../pages/**', '../../pages/**', '../../../pages/**'],
message: 'Use @/pages/* instead of relative paths'
}
},
]
}],
},
Expand Down Expand Up @@ -84,7 +84,7 @@ module.exports = {
{
"group": ["../db/**", "../../db/**"],
"message": "Architecture: No direct DB imports from components. Import a hook/API instead."
}
},
]
}]
}
Expand Down Expand Up @@ -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"
}
]
}]
}
}
],
}
2 changes: 2 additions & 0 deletions src/api/hooks/useKanbanBoard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 ---
Expand Down Expand Up @@ -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 })
Expand Down
11 changes: 8 additions & 3 deletions src/components/ActiveDevicesSettings.tsx
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -149,13 +149,18 @@ export function ActiveDevicesSettings({ user, maxDevices }: ActiveDevicesSetting
This Device
</Badge>
) : (
<Button
<AnalyticsButton
variant="destructive"
size="sm"
onClick={() => handleDeviceLogout(device.id)}
analyticsEvent="deactivate_device_clicked"
analyticsProperties={{
context: 'deactivate_device',
button_location: 'active_devices_settings'
}}
>
Deactivate
</Button>
</AnalyticsButton>
)}
</div>
</div>
Expand Down
29 changes: 22 additions & 7 deletions src/components/AppSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -514,7 +514,7 @@ export function AppSelector({

{onIsAllowListChange && (
<div className="flex items-center gap-1">
<Button
<AnalyticsButton
variant="ghost"
size="sm"
className={cn(
Expand All @@ -525,25 +525,35 @@ export function AppSelector({
e.stopPropagation()
handleAllowListChange(false)
}}
analyticsEvent="block_list_clicked"
analyticsProperties={{
context: 'block_list',
button_location: 'app_selector'
}}
>
Block
</Button>
</AnalyticsButton>

{!canUseAllowList ? (
<PaywallDialog>
<Button
<AnalyticsButton
variant="ghost"
size="sm"
className={cn(
'h-6 px-2 text-xs text-muted-foreground/80 hover:text-foreground',
isAllowList && 'bg-muted/50'
)}
analyticsEvent="allow_list_clicked"
analyticsProperties={{
context: 'allow_list',
button_location: 'app_selector'
}}
>
Allow
</Button>
</AnalyticsButton>
</PaywallDialog>
) : (
<Button
<AnalyticsButton
variant="ghost"
size="sm"
className={cn(
Expand All @@ -554,9 +564,14 @@ export function AppSelector({
e.stopPropagation()
handleAllowListChange(true)
}}
analyticsEvent="allow_list_clicked"
analyticsProperties={{
context: 'allow_list',
button_location: 'app_selector'
}}
>
Allow
</Button>
</AnalyticsButton>
)}
</div>
)}
Expand Down
57 changes: 40 additions & 17 deletions src/components/FriendsComparisonCard.tsx
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -173,16 +173,18 @@ const PendingInvitesTab = () => {
<div className="text-xs text-muted-foreground">Email: {invite.from_auth_user_email || invite.to_email}</div>
</div>
<div className="flex items-center gap-2">
<Button
<AnalyticsButton
analyticsEvent="accept_friend_request_clicked"
size="sm"
onClick={() => handleAcceptInviteWithLoading(invite.id)}
className="h-8 px-3"
loading={isResponding && processingRequest?.requestId === invite.id && processingRequest?.action === 'accept'}
icon={<Check className="h-3 w-3" />}
>
Accept
</Button>
<Button
</AnalyticsButton>
<AnalyticsButton
analyticsEvent="decline_friend_request_clicked"
size="sm"
variant="outline"
onClick={() => handleDeclineInviteWithLoading(invite.id)}
Expand All @@ -191,21 +193,22 @@ const PendingInvitesTab = () => {
icon={<X className="h-3 w-3" />}
>
Decline
</Button>
</AnalyticsButton>
</div>
</div>
))}
</div>
{receivedRequests.length > visibleReceived && (
<div className="text-center mt-4">
<Button
<AnalyticsButton
analyticsEvent="load_more_received_invites_clicked"
variant="outline"
size="sm"
onClick={handleLoadMoreReceived}
className="text-xs"
>
Load More ({receivedRequests.length - visibleReceived} remaining)
</Button>
</AnalyticsButton>
</div>
)}
</div>
Expand All @@ -232,14 +235,15 @@ const PendingInvitesTab = () => {
</div>
{sentRequests.length > visibleSent && (
<div className="text-center mt-4">
<Button
<AnalyticsButton
analyticsEvent="load_more_sent_invites_clicked"
variant="outline"
size="sm"
onClick={handleLoadMoreSent}
className="text-xs"
>
Load More ({sentRequests.length - visibleSent} remaining)
</Button>
</AnalyticsButton>
</div>
)}
</div>
Expand Down Expand Up @@ -295,9 +299,16 @@ const EmptyFriendsState = () => {
required
disabled={isInviting}
/>
<Button type="submit" size="lg" className="flex items-center gap-2" loading={isInviting} icon={<Send className="h-5 w-5" />}>
<AnalyticsButton
analyticsEvent="invite_friends_clicked"
type="submit"
size="lg"
className="flex items-center gap-2"
loading={isInviting}
icon={<Send className="h-5 w-5" />}
>
Invite
</Button>
</AnalyticsButton>
</div>
</form>
</div>
Expand Down Expand Up @@ -358,10 +369,16 @@ export const FriendsComparisonCard = ({
}
</p>
</div>
<Button variant="outline" size="sm" className="flex items-center gap-1" onClick={handleInviteClick}>
<AnalyticsButton
analyticsEvent="invite_friends_clicked"
variant="outline"
size="sm"
className="flex items-center gap-1"
onClick={handleInviteClick}
>
<UserPlus className="h-4 w-4" />
Invite Friends
</Button>
</AnalyticsButton>
</div>
</CardHeader>
<CardContent>
Expand Down Expand Up @@ -429,17 +446,23 @@ export const FriendsComparisonCard = ({
/>
</div>
<div className="flex justify-end gap-2">
<Button
<AnalyticsButton
analyticsEvent="invite_friends_canceled"
type="button"
variant="outline"
onClick={() => setShowInviteModal(false)}
disabled={isInviting}
>
Cancel
</Button>
<Button type="submit" loading={isInviting} icon={<Send className="h-5 w-5" />}>
</AnalyticsButton>
<AnalyticsButton
analyticsEvent="invite_friends_sent"
type="submit"
loading={isInviting}
icon={<Send className="h-5 w-5" />}
>
Send Invite
</Button>
</AnalyticsButton>
</div>
</div>
</form>
Expand Down
8 changes: 4 additions & 4 deletions src/components/ModeToggle.tsx
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -15,11 +15,11 @@ export function ModeToggle() {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="icon">
<AnalyticsButton variant="outline" size="icon" analyticsEvent="mode_toggle_clicked">
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
<span className="sr-only">Toggle theme</span>
</Button>
<span className="sr -only">Toggle theme</span>
</AnalyticsButton>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => setTheme('light')}>
Expand Down
7 changes: 4 additions & 3 deletions src/components/NotificationBanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -51,14 +51,15 @@ export const NotificationBanner: React.FC = () => {
<span className="font-medium">{activeNotification.content}</span>
<div className="flex items-center gap-2">
{notificationAction && (
<Button
<AnalyticsButton
analyticsEvent="notification_dismissed"
variant="outline"
size="sm"
className="border-white/30 text-white hover:bg-white/10 hover:text-white"
onClick={notificationAction.action}
>
{notificationAction.label}
</Button>
</AnalyticsButton>
)}
<button
className="p-1 rounded hover:bg-primary/80 transition-colors"
Expand Down
12 changes: 8 additions & 4 deletions src/components/NotificationPanel/ActionButton.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Button } from '@/components/ui/button'
import { AnalyticsButton } from '@/components/ui/analytics-button'
import { Hotkey } from '@/components/ui/hotkey'
import { cn } from '@/lib/utils/tailwind.util'
import { AnalyticsEvent } from '../../lib/analytics'

interface ActionButtonProps {
buttonText: string
Expand All @@ -10,6 +11,7 @@ interface ActionButtonProps {
shortcutParts: string[]
pressedKeys: Set<string>
onClick: () => void
analyticsEvent: AnalyticsEvent
}

const getHotkeyColor = (iconColor: string): string => {
Expand All @@ -27,10 +29,11 @@ export const ActionButton = ({
iconColor,
shortcutParts,
pressedKeys,
onClick
onClick,
analyticsEvent
}: ActionButtonProps) => {
return (
<Button
<AnalyticsButton
variant="outline"
size="sm"
className={cn(
Expand All @@ -40,6 +43,7 @@ export const ActionButton = ({
)}
onClick={onClick}
disabled={buttonState !== 'idle' || isDisabled}
analyticsEvent={analyticsEvent}
>
<span>
{buttonState === 'idle' && buttonText}
Expand Down Expand Up @@ -76,6 +80,6 @@ export const ActionButton = ({
})}
</div>
)}
</Button>
</AnalyticsButton>
)
}
Loading