diff --git a/CHANGELOG.md b/CHANGELOG.md index 1242fbd..f303c7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ### Improved - **Cleaner site navigation.** Dashboard, Uptime, Funnels, and Settings now use an underline tab bar instead of floating buttons. The active section is highlighted with an orange underline, making it easy to see where you are and switch between views. +- **Consistent icon style.** All dashboard icons now use a single, unified icon set for a cleaner look across Technology, Locations, Campaigns, and Referrers panels. ### Fixed diff --git a/components/OfflineBanner.tsx b/components/OfflineBanner.tsx index bca7c20..784b8d9 100644 --- a/components/OfflineBanner.tsx +++ b/components/OfflineBanner.tsx @@ -1,13 +1,13 @@ 'use client'; -import { FiWifiOff } from 'react-icons/fi'; +import { WifiSlash } from '@phosphor-icons/react'; export function OfflineBanner({ isOnline }: { isOnline: boolean }) { if (isOnline) return null; return (
- + You are currently offline. Changes may not be saved.
); diff --git a/components/dashboard/Campaigns.tsx b/components/dashboard/Campaigns.tsx index defc79e..9f0ffe0 100644 --- a/components/dashboard/Campaigns.tsx +++ b/components/dashboard/Campaigns.tsx @@ -9,7 +9,7 @@ import { Modal, ArrowRightIcon } from '@ciphera-net/ui' import { ListSkeleton } from '@/components/skeletons' import { getCampaigns, CampaignStat } from '@/lib/api/stats' import { getReferrerFavicon, getReferrerIcon, getReferrerDisplayName } from '@/lib/utils/icons' -import { FaBullhorn } from 'react-icons/fa' +import { Megaphone } from '@phosphor-icons/react' import UtmBuilder from '@/components/tools/UtmBuilder' import { type DimensionFilter } from '@/lib/filters' @@ -190,7 +190,7 @@ export default function Campaigns({ siteId, dateRange, filters, onFilter }: Camp ) : (
- +

Track your marketing campaigns diff --git a/components/dashboard/Locations.tsx b/components/dashboard/Locations.tsx index 5cc15d4..74f819e 100644 --- a/components/dashboard/Locations.tsx +++ b/components/dashboard/Locations.tsx @@ -9,8 +9,7 @@ import iso3166 from 'iso-3166-2' import WorldMap from './WorldMap' import { Modal, GlobeIcon } from '@ciphera-net/ui' import { ListSkeleton } from '@/components/skeletons' -import { SiTorproject } from 'react-icons/si' -import { FaUserSecret, FaSatellite } from 'react-icons/fa' +import { ShieldCheck, Detective, Broadcast } from '@phosphor-icons/react' import { getCountries, getCities, getRegions } from '@/lib/api/stats' import { type DimensionFilter } from '@/lib/filters' @@ -69,11 +68,11 @@ export default function Locations({ countries, cities, regions, geoDataLevel = ' switch (countryCode) { case 'T1': - return + return case 'A1': - return + return case 'A2': - return + return case 'O1': case 'EU': case 'AP': diff --git a/components/dashboard/TechSpecs.tsx b/components/dashboard/TechSpecs.tsx index d31c27e..d9251ec 100644 --- a/components/dashboard/TechSpecs.tsx +++ b/components/dashboard/TechSpecs.tsx @@ -5,7 +5,7 @@ import { logger } from '@/lib/utils/logger' import { formatNumber } from '@ciphera-net/ui' import { useTabListKeyboard } from '@/lib/hooks/useTabListKeyboard' import { getBrowserIcon, getOSIcon, getDeviceIcon } from '@/lib/utils/icons' -import { MdMonitor } from 'react-icons/md' +import { Monitor } from '@phosphor-icons/react' import { Modal, GridIcon } from '@ciphera-net/ui' import { ListSkeleton } from '@/components/skeletons' import { getBrowsers, getOS, getDevices, getScreenResolutions } from '@/lib/api/stats' @@ -64,7 +64,7 @@ export default function TechSpecs({ browsers, os, devices, screenResolutions, co data = res.map(d => ({ name: d.device, pageviews: d.pageviews, icon: getDeviceIcon(d.device) })) } else if (activeTab === 'screens') { const res = await getScreenResolutions(siteId, dateRange.start, dateRange.end, 100) - data = res.map(s => ({ name: s.screen_resolution, pageviews: s.pageviews, icon: })) + data = res.map(s => ({ name: s.screen_resolution, pageviews: s.pageviews, icon: })) } setFullData(filterUnknown(data)) } catch (e) { @@ -88,7 +88,7 @@ export default function TechSpecs({ browsers, os, devices, screenResolutions, co case 'devices': return devices.map(d => ({ name: d.device, pageviews: d.pageviews, icon: getDeviceIcon(d.device) })) case 'screens': - return screenResolutions.map(s => ({ name: s.screen_resolution, pageviews: s.pageviews, icon: })) + return screenResolutions.map(s => ({ name: s.screen_resolution, pageviews: s.pageviews, icon: })) default: return [] } diff --git a/components/tools/UtmBuilder.tsx b/components/tools/UtmBuilder.tsx index 7f5ad34..8bced1b 100644 --- a/components/tools/UtmBuilder.tsx +++ b/components/tools/UtmBuilder.tsx @@ -2,7 +2,7 @@ import { useState, useEffect } from 'react' import { logger } from '@/lib/utils/logger' -import { CopyIcon, CheckIcon } from '@radix-ui/react-icons' +import { Copy, Check } from '@phosphor-icons/react' import { listSites, Site } from '@/lib/api/sites' import { Select, Input, Button } from '@ciphera-net/ui' @@ -205,7 +205,7 @@ export default function UtmBuilder({ initialSiteId }: UtmBuilderProps) { className="ml-4 shrink-0 h-9 w-9 p-0 rounded-lg" title="Copy to clipboard" > - {copied ? : } + {copied ? : }

)} diff --git a/lib/utils/icons.tsx b/lib/utils/icons.tsx index f3cca87..2b45fec 100644 --- a/lib/utils/icons.tsx +++ b/lib/utils/icons.tsx @@ -1,98 +1,80 @@ import React from 'react' +import { + Globe, + WindowsLogo, + AppleLogo, + LinuxLogo, + AndroidLogo, + Question, + DeviceMobile, + DeviceTablet, + Desktop, + GoogleLogo, + FacebookLogo, + XLogo, + LinkedinLogo, + InstagramLogo, + GithubLogo, + YoutubeLogo, + RedditLogo, + Robot, +} from '@phosphor-icons/react' /** * Google's public favicon service base URL. * Append `?domain=&sz=` to get a favicon. */ export const FAVICON_SERVICE_URL = 'https://www.google.com/s2/favicons' -import { - FaChrome, - FaFirefox, - FaSafari, - FaEdge, - FaOpera, - FaInternetExplorer, - FaWindows, - FaApple, - FaLinux, - FaAndroid, - FaDesktop, - FaMobileAlt, - FaTabletAlt, - FaGoogle, - FaFacebook, - FaLinkedin, - FaInstagram, - FaGithub, - FaYoutube, - FaReddit, - FaQuestion, - FaGlobe -} from 'react-icons/fa' -import { FaXTwitter } from 'react-icons/fa6' -import { SiBrave, SiOpenai, SiPerplexity, SiAnthropic, SiGooglegemini } from 'react-icons/si' -import { RiRobot2Fill } from 'react-icons/ri' -import { MdDeviceUnknown, MdSmartphone, MdTabletMac, MdDesktopWindows } from 'react-icons/md' export function getBrowserIcon(browserName: string) { - if (!browserName) return - const lower = browserName.toLowerCase() - if (lower.includes('chrome')) return - if (lower.includes('firefox')) return - if (lower.includes('safari')) return - if (lower.includes('edge')) return - if (lower.includes('opera')) return - if (lower.includes('ie') || lower.includes('explorer')) return - if (lower.includes('brave')) return - - return + if (!browserName) return + return } export function getOSIcon(osName: string) { - if (!osName) return + if (!osName) return const lower = osName.toLowerCase() - if (lower.includes('win')) return - if (lower.includes('mac') || lower.includes('ios')) return - if (lower.includes('linux') || lower.includes('ubuntu') || lower.includes('debian')) return - if (lower.includes('android')) return - - return + if (lower.includes('win')) return + if (lower.includes('mac') || lower.includes('ios')) return + if (lower.includes('linux') || lower.includes('ubuntu') || lower.includes('debian')) return + if (lower.includes('android')) return + + return } export function getDeviceIcon(deviceName: string) { - if (!deviceName) return + if (!deviceName) return const lower = deviceName.toLowerCase() - if (lower.includes('mobile') || lower.includes('phone')) return - if (lower.includes('tablet') || lower.includes('ipad')) return - if (lower.includes('desktop') || lower.includes('laptop')) return - - return + if (lower.includes('mobile') || lower.includes('phone')) return + if (lower.includes('tablet') || lower.includes('ipad')) return + if (lower.includes('desktop') || lower.includes('laptop')) return + + return } export function getReferrerIcon(referrerName: string) { - if (!referrerName) return + if (!referrerName) return const lower = referrerName.toLowerCase() - if (lower.includes('google')) return - if (lower.includes('facebook')) return - if (lower.includes('twitter') || lower.includes('t.co') || lower.includes('x.com')) return - if (lower.includes('linkedin')) return - if (lower.includes('instagram')) return - if (lower.includes('github')) return - if (lower.includes('youtube')) return - if (lower.includes('reddit')) return + if (lower.includes('google')) return + if (lower.includes('facebook')) return + if (lower.includes('twitter') || lower.includes('t.co') || lower.includes('x.com')) return + if (lower.includes('linkedin')) return + if (lower.includes('instagram')) return + if (lower.includes('github')) return + if (lower.includes('youtube')) return + if (lower.includes('reddit')) return // AI assistants and search tools - if (lower.includes('chatgpt') || lower.includes('openai')) return - if (lower.includes('perplexity')) return - if (lower.includes('claude') || lower.includes('anthropic')) return - if (lower.includes('gemini')) return - if (lower.includes('copilot')) return - if (lower.includes('deepseek')) return - if (lower.includes('grok') || lower.includes('x.ai')) return - if (lower.includes('phind')) return - if (lower.includes('you.com')) return + if (lower.includes('chatgpt') || lower.includes('openai')) return + if (lower.includes('perplexity')) return + if (lower.includes('claude') || lower.includes('anthropic')) return + if (lower.includes('gemini')) return + if (lower.includes('copilot')) return + if (lower.includes('deepseek')) return + if (lower.includes('grok') || lower.includes('x.ai')) return + if (lower.includes('phind')) return + if (lower.includes('you.com')) return - // Try to use a generic globe or maybe check if it is a URL - return + return } const REFERRER_NO_FAVICON = ['direct', 'unknown', ''] diff --git a/next.config.ts b/next.config.ts index 0052619..05f96a5 100644 --- a/next.config.ts +++ b/next.config.ts @@ -30,7 +30,7 @@ const nextConfig: NextConfig = { // * Privacy-first: Disable analytics and telemetry productionBrowserSourceMaps: false, experimental: { - optimizePackageImports: ['react-icons'], + optimizePackageImports: ['@phosphor-icons/react'], }, images: { remotePatterns: [ diff --git a/package-lock.json b/package-lock.json index fe86ed2..7c842cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "@ciphera-net/ui": "^0.0.93", "@ducanh2912/next-pwa": "^10.2.9", - "@radix-ui/react-icons": "^1.3.0", + "@phosphor-icons/react": "^2.1.10", "@simplewebauthn/browser": "^13.2.2", "@stripe/react-stripe-js": "^5.6.0", "@stripe/stripe-js": "^8.7.0", @@ -26,7 +26,6 @@ "next": "^16.1.1", "react": "^19.2.3", "react-dom": "^19.2.3", - "react-icons": "^5.5.0", "react-markdown": "^10.1.0", "react-simple-maps": "^3.0.0", "recharts": "^2.15.0", @@ -3274,6 +3273,19 @@ "node": ">=12.4.0" } }, + "node_modules/@phosphor-icons/react": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@phosphor-icons/react/-/react-2.1.10.tgz", + "integrity": "sha512-vt8Tvq8GLjheAZZYa+YG/pW7HDbov8El/MANW8pOAz4eGxrwhnbfrQZq0Cp4q8zBEu8NIhHdnr+r8thnfRSNYA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">= 16.8", + "react-dom": ">= 16.8" + } + }, "node_modules/@radix-ui/react-icons": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.2.tgz", @@ -11081,15 +11093,6 @@ "react": "^19.2.4" } }, - "node_modules/react-icons": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz", - "integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==", - "license": "MIT", - "peerDependencies": { - "react": "*" - } - }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", diff --git a/package.json b/package.json index 99d6af7..6cd541d 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "dependencies": { "@ciphera-net/ui": "^0.0.93", "@ducanh2912/next-pwa": "^10.2.9", - "@radix-ui/react-icons": "^1.3.0", + "@phosphor-icons/react": "^2.1.10", "@simplewebauthn/browser": "^13.2.2", "@stripe/react-stripe-js": "^5.6.0", "@stripe/stripe-js": "^8.7.0", @@ -30,7 +30,6 @@ "next": "^16.1.1", "react": "^19.2.3", "react-dom": "^19.2.3", - "react-icons": "^5.5.0", "react-markdown": "^10.1.0", "react-simple-maps": "^3.0.0", "recharts": "^2.15.0",