From f52ca401cfb2af5965d503b69c2666ed07efd841 Mon Sep 17 00:00:00 2001 From: zihluwang Date: Mon, 19 Jan 2026 14:39:21 +0800 Subject: [PATCH] feat: implement JSON Viewer page and enhance Home page layout - Added a new JSON Viewer page for visualizing and querying JSON data using JSONPath. - Updated the Home page with a new header, main call-to-action, and feature highlights for the JSON Visualiser tool. - Removed unused state and components from the Home page to streamline the layout. --- src/page/home/index.tsx | 177 ++++++++------------------------- src/page/json-viewer/index.tsx | 151 ++++++++++++++++++++++++++++ src/router/index.tsx | 4 + 3 files changed, 195 insertions(+), 137 deletions(-) create mode 100644 src/page/json-viewer/index.tsx diff --git a/src/page/home/index.tsx b/src/page/home/index.tsx index 4a30aee..a645d5c 100644 --- a/src/page/home/index.tsx +++ b/src/page/home/index.tsx @@ -1,149 +1,52 @@ -import { useCallback, useMemo, useState } from "react" -import jp from "jsonpath" -import JsonTreeNode from "@/components/json-tree-node" +import { Link } from "react-router-dom" /** * Home page component that displays the main landing content. */ export default function Home() { - const initialData = { - centre_id: "LON-01", - location: "London", - is_active: true, - staff_members: [ - { id: 101, name: "Alice", roles: ["Admin", "Manager"] }, - { id: 102, name: "Bob", roles: ["Developer"] }, - ], - config: { - colour_scheme: "Dark Mode", - retention_days: 30, - }, - } - - const [jsonInput, setJsonInput] = useState(JSON.stringify(initialData, null, 2)) - const [query, setQuery] = useState("$.staff_members[*].name") - const [copied, setCopied] = useState(false) - - // Compute matching results - const result = useMemo(() => { - let parsed - try { - parsed = JSON.parse(jsonInput) - } catch (e) { - return { parsed: null, matchedPaths: [], matchedValues: [], error: (e as Error).message, queryError: null } - } - - try { - const nodes = jp.nodes(parsed, query) - return { - parsed, - matchedPaths: nodes.map((n) => jp.stringify(n.path)), - matchedValues: nodes.map((n) => n.value), - error: null, - queryError: null, - } - } catch (e) { - // When JSONPath expression is invalid, still display the JSON tree but with no matches - return { parsed, matchedPaths: [], matchedValues: [], error: null, queryError: (e as Error).message } - } - }, [jsonInput, query]) - - // Copy as CSV - const copyAsCsv = useCallback(() => { - if (result.matchedValues.length === 0) return - - const escapeCsvValue = (val: unknown): string => { - const str = typeof val === "object" ? JSON.stringify(val) : String(val) - if (str.includes(",") || str.includes('"') || str.includes("\n")) { - return `"${str.replace(/"/g, '""')}"` - } - return str - } - - const header = query - const rows = result.matchedValues.map(escapeCsvValue) - const csv = [header, ...rows].join("\n") - - navigator.clipboard.writeText(csv).then(() => { - setCopied(true) - setTimeout(() => setCopied(false), 2000) - }) - }, [query, result.matchedValues]) - return ( -
- {/* Left panel - 30% */} -
- {/* JSON Source - fills remaining height */} -
-
- - JSON Source - -
-