diff --git a/app/[slug]/PageClient.tsx b/app/[slug]/PageClient.tsx index 50187c9..490f78c 100644 --- a/app/[slug]/PageClient.tsx +++ b/app/[slug]/PageClient.tsx @@ -22,6 +22,7 @@ import SEOComponent from '@/app/components/theme/SEOComponent/SEOComponent'; import { detectBrowser } from '@/app/libs/helpers'; import EdgeIcon from '@/app/components/theme/Icon/edgeIcon'; import { useTheme } from '@/app/contexts/themeContext'; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from '@/app/libs/analytics'; const PageClient = ({ slug }: { slug: string }) => { const [browser, setBrowser] = useState('chrome'); @@ -51,6 +52,7 @@ const PageClient = ({ slug }: { slug: string }) => { const currentTool = developmentToolsRoutes.find( (tool: any) => tool.path === pathname ); + const toolName = meta_data?.meta_title?.split(' - ')?.[0] || pathname; return ( <> @@ -94,7 +96,18 @@ const PageClient = ({ slug }: { slug: string }) => { Swiftly document and share bugs like never before

- + + trackEvent('add_to_chrome_click', { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + section_name: 'Hero Section', + cta_text: browser === 'edge' ? 'Add to Edge' : 'Add to Chrome', + }) + } + >
{development_tools_list?.map((tool: any, index: any) => ( - + + trackEvent('dev_tool_card_click', { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: tool?.tool, + }) + } + >
@@ -1076,7 +1099,18 @@ const PageClient = ({ slug }: { slug: string }) => { Swiftly document and share bugs like never before

- + + trackEvent('add_to_chrome_click', { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + section_name: 'Bottom CTA Section', + cta_text: browser === 'edge' ? 'Add to Edge' : 'Add to Chrome', + }) + } + >
+
diff --git a/app/components/developmentToolsComponent/cssValidator.tsx b/app/components/developmentToolsComponent/cssValidator.tsx index 0546186..c0e8f5b 100644 --- a/app/components/developmentToolsComponent/cssValidator.tsx +++ b/app/components/developmentToolsComponent/cssValidator.tsx @@ -1,5 +1,6 @@ "use client"; -import React, { useMemo, useState } from "react"; +import React, { useEffect, useMemo, useRef, useState } from "react"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; type IssueType = "error" | "warning"; interface Issue { line: number; column: number; message: string; type: IssueType } @@ -55,6 +56,29 @@ const CssValidator = () => { const [css, setCss] = useState(`/* Paste CSS here */\nbody {\n font-family: system-ui\n color: #222;\n}`); const issues = useMemo(() => validateCss(css), [css]); const hasErrors = issues.some((i) => i.type === "error"); + const debounceTimerRef = useRef | null>(null); + const isFirstRenderRef = useRef(true); + + useEffect(() => { + if (isFirstRenderRef.current) { + isFirstRenderRef.current = false; + return; + } + if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current); + if (css.trim()) { + debounceTimerRef.current = setTimeout(() => { + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "CSS Validator", + tool_action: "Validate", + }); + }, 1000); + } + return () => { + if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current); + }; + }, [css]); return (
diff --git a/app/components/developmentToolsComponent/csvToExcelFileConvertor.tsx b/app/components/developmentToolsComponent/csvToExcelFileConvertor.tsx index 152886a..7a94914 100644 --- a/app/components/developmentToolsComponent/csvToExcelFileConvertor.tsx +++ b/app/components/developmentToolsComponent/csvToExcelFileConvertor.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useMemo, useState } from "react"; import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; type ParsedCSV = { rows: string[][]; @@ -116,6 +117,12 @@ const CsvToExcelFileConvertor = () => { a.click(); URL.revokeObjectURL(url); } + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "CSV to Excel File Convertor", + tool_action: "Download", + }); }; const clearAll = () => { diff --git a/app/components/developmentToolsComponent/csvToJson.tsx b/app/components/developmentToolsComponent/csvToJson.tsx index 182df85..d6647c0 100644 --- a/app/components/developmentToolsComponent/csvToJson.tsx +++ b/app/components/developmentToolsComponent/csvToJson.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useRef, useState } from "react"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; const detectDelimiter = (text: string): string => { const candidates = [",", ";", "\t", "|", ":"]; // common delimiters @@ -76,6 +77,12 @@ const CSVToJSON = () => { const data = parseCSV(csv, { delimiter, hasHeader, trim, ignoreEmpty }); try { setJsonOut(JSON.stringify(data, null, 2)); + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "CSV to JSON Converter", + tool_action: "Convert", + }); } catch { setJsonOut("[]"); } diff --git a/app/components/developmentToolsComponent/csvToTextConverter.tsx b/app/components/developmentToolsComponent/csvToTextConverter.tsx index a80ae1f..7f96a56 100644 --- a/app/components/developmentToolsComponent/csvToTextConverter.tsx +++ b/app/components/developmentToolsComponent/csvToTextConverter.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useMemo, useState, useRef } from "react"; import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; const CsvToTextConverter = () => { const [csvText, setCsvText] = useState(""); @@ -173,6 +174,12 @@ const CsvToTextConverter = () => { await navigator.clipboard.writeText(convertedText); setError("Text copied to clipboard successfully!"); setTimeout(() => setError(""), 3000); + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "CSV to TXT Converter", + tool_action: "Copy", + }); } catch (err) { setError("Failed to copy to clipboard"); } diff --git a/app/components/developmentToolsComponent/curlToCodeConverter.tsx b/app/components/developmentToolsComponent/curlToCodeConverter.tsx index 039a945..be1dc37 100644 --- a/app/components/developmentToolsComponent/curlToCodeConverter.tsx +++ b/app/components/developmentToolsComponent/curlToCodeConverter.tsx @@ -3,6 +3,7 @@ import React, { useState, useCallback } from "react"; import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss"; import CopyIcon from "../theme/Icon/copyIcon"; import ReloadIcon from "../theme/Icon/reload"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; // ─── Types ─────────────────────────────────────────────────────────────────── @@ -591,6 +592,12 @@ const CurlToCodeConverter = () => { await navigator.clipboard.writeText(output); setCopySuccess(true); setTimeout(() => setCopySuccess(false), 2000); + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "Curl To Code Converter", + tool_action: "Copy", + }); } catch { setError("Failed to copy to clipboard."); } diff --git a/app/components/developmentToolsComponent/decimalToAsciiConverter.tsx b/app/components/developmentToolsComponent/decimalToAsciiConverter.tsx index f4d5799..7648c76 100644 --- a/app/components/developmentToolsComponent/decimalToAsciiConverter.tsx +++ b/app/components/developmentToolsComponent/decimalToAsciiConverter.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useState } from "react"; import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; const DecimalToAsciiConverter = () => { const [input, setInput] = useState(""); @@ -35,7 +36,16 @@ const DecimalToAsciiConverter = () => { return; } - setOutput(convertDecimalToAscii(input)); + const result = convertDecimalToAscii(input); + setOutput(result); + if (!result.startsWith("Error")) { + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "Decimal to ASCII Converter", + tool_action: "Convert", + }); + } }; const clearAll = () => { diff --git a/app/components/developmentToolsComponent/decimalToBinaryConverter.tsx b/app/components/developmentToolsComponent/decimalToBinaryConverter.tsx index 35d0bab..9920749 100644 --- a/app/components/developmentToolsComponent/decimalToBinaryConverter.tsx +++ b/app/components/developmentToolsComponent/decimalToBinaryConverter.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; function parseDecimal(input: string): number | null { const clean = input.trim(); @@ -33,6 +34,18 @@ const DecimalToBinaryConverter: React.FC = () => { useEffect(() => { if (autoUpdate) convert(); }, [input, groupSize, padTo, autoUpdate, convert]); + const onConvertClick = useCallback(() => { + convert(); + if (parseDecimal(input) !== null) { + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "Decimal to Binary Converter", + tool_action: "Convert", + }); + } + }, [convert, input]); + const onCopy = useCallback(async () => { try { await navigator.clipboard.writeText(output); } catch (_) {} }, [output]); const onDownload = useCallback(() => { const blob = new Blob([output], { type: "text/plain;charset=utf-8" }); @@ -63,7 +76,7 @@ const DecimalToBinaryConverter: React.FC = () => { setAutoUpdate(e.target.checked)} /> Auto-update - +
diff --git a/app/components/developmentToolsComponent/decimalToGrayCode.tsx b/app/components/developmentToolsComponent/decimalToGrayCode.tsx index ac491ad..e50a6e1 100644 --- a/app/components/developmentToolsComponent/decimalToGrayCode.tsx +++ b/app/components/developmentToolsComponent/decimalToGrayCode.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useCallback, useMemo, useState } from "react"; import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; type ConvertOptions = { showMapping: boolean; @@ -83,6 +84,12 @@ const DecimalToGrayCode: React.FC = () => { if (hasInvalid) { setError("Some inputs were invalid. They are marked as Invalid."); } + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "Decimal to Grey Code", + tool_action: "Convert", + }); } catch (_e: unknown) { setError("Failed to convert. Please check your input."); } diff --git a/app/components/developmentToolsComponent/decimalToHexConverter.tsx b/app/components/developmentToolsComponent/decimalToHexConverter.tsx index a4df102..970ba00 100644 --- a/app/components/developmentToolsComponent/decimalToHexConverter.tsx +++ b/app/components/developmentToolsComponent/decimalToHexConverter.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; function parseDecimal(input: string): number | null { const clean = input.trim(); @@ -32,7 +33,19 @@ const DecimalToHexConverter: React.FC = () => { useEffect(() => { if (autoUpdate) convert(); }, [input, uppercase, autoUpdate, convert]); - const onCopy = useCallback(async () => { try { await navigator.clipboard.writeText(output); } catch (_) {} }, [output]); + const onCopy = useCallback(async () => { + try { + await navigator.clipboard.writeText(output); + if (output) { + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "Decimal to Hex Converter", + tool_action: "Copy", + }); + } + } catch (_) {} + }, [output]); const onDownload = useCallback(() => { const blob = new Blob([output], { type: "text/plain;charset=utf-8" }); const url = URL.createObjectURL(blob); diff --git a/app/components/developmentToolsComponent/decimalToOctalConverter.tsx b/app/components/developmentToolsComponent/decimalToOctalConverter.tsx index 5c6df2a..cc739c1 100644 --- a/app/components/developmentToolsComponent/decimalToOctalConverter.tsx +++ b/app/components/developmentToolsComponent/decimalToOctalConverter.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; function parseDecimal(input: string): number | null { const clean = input.trim(); @@ -30,6 +31,18 @@ const DecimalToOctalConverter: React.FC = () => { useEffect(() => { if (autoUpdate) convert(); }, [input, autoUpdate, convert]); + const onConvertClick = useCallback(() => { + convert(); + if (parseDecimal(input) !== null) { + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "Decimal to Octal Converter", + tool_action: "Convert", + }); + } + }, [convert, input]); + const onCopy = useCallback(async () => { try { await navigator.clipboard.writeText(output); } catch (_) {} }, [output]); const onDownload = useCallback(() => { const blob = new Blob([output], { type: "text/plain;charset=utf-8" }); @@ -60,7 +73,7 @@ const DecimalToOctalConverter: React.FC = () => { setAutoUpdate(e.target.checked)} /> Auto-update - +
diff --git a/app/components/developmentToolsComponent/epochConverter.tsx b/app/components/developmentToolsComponent/epochConverter.tsx index af8d025..bbfd506 100644 --- a/app/components/developmentToolsComponent/epochConverter.tsx +++ b/app/components/developmentToolsComponent/epochConverter.tsx @@ -7,6 +7,7 @@ import utc from "dayjs/plugin/utc"; import timezone from "dayjs/plugin/timezone"; import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss"; import { ReloadOutlined, DeleteOutlined, CopyOutlined, CheckOutlined } from "@ant-design/icons"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; dayjs.extend(relativeTime); dayjs.extend(utc); @@ -88,6 +89,15 @@ const EpochConverter = () => { } }; + const trackCopy = () => { + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "Unix Timestamp Converter", + tool_action: "Copy", + }); + }; + // Derived values for display const validTimestamp = /^-?\d+$/.test(timestamp); const currentDayjs = validTimestamp ? dayjs(isMilliseconds ? Number(timestamp) : Number(timestamp) * 1000) : null; @@ -180,6 +190,7 @@ const EpochConverter = () => { copyable={{ text: currentDayjs.format("YYYY-MM-DD HH:mm:ss"), icon: [...copyIconPair], + onCopy: trackCopy, }} className="text-lg font-mono text-green-400 [&_.ant-typography-copy]:text-primary" > @@ -196,6 +207,7 @@ const EpochConverter = () => { copyable={{ text: `${currentDayjs.utc().format("YYYY-MM-DD HH:mm:ss")} UTC`, icon: [...copyIconPair], + onCopy: trackCopy, }} className="text-lg font-mono text-blue-400 [&_.ant-typography-copy]:text-primary" > @@ -212,6 +224,7 @@ const EpochConverter = () => { copyable={{ text: currentDayjs.toISOString(), icon: [...copyIconPair], + onCopy: trackCopy, }} className="text-sm font-mono text-yellow-500 break-all [&_.ant-typography-copy]:text-primary" > diff --git a/app/components/developmentToolsComponent/excelCompare.tsx b/app/components/developmentToolsComponent/excelCompare.tsx index 7397451..45e4eea 100644 --- a/app/components/developmentToolsComponent/excelCompare.tsx +++ b/app/components/developmentToolsComponent/excelCompare.tsx @@ -1,5 +1,6 @@ "use client"; import React, { useMemo, useState } from "react"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; type CellState = "same" | "diff" | "added" | "removed"; @@ -145,6 +146,12 @@ const ExcelCompare = () => { a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "Excel Compare", + tool_action: "Download", + }); }; return ( diff --git a/app/components/developmentToolsComponent/fibonacciCalculator.tsx b/app/components/developmentToolsComponent/fibonacciCalculator.tsx index 1f5565b..9a2779c 100644 --- a/app/components/developmentToolsComponent/fibonacciCalculator.tsx +++ b/app/components/developmentToolsComponent/fibonacciCalculator.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useEffect, useRef, useState } from "react"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; type Mode = "nth" | "sequence"; @@ -98,6 +99,12 @@ const FibonacciCalculator: React.FC = () => { } else { setOutput(computeSequence(n)); } + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "Fabonacci Calculator", + tool_action: "Convert", + }); }; const onCopy = async () => { diff --git a/app/components/developmentToolsComponent/findAndReplaceString.tsx b/app/components/developmentToolsComponent/findAndReplaceString.tsx index ca21566..55994a7 100644 --- a/app/components/developmentToolsComponent/findAndReplaceString.tsx +++ b/app/components/developmentToolsComponent/findAndReplaceString.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useEffect, useMemo, useRef, useState } from "react"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; type Flags = { global: boolean; @@ -76,6 +77,7 @@ const FindAndReplaceString: React.FC = () => { setOutput(res.output); setCount(res.count); setError(res.error); + return res; }; useEffect(() => { @@ -83,6 +85,18 @@ const FindAndReplaceString: React.FC = () => { run(); }, [input, find, replace, autoUpdate, useRegex, wholeWord, flags]); + const handleReplaceClick = () => { + const res = run(); + if (!res.error) { + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "Find and Replace String", + tool_action: "Replace", + }); + } + }; + const onCopy = async () => { try { await navigator.clipboard.writeText(output); @@ -227,7 +241,7 @@ const FindAndReplaceString: React.FC = () => {
+
diff --git a/app/components/developmentToolsComponent/hexToBinaryConverter.tsx b/app/components/developmentToolsComponent/hexToBinaryConverter.tsx index 88aaafe..af81392 100644 --- a/app/components/developmentToolsComponent/hexToBinaryConverter.tsx +++ b/app/components/developmentToolsComponent/hexToBinaryConverter.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; function parseHex(input: string): string | null { const clean = input.replace(/\s+/g, "").replace(/^0x/i, ""); @@ -38,7 +39,19 @@ const HexToBinaryConverter: React.FC = () => { useEffect(() => { if (autoUpdate) convert(); }, [input, groupSize, autoUpdate, convert]); - const onCopy = useCallback(async () => { try { await navigator.clipboard.writeText(output); } catch (_) {} }, [output]); + const onCopy = useCallback(async () => { + try { + await navigator.clipboard.writeText(output); + if (output) { + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "Hex to Binary Converter", + tool_action: "Copy", + }); + } + } catch (_) {} + }, [output]); const onDownload = useCallback(() => { const blob = new Blob([output], { type: "text/plain;charset=utf-8" }); const url = URL.createObjectURL(blob); diff --git a/app/components/developmentToolsComponent/hexToCmykConverter.tsx b/app/components/developmentToolsComponent/hexToCmykConverter.tsx index 8ab1eff..fa36d56 100644 --- a/app/components/developmentToolsComponent/hexToCmykConverter.tsx +++ b/app/components/developmentToolsComponent/hexToCmykConverter.tsx @@ -2,6 +2,7 @@ import React, { useCallback, useEffect, useMemo, useState } from "react"; import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; function parseHex(hex: string): { r: number; g: number; b: number } | null { let h = hex.trim(); @@ -131,6 +132,12 @@ const HexToCmykConverter: React.FC = () => { const y = Math.round(cmyk.y * 100); const k = Math.round(cmyk.k * 100); await navigator.clipboard.writeText(`cmyk(${c}%, ${m}%, ${y}%, ${k}%)`); + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "HEX to CMYK Converter", + tool_action: "Copy", + }); } catch (_) {} }, [cmyk]); diff --git a/app/components/developmentToolsComponent/hexToPantone.tsx b/app/components/developmentToolsComponent/hexToPantone.tsx index 589d279..9d4d41c 100644 --- a/app/components/developmentToolsComponent/hexToPantone.tsx +++ b/app/components/developmentToolsComponent/hexToPantone.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useEffect, useMemo, useState, useCallback } from "react"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; type Pantone = { name: string; r: number; g: number; b: number }; @@ -56,6 +57,18 @@ const HexToPantone: React.FC = () => { try { await navigator.clipboard.writeText(hex.toUpperCase()); } catch (_) {} }, [hex]); + const onCopyPantoneName = useCallback(async (name: string) => { + try { + await navigator.clipboard.writeText(name); + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "Hex to Pantone", + tool_action: "Copy", + }); + } catch (_) {} + }, []); + return (
@@ -121,7 +134,7 @@ const HexToPantone: React.FC = () => {
{it.pantone.name}
diff --git a/app/components/developmentToolsComponent/hexToRGBConverter.tsx b/app/components/developmentToolsComponent/hexToRGBConverter.tsx index 2ef45f3..9e89089 100644 --- a/app/components/developmentToolsComponent/hexToRGBConverter.tsx +++ b/app/components/developmentToolsComponent/hexToRGBConverter.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useCallback, useMemo, useState } from "react"; import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; type ConvertOptions = { outputCssFunction: boolean; // rgb()/rgba() vs raw numbers @@ -65,6 +66,12 @@ const HexToRGBConverter: React.FC = () => { try { setError(""); setOutput(convertInputToOutput(input, options)); + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "Hex to RGB Converter", + tool_action: "Convert", + }); } catch (e: unknown) { setError("Failed to convert. Please check your input."); } diff --git a/app/components/developmentToolsComponent/hoursToSecounds.tsx b/app/components/developmentToolsComponent/hoursToSecounds.tsx index 5f35a01..732a32b 100644 --- a/app/components/developmentToolsComponent/hoursToSecounds.tsx +++ b/app/components/developmentToolsComponent/hoursToSecounds.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useMemo, useState } from "react"; import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; const HoursToSecounds = () => { const [hours, setHours] = useState(""); @@ -29,6 +30,12 @@ const HoursToSecounds = () => { const handleCopy = async () => { try { await navigator.clipboard.writeText(String(totalSeconds)); + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "Hours to Seconds", + tool_action: "Copy", + }); } catch (_) {} }; diff --git a/app/components/developmentToolsComponent/htmlCodeGenerator.tsx b/app/components/developmentToolsComponent/htmlCodeGenerator.tsx index 22b6da8..4752628 100644 --- a/app/components/developmentToolsComponent/htmlCodeGenerator.tsx +++ b/app/components/developmentToolsComponent/htmlCodeGenerator.tsx @@ -3,6 +3,7 @@ import React, { useState } from "react"; import CopyIcon from "../theme/Icon/copyIcon"; import { CheckIcon } from "../theme/Icon/checkIcon"; import DownOutlinedIcon from "../theme/Icon/downOutlinedIcon"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; const HtmlCodeGenerator: React.FC = () => { const [generated, setGenerated] = useState(""); @@ -62,6 +63,12 @@ const HtmlCodeGenerator: React.FC = () => { const data = await res.json(); if (data?.html) { setGenerated(data.html); + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "HTML Code Generator", + tool_action: "Generate", + }); } else if (data?.error) { setGenerated(``); } diff --git a/app/components/developmentToolsComponent/htmlEntitiesToTextConverter.tsx b/app/components/developmentToolsComponent/htmlEntitiesToTextConverter.tsx index 1f2513a..ceedd8c 100644 --- a/app/components/developmentToolsComponent/htmlEntitiesToTextConverter.tsx +++ b/app/components/developmentToolsComponent/htmlEntitiesToTextConverter.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useState } from "react"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; // Decode basic named entities and numeric (decimal/hex) references const decodeEntities = (input: string) => { @@ -34,7 +35,15 @@ const HtmlEntitiesToTextConverter = () => { const [input, setInput] = useState(""); const [output, setOutput] = useState(""); - const onConvert = () => setOutput(decodeEntities(input)); + const onConvert = () => { + setOutput(decodeEntities(input)); + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "HTML Entities to Text Converter", + tool_action: "Convert", + }); + }; const onCopy = async () => { try { await navigator.clipboard.writeText(output); } catch {} }; const onDownload = () => { const blob = new Blob([output], { type: "text/plain;charset=utf-8" }); diff --git a/app/components/developmentToolsComponent/htmlEscape.tsx b/app/components/developmentToolsComponent/htmlEscape.tsx index 4dc8078..d62a474 100644 --- a/app/components/developmentToolsComponent/htmlEscape.tsx +++ b/app/components/developmentToolsComponent/htmlEscape.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useEffect, useRef, useState } from "react"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; const NAMED_MAP: Record = { "&": "&", @@ -44,6 +45,16 @@ const HtmlEscape: React.FC = () => { run(); }, [input, autoUpdate, encodeQuotes, encodeNonAscii, useNamed]); + const handleEscapeClick = () => { + run(); + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "HTML Escape", + tool_action: "Escape", + }); + }; + const onCopy = async () => { try { await navigator.clipboard.writeText(output); @@ -136,7 +147,7 @@ const HtmlEscape: React.FC = () => {
+
diff --git a/app/components/developmentToolsComponent/htmlToMarkDownComponent.tsx b/app/components/developmentToolsComponent/htmlToMarkDownComponent.tsx index d6acd61..67cdd5c 100644 --- a/app/components/developmentToolsComponent/htmlToMarkDownComponent.tsx +++ b/app/components/developmentToolsComponent/htmlToMarkDownComponent.tsx @@ -5,6 +5,7 @@ import { Editor } from "@monaco-editor/react"; import TurndownService from "turndown"; import { ToastContainer, toast } from "react-toastify"; // Import ToastContainer and toast import "react-toastify/dist/ReactToastify.css"; // Import the toast CSS +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; const HtmlToMarkDownComponent = () => { const [htmlInput, setHtmlInput] = useState(""); @@ -62,6 +63,12 @@ const HtmlToMarkDownComponent = () => { // Update state with the Markdown output setMarkdownOutput(markdown); toast.success("Conversion successful!"); + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "HTML To Markdown", + tool_action: "Convert", + }); }; const removeCss = (html: any) => { diff --git a/app/components/developmentToolsComponent/htmlUnescape.tsx b/app/components/developmentToolsComponent/htmlUnescape.tsx index a5f3950..7e62c21 100644 --- a/app/components/developmentToolsComponent/htmlUnescape.tsx +++ b/app/components/developmentToolsComponent/htmlUnescape.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useEffect, useRef, useState } from "react"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; function unescapeHtml(input: string): string { // handle numeric decimal, hex, and common named entities @@ -28,6 +29,16 @@ const HtmlUnescape: React.FC = () => { run(); }, [input, autoUpdate]); + const handleUnescapeClick = () => { + run(); + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "HTML Unescape", + tool_action: "Unescape", + }); + }; + const onCopy = async () => { try { await navigator.clipboard.writeText(output); @@ -92,7 +103,7 @@ const HtmlUnescape: React.FC = () => {
+
diff --git a/app/components/developmentToolsComponent/idnEncode.tsx b/app/components/developmentToolsComponent/idnEncode.tsx index d474afe..1dab047 100644 --- a/app/components/developmentToolsComponent/idnEncode.tsx +++ b/app/components/developmentToolsComponent/idnEncode.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; type IndentSize = 2 | 4; @@ -154,7 +155,19 @@ const IdnEncode: React.FC = () => { setAutoUpdate(e.target.checked)} /> Auto-update - +
diff --git a/app/components/developmentToolsComponent/imageResizer.tsx b/app/components/developmentToolsComponent/imageResizer.tsx index b8d35a0..a8116b7 100644 --- a/app/components/developmentToolsComponent/imageResizer.tsx +++ b/app/components/developmentToolsComponent/imageResizer.tsx @@ -2,6 +2,7 @@ import React, { useEffect, useMemo, useRef, useState } from "react"; import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; const clamp = (value: number, min: number, max: number) => { return Math.min(max, Math.max(min, value)); @@ -178,6 +179,12 @@ const ImageResizer = () => { const base = fileName ? fileName.replace(/\.[^.]+$/, "") : "image"; a.download = `${base}-${targetWidth}x${targetHeight}.${format === "jpg" ? "jpg" : format}`; a.click(); + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "Image Resizer", + tool_action: "Download", + }); }; const targetBytes = useMemo(() => { @@ -220,6 +227,12 @@ const ImageResizer = () => { return URL.createObjectURL(finalBlob); }); setOutputBytes(finalBlob.size); + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "Image Resizer", + tool_action: "Compress", + }); } catch { setError("Failed to optimize file size"); } finally { diff --git a/app/components/developmentToolsComponent/imageToBase64Converter.tsx b/app/components/developmentToolsComponent/imageToBase64Converter.tsx index 852df36..ea9e8bf 100644 --- a/app/components/developmentToolsComponent/imageToBase64Converter.tsx +++ b/app/components/developmentToolsComponent/imageToBase64Converter.tsx @@ -2,6 +2,7 @@ import React, { useCallback, useRef, useState } from "react"; import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; const MAX_BYTES = 10 * 1024 * 1024; @@ -95,6 +96,12 @@ const ImageToBase64Converter: React.FC = () => { if (!outputText) return; try { await navigator.clipboard.writeText(outputText); + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "Image To Base64 Converter", + tool_action: "Copy", + }); } catch { // ignore } diff --git a/app/components/developmentToolsComponent/internetSpeedTest.tsx b/app/components/developmentToolsComponent/internetSpeedTest.tsx index 49eba26..cf47a22 100644 --- a/app/components/developmentToolsComponent/internetSpeedTest.tsx +++ b/app/components/developmentToolsComponent/internetSpeedTest.tsx @@ -2,6 +2,7 @@ import React, { useState, useRef, useEffect } from "react"; import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; interface SpeedTestResult { downloadSpeed: number; @@ -77,6 +78,12 @@ const InternetSpeedTest = () => { const testResult = await simulateSpeedTest(); setResult(testResult); setTestHistory((prev) => [testResult, ...prev.slice(0, 4)]); // Keep last 5 results + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "Internet Speed Test", + tool_action: "Run Test", + }); } catch (err) { setError("Speed test failed. Please try again."); } finally { diff --git a/app/components/developmentToolsComponent/ipToHexConverter.tsx b/app/components/developmentToolsComponent/ipToHexConverter.tsx index ca32541..6dabfd9 100644 --- a/app/components/developmentToolsComponent/ipToHexConverter.tsx +++ b/app/components/developmentToolsComponent/ipToHexConverter.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; type GroupSeparator = "" | " " | ":" | "-"; @@ -82,7 +83,7 @@ const IpToHexConverter: React.FC = () => { const fileRef = useRef(null); - const convert = useCallback(() => { + const convert = useCallback((trackOnSuccess = false) => { const v4 = parseIPv4(input); const v6 = v4 ? null : parseIPv6(input); if (!v4 && !v6) { @@ -95,6 +96,14 @@ const IpToHexConverter: React.FC = () => { let hex = toHex(bytes, uppercase, separator); if (prefix0x && separator === "") hex = "0x" + (uppercase ? hex.toUpperCase() : hex.toLowerCase()); setOutput(hex); + if (trackOnSuccess) { + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "IP to Hex Converter", + tool_action: "Convert", + }); + } }, [input, uppercase, separator, prefix0x]); useEffect(() => { if (autoUpdate) convert(); }, [input, uppercase, separator, prefix0x, autoUpdate, convert]); @@ -129,7 +138,7 @@ const IpToHexConverter: React.FC = () => { setAutoUpdate(e.target.checked)} /> Auto-update - +
diff --git a/app/components/developmentToolsComponent/ipv4SubnetCalculator.tsx b/app/components/developmentToolsComponent/ipv4SubnetCalculator.tsx index 7d8fdcc..00849e2 100644 --- a/app/components/developmentToolsComponent/ipv4SubnetCalculator.tsx +++ b/app/components/developmentToolsComponent/ipv4SubnetCalculator.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useCallback, useMemo, useState } from "react"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; interface SubnetResults { networkAddress: string; @@ -208,6 +209,12 @@ const Ipv4SubnetCalculator: React.FC = () => { } else { setCidrInput(String(cidr)); } + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "IPv4 Subnet Calculator", + tool_action: "Calculate", + }); } }, [ip, cidrInput, maskInput, inputMode]); diff --git a/app/components/developmentToolsComponent/javascriptEscape.tsx b/app/components/developmentToolsComponent/javascriptEscape.tsx index 794588d..5b8b062 100644 --- a/app/components/developmentToolsComponent/javascriptEscape.tsx +++ b/app/components/developmentToolsComponent/javascriptEscape.tsx @@ -1,5 +1,6 @@ "use client"; import React, { useMemo, useState } from "react"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; const Cmd = ({ children }: { children: string }) => ( {children} @@ -48,7 +49,17 @@ const JavaScriptEscape = () => { return `const s = "${inner}";`; }, [mode]); - const copy = async (text: string) => { try { await navigator.clipboard.writeText(text); } catch {} }; + const copy = async (text: string) => { + try { + await navigator.clipboard.writeText(text); + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "JavaScript Escape", + tool_action: "Copy", + }); + } catch {} + }; const clear = () => setRaw(""); return ( diff --git a/app/components/developmentToolsComponent/javascriptMinifierComponent.tsx b/app/components/developmentToolsComponent/javascriptMinifierComponent.tsx index 07bef66..8c37b0e 100644 --- a/app/components/developmentToolsComponent/javascriptMinifierComponent.tsx +++ b/app/components/developmentToolsComponent/javascriptMinifierComponent.tsx @@ -4,6 +4,7 @@ import MonacoEditor, { Editor } from "@monaco-editor/react"; import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss"; import { ToastContainer, toast } from "react-toastify"; // Import ToastContainer and toast import "react-toastify/dist/ReactToastify.css"; // Import the toast CSS +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; const JavascriptMinifierComponent = () => { const [inputJson, setInputJson] = useState(""); @@ -19,6 +20,12 @@ const JavascriptMinifierComponent = () => { const parsedJson = JSON.parse(inputJson); setMinifiedJson(JSON.stringify(parsedJson)); toast.success("JSON successfully minified!"); + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "JavaScript Minifier", + tool_action: "Minify", + }); } catch (error) { toast.error("Invalid JSON input. Please fix it and try again."); } diff --git a/app/components/developmentToolsComponent/javascriptRegexTester.tsx b/app/components/developmentToolsComponent/javascriptRegexTester.tsx index 9221b66..9116d22 100644 --- a/app/components/developmentToolsComponent/javascriptRegexTester.tsx +++ b/app/components/developmentToolsComponent/javascriptRegexTester.tsx @@ -1,5 +1,6 @@ "use client" import React, { useMemo, useState } from 'react' +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from '@/app/libs/analytics' type RegexFlags = { i: boolean @@ -79,6 +80,12 @@ const JavascriptRegexTester = () => { try { const r = new RegExp(regex.source, flagsString.replace('g','')) setText(text.replace(r, replaceWith)) + trackEvent('dev_tool_used', { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: 'JavaScript Regex Tester', + tool_action: 'Replace First', + }) } catch {} } @@ -87,10 +94,24 @@ const JavascriptRegexTester = () => { try { const r = new RegExp(regex.source, flagsString.includes('g') ? flagsString : flagsString + 'g') setText(text.replace(r, replaceWith)) + trackEvent('dev_tool_used', { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: 'JavaScript Regex Tester', + tool_action: 'Replace All', + }) } catch {} } - const runTest = () => setManualRunKey((k) => k + 1) + const runTest = () => { + setManualRunKey((k) => k + 1) + trackEvent('dev_tool_used', { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: 'JavaScript Regex Tester', + tool_action: 'Test Match', + }) + } const resetAll = () => { setPattern('') setText('') diff --git a/app/components/developmentToolsComponent/javascriptTester.tsx b/app/components/developmentToolsComponent/javascriptTester.tsx index e7fc28c..5e390b0 100644 --- a/app/components/developmentToolsComponent/javascriptTester.tsx +++ b/app/components/developmentToolsComponent/javascriptTester.tsx @@ -1,5 +1,6 @@ "use client"; import React, { useEffect, useMemo, useRef, useState } from "react"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; type LogEntry = { type: "log" | "warn" | "error"; message: string }; @@ -36,6 +37,12 @@ const JavaScriptTester = () => { setSrcDoc(wrap); // bump key to ensure fresh execution setReloadKey((k) => k + 1); + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "JavaScript Tester", + tool_action: "Run", + }); }; useEffect(() => { diff --git a/app/components/developmentToolsComponent/javascriptValidatorLinter.tsx b/app/components/developmentToolsComponent/javascriptValidatorLinter.tsx index fb2b4a2..58b325f 100644 --- a/app/components/developmentToolsComponent/javascriptValidatorLinter.tsx +++ b/app/components/developmentToolsComponent/javascriptValidatorLinter.tsx @@ -1,5 +1,6 @@ "use client"; -import React, { useMemo, useState } from "react"; +import React, { useEffect, useMemo, useRef, useState } from "react"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; type IssueType = "error" | "warning"; interface Issue { line: number; column: number; message: string; type: IssueType } @@ -66,6 +67,29 @@ const JavaScriptValidatorLinter = () => { const syntaxIssues = useMemo(() => checkSyntax(code), [code]); const lintIssues = useMemo(() => (autoLint ? basicLint(code) : []), [code, autoLint]); const hasErrors = syntaxIssues.length > 0; + const debounceTimerRef = useRef | null>(null); + const isFirstRenderRef = useRef(true); + + useEffect(() => { + if (isFirstRenderRef.current) { + isFirstRenderRef.current = false; + return; + } + if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current); + if (code.trim()) { + debounceTimerRef.current = setTimeout(() => { + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "JavaScript Validator & Linter", + tool_action: "Validate", + }); + }, 1000); + } + return () => { + if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current); + }; + }, [code]); return (
diff --git a/app/components/developmentToolsComponent/jsObfuscatorComponent.tsx b/app/components/developmentToolsComponent/jsObfuscatorComponent.tsx index f7668ae..bd2b1e7 100644 --- a/app/components/developmentToolsComponent/jsObfuscatorComponent.tsx +++ b/app/components/developmentToolsComponent/jsObfuscatorComponent.tsx @@ -4,6 +4,7 @@ import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss"; import { toast, ToastContainer } from "react-toastify"; import "react-toastify/dist/ReactToastify.css"; import { Editor } from "@monaco-editor/react"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; const JsObfuscatorComponent = () => { const [inputCode, setInputCode] = useState(""); @@ -22,6 +23,12 @@ const JsObfuscatorComponent = () => { controlFlowFlattening: true, }).getObfuscatedCode(); setOutputCode(obfuscatedCode); + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "JS Obfuscator", + tool_action: "Obfuscate", + }); } catch (error) { setOutputCode(""); toast.error(`Error obfuscating code: ${(error as Error).message}`); diff --git a/app/components/developmentToolsComponent/jsonCompare.tsx b/app/components/developmentToolsComponent/jsonCompare.tsx index 5781819..9a0edfd 100644 --- a/app/components/developmentToolsComponent/jsonCompare.tsx +++ b/app/components/developmentToolsComponent/jsonCompare.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useMemo, useState } from "react"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; type ChangeType = "added" | "removed" | "modified"; interface ChangeItem { path: string; type: ChangeType; left?: any; right?: any } @@ -124,6 +125,12 @@ const JSONCompare = () => { const onCompare = () => { try { parseJson(left); parseJson(right); setCompared(true); setError(null); + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "JSON Compare", + tool_action: "Compare", + }); } catch (e: any) { setError(e?.message || "Invalid JSON"); setCompared(false); } }; diff --git a/app/components/developmentToolsComponent/jsonMinifierComponent.tsx b/app/components/developmentToolsComponent/jsonMinifierComponent.tsx index 5d29c61..43e1b65 100644 --- a/app/components/developmentToolsComponent/jsonMinifierComponent.tsx +++ b/app/components/developmentToolsComponent/jsonMinifierComponent.tsx @@ -4,6 +4,7 @@ import { Editor } from "@monaco-editor/react"; import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss"; import { ToastContainer, toast } from "react-toastify"; // Import ToastContainer and toast import "react-toastify/dist/ReactToastify.css"; // Import the toast CSS +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; const JsonMinifierComponent = () => { const [inputJson, setInputJson] = useState(""); @@ -39,6 +40,12 @@ const JsonMinifierComponent = () => { const parsedJson = JSON.parse(inputJson); setMinifiedJson(JSON.stringify(parsedJson)); toast.success("JSON minified successfully!"); + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "JSON Minifier", + tool_action: "Minify", + }); } catch (error) { toast.error("Invalid JSON input. Please fix it and try again."); toast.success(""); diff --git a/app/components/developmentToolsComponent/jsonPrittifierComponent.tsx b/app/components/developmentToolsComponent/jsonPrittifierComponent.tsx index b2ddea3..c5b987b 100644 --- a/app/components/developmentToolsComponent/jsonPrittifierComponent.tsx +++ b/app/components/developmentToolsComponent/jsonPrittifierComponent.tsx @@ -5,6 +5,7 @@ import Editor from "@monaco-editor/react"; import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss"; import { ToastContainer, toast } from "react-toastify"; // Import ToastContainer and toast import "react-toastify/dist/ReactToastify.css"; // Import the toast CSS +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; const JsonPrettifierComponent = () => { const [inputJson, setInputJson] = useState(""); @@ -48,6 +49,12 @@ const JsonPrettifierComponent = () => { const parsedJson = JSON.parse(inputJson); setPrettifiedJson(JSON.stringify(parsedJson, null, indentation)); // Prettify JSON with selected indentation toast.success("JSON successfully prettified!"); + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "JSON Formatter/Prettifier Tool", + tool_action: "Prettify", + }); } catch (error) { toast.error("Invalid JSON input. Please fix it and try again."); } diff --git a/app/components/developmentToolsComponent/jsonToCsvConverter.tsx b/app/components/developmentToolsComponent/jsonToCsvConverter.tsx index 53eff7a..83c0553 100644 --- a/app/components/developmentToolsComponent/jsonToCsvConverter.tsx +++ b/app/components/developmentToolsComponent/jsonToCsvConverter.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useRef, useState } from "react"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; // Flatten nested objects: {user: {name: "Alice"}} => {"user.name": "Alice"} const flattenObject = (obj: any, prefix = ""): Record => { @@ -88,6 +89,12 @@ const JsonToCsvConverter: React.FC = () => { } const csv = jsonToCsv(parsed, { delimiter, includeHeader, flatten }); setCsvOut(csv); + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "JSON to CSV Converter", + tool_action: "Convert", + }); } catch (err) { setError("Invalid JSON format"); setCsvOut(""); diff --git a/app/components/developmentToolsComponent/jsonToTxt.tsx b/app/components/developmentToolsComponent/jsonToTxt.tsx index 5f1755c..6188a35 100644 --- a/app/components/developmentToolsComponent/jsonToTxt.tsx +++ b/app/components/developmentToolsComponent/jsonToTxt.tsx @@ -2,6 +2,7 @@ import React, { useEffect, useRef, useState } from "react"; import CopyButton from "../ui/CopyButton"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; type Mode = "json-lines" | "keys" | "values" | "key=value" | "path=value"; @@ -75,13 +76,21 @@ const JsonToTxt: React.FC = () => { const [uniqueOnly, setUniqueOnly] = useState(false); const fileRef = useRef(null); - const convert = () => { + const convert = (manual = false) => { let text = toText(input, mode, pretty); if (uniqueOnly && !text.startsWith("Error:")) { const lines = text.split(/\r?\n/); text = Array.from(new Set(lines)).join("\n"); } setOutput(text); + if (manual && !text.startsWith("Error:")) { + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "JSON to TXT Online Converter", + tool_action: "Convert", + }); + } }; useEffect(() => { @@ -178,7 +187,7 @@ const JsonToTxt: React.FC = () => {
+
diff --git a/app/components/developmentToolsComponent/jsonToYamlConverter.tsx b/app/components/developmentToolsComponent/jsonToYamlConverter.tsx index 3078074..5affa55 100644 --- a/app/components/developmentToolsComponent/jsonToYamlConverter.tsx +++ b/app/components/developmentToolsComponent/jsonToYamlConverter.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; type IndentSize = 2 | 4; @@ -118,7 +119,21 @@ const JsonToYamlConverter: React.FC = () => { setAutoUpdate(e.target.checked)} /> Auto-update - +
diff --git a/app/components/developmentToolsComponent/jsonValidator.tsx b/app/components/developmentToolsComponent/jsonValidator.tsx index f83b21a..5c29bb4 100644 --- a/app/components/developmentToolsComponent/jsonValidator.tsx +++ b/app/components/developmentToolsComponent/jsonValidator.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useState } from "react"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; interface ValidationResult { isValid: boolean; @@ -236,6 +237,12 @@ const JsonValidator = () => { const results = validateJson(jsonInput); setValidationResults(results); setIsLoading(false); + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "JSON Validator Online", + tool_action: "Validate", + }); }, 100); }; @@ -247,6 +254,12 @@ const JsonValidator = () => { const results = validateAgainstSchema(jsonInput, schemaInput); setSchemaValidation(results); setIsLoading(false); + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "JSON Validator Online", + tool_action: "Schema Validate", + }); }, 100); }; @@ -280,6 +293,12 @@ const JsonValidator = () => { ); setJsonInput(fixed); + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "JSON Validator Online", + tool_action: "Smart Repair", + }); }; const handleFileUpload = (event: React.ChangeEvent) => { diff --git a/app/components/developmentToolsComponent/jwtDecoder.tsx b/app/components/developmentToolsComponent/jwtDecoder.tsx index cae8b29..2df9983 100644 --- a/app/components/developmentToolsComponent/jwtDecoder.tsx +++ b/app/components/developmentToolsComponent/jwtDecoder.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; interface JwtParts { header: any; @@ -98,7 +99,19 @@ const JwtDecoder: React.FC = () => { useEffect(() => { if (autoUpdate) decode(); }, [input, formatJson, showExpiry, autoUpdate, decode]); - const onCopy = useCallback(async () => { try { await navigator.clipboard.writeText(output); } catch (_) {} }, [output]); + const onCopy = useCallback(async () => { + try { + await navigator.clipboard.writeText(output); + if (output) { + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "JWT Decoder", + tool_action: "Copy", + }); + } + } catch (_) {} + }, [output]); const onDownload = useCallback(() => { const blob = new Blob([output], { type: "text/plain;charset=utf-8" }); const url = URL.createObjectURL(blob); diff --git a/app/components/developmentToolsComponent/kmToMilesConverter.tsx b/app/components/developmentToolsComponent/kmToMilesConverter.tsx index 6bb02c7..7138a4a 100644 --- a/app/components/developmentToolsComponent/kmToMilesConverter.tsx +++ b/app/components/developmentToolsComponent/kmToMilesConverter.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; const KM_TO_MI = 1 / 1.609344; // exact inverse @@ -31,7 +32,19 @@ const KmToMilesConverter: React.FC = () => { useEffect(() => { if (autoUpdate) convert(); }, [input, precision, autoUpdate, convert]); - const onCopy = useCallback(async () => { try { await navigator.clipboard.writeText(output); } catch (_) {} }, [output]); + const onCopy = useCallback(async () => { + try { + await navigator.clipboard.writeText(output); + if (output) { + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "Kilometers to Miles Converter", + tool_action: "Copy", + }); + } + } catch (_) {} + }, [output]); const onDownload = useCallback(() => { const blob = new Blob([output], { type: "text/plain;charset=utf-8" }); const url = URL.createObjectURL(blob); diff --git a/app/components/developmentToolsComponent/lineCounterComponent.tsx b/app/components/developmentToolsComponent/lineCounterComponent.tsx index c1cf05d..9f479ae 100644 --- a/app/components/developmentToolsComponent/lineCounterComponent.tsx +++ b/app/components/developmentToolsComponent/lineCounterComponent.tsx @@ -1,12 +1,32 @@ "use client"; -import React, { useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; const LineCounterComponent = () => { const [text, setText] = useState(""); + const debounceTimerRef = useRef | null>(null); + + useEffect(() => { + return () => { + if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current); + }; + }, []); const handleTextChange = (e: any) => { - setText(e.target.value); + const value = e.target.value; + setText(value); + if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current); + if (value.trim()) { + debounceTimerRef.current = setTimeout(() => { + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "Line Counter", + tool_action: "Count", + }); + }, 1000); + } }; const countLines = () => { diff --git a/app/components/developmentToolsComponent/loremIpsumGeneratorComponent.tsx b/app/components/developmentToolsComponent/loremIpsumGeneratorComponent.tsx index dc4ce36..47bac24 100644 --- a/app/components/developmentToolsComponent/loremIpsumGeneratorComponent.tsx +++ b/app/components/developmentToolsComponent/loremIpsumGeneratorComponent.tsx @@ -2,6 +2,7 @@ import React, { useState } from "react"; import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss"; import { lorem } from "@/app/libs/constants"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; const LoremIpsumGeneratorComponent = () => { const [count, setCount] = useState(1); @@ -59,6 +60,12 @@ const LoremIpsumGeneratorComponent = () => { } setLoremText(output); + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "Lorem Ipsum Generator", + tool_action: "Generate", + }); }; const copyToClipboard = (text: string) => { diff --git a/app/components/developmentToolsComponent/lowerCaseConverterComponent.tsx b/app/components/developmentToolsComponent/lowerCaseConverterComponent.tsx index 64d3abd..f0e96fc 100644 --- a/app/components/developmentToolsComponent/lowerCaseConverterComponent.tsx +++ b/app/components/developmentToolsComponent/lowerCaseConverterComponent.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useState } from "react"; import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; const LowerCaseConverterComponent = () => { const [inputText, setInputText] = useState(""); @@ -13,6 +14,12 @@ const LowerCaseConverterComponent = () => { const transformToLowerCase = () => { setTransformedText(inputText.toLowerCase()); + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "Text to Lowercase Converter", + tool_action: "Convert", + }); }; const copyToClipboard = () => { diff --git a/app/components/developmentToolsComponent/markDownToHTMLComponent.tsx b/app/components/developmentToolsComponent/markDownToHTMLComponent.tsx index 68a52ae..0ad976c 100644 --- a/app/components/developmentToolsComponent/markDownToHTMLComponent.tsx +++ b/app/components/developmentToolsComponent/markDownToHTMLComponent.tsx @@ -5,6 +5,7 @@ import { Editor } from "@monaco-editor/react"; import { marked } from "marked"; // Import marked.js for Markdown to HTML conversion import { toast, ToastContainer } from "react-toastify"; // Import toast and ToastContainer import "react-toastify/dist/ReactToastify.css"; // Import toast styles +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; const MarkDownToHtmlComponent = () => { const [markdownInput, setMarkdownInput] = useState(""); @@ -34,6 +35,12 @@ const MarkDownToHtmlComponent = () => { const html: any = marked(markdownWithClassesRestored); setHtmlOutput(html); toast.success("Conversion successful!"); // Display success toast + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "Markdown To HTML", + tool_action: "Convert", + }); }; const handleClear = () => { diff --git a/app/components/developmentToolsComponent/markdownFormatter.tsx b/app/components/developmentToolsComponent/markdownFormatter.tsx index 8b6d5e5..dc71e8b 100644 --- a/app/components/developmentToolsComponent/markdownFormatter.tsx +++ b/app/components/developmentToolsComponent/markdownFormatter.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useEffect, useRef, useState } from "react"; +import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics"; // Lightweight markdown formatter: trims trailing spaces, normalizes heading spaces, // ensures single blank lines between blocks, normalizes list/blockquote/code fences. @@ -78,6 +79,16 @@ const MarkdownFormatter: React.FC = () => { ); }; + const onFormatClick = () => { + convert(); + trackEvent("dev_tool_used", { + page_type: PAGE_TYPE, + platform: getRuntimePlatform(), + tool_name: "Markdown Formatter", + tool_action: "Format", + }); + }; + useEffect(() => { if (autoUpdate) convert(); }, [ @@ -154,7 +165,7 @@ const MarkdownFormatter: React.FC = () => {
- +