diff --git a/eslint.config.js b/eslint.config.js index 3d1216f..944f4f8 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,4 +1,5 @@ import js from "@eslint/js"; +import pluginQuery from "@tanstack/eslint-plugin-query"; import reactHooks from "eslint-plugin-react-hooks"; import reactRefresh from "eslint-plugin-react-refresh"; import globals from "globals"; @@ -7,18 +8,21 @@ import tseslint from "typescript-eslint"; export default tseslint.config( { ignores: ["dist"] }, { - extends: [js.configs.recommended, ...tseslint.configs.recommended], + extends: [ + js.configs.recommended, + reactHooks.configs["recommended-latest"], + ...tseslint.configs.recommended, + ...pluginQuery.configs["flat/recommended"], + ], files: ["**/*.{ts,tsx}"], languageOptions: { ecmaVersion: 2020, globals: globals.browser, }, plugins: { - "react-hooks": reactHooks, "react-refresh": reactRefresh, }, rules: { - ...reactHooks.configs.recommended.rules, "react-refresh/only-export-components": [ "warn", { allowConstantExport: true }, diff --git a/package-lock.json b/package-lock.json index de9e9e3..466cf7e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,8 @@ "dependencies": { "@emotion/styled": "^11.14.1", "@mui/icons-material": "^7.2.0", + "@tanstack/react-query": "^5.82.0", + "@tanstack/react-query-devtools": "^5.82.0", "d3": "^7.9.0", "react": "^19.1.0", "react-dom": "^19.1.0", @@ -17,6 +19,7 @@ }, "devDependencies": { "@eslint/js": "^9.30.1", + "@tanstack/eslint-plugin-query": "^5.81.2", "@types/d3": "^7.4.3", "@types/node": "^24.0.12", "@types/react": "^19.1.8", @@ -2089,6 +2092,76 @@ "win32" ] }, + "node_modules/@tanstack/eslint-plugin-query": { + "version": "5.81.2", + "resolved": "https://registry.npmjs.org/@tanstack/eslint-plugin-query/-/eslint-plugin-query-5.81.2.tgz", + "integrity": "sha512-h4k6P6fm5VhKP5NkK+0TTVpGGyKQdx6tk7NYYG7J7PkSu7ClpLgBihw7yzK8N3n5zPaF3IMyErxfoNiXWH/3/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^8.18.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, + "node_modules/@tanstack/query-core": { + "version": "5.82.0", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.82.0.tgz", + "integrity": "sha512-JrjoVuaajBQtnoWSg8iaPHaT4mW73lK2t+exxHNOSMqy0+13eKLqJgTKXKImLejQIfdAHQ6Un0njEhOvUtOd5w==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/query-devtools": { + "version": "5.81.2", + "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.81.2.tgz", + "integrity": "sha512-jCeJcDCwKfoyyBXjXe9+Lo8aTkavygHHsUHAlxQKKaDeyT0qyQNLKl7+UyqYH2dDF6UN/14873IPBHchcsU+Zg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.82.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.82.0.tgz", + "integrity": "sha512-mnk8/ofKEthFeMdhV1dV8YXRf+9HqvXAcciXkoo755d/ocfWq7N/Y9jGOzS3h7ZW9dDGwSIhs3/HANWUBsyqYg==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.82.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@tanstack/react-query-devtools": { + "version": "5.82.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.82.0.tgz", + "integrity": "sha512-MC05Zq3zr/59jhgF7dL6JSGPg1krbasDSizmRxjNcvxgh/sUTwRFD9CGN10YYX7LB6jq0ZpFtCjSVGdLiFrKAA==", + "license": "MIT", + "dependencies": { + "@tanstack/query-devtools": "5.81.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "@tanstack/react-query": "^5.82.0", + "react": "^18 || ^19" + } + }, "node_modules/@tsconfig/node10": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", diff --git a/package.json b/package.json index 371c3b7..d1289d4 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,8 @@ "dependencies": { "@emotion/styled": "^11.14.1", "@mui/icons-material": "^7.2.0", + "@tanstack/react-query": "^5.82.0", + "@tanstack/react-query-devtools": "^5.82.0", "d3": "^7.9.0", "react": "^19.1.0", "react-dom": "^19.1.0", @@ -19,6 +21,7 @@ }, "devDependencies": { "@eslint/js": "^9.30.1", + "@tanstack/eslint-plugin-query": "^5.81.2", "@types/d3": "^7.4.3", "@types/node": "^24.0.12", "@types/react": "^19.1.8", diff --git a/src/App.tsx b/src/App.tsx index 7ddfe45..093c0f7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,24 +1,26 @@ +import { useQuery } from "@tanstack/react-query"; import * as d3 from "d3"; import { useEffect, useState } from "react"; import Legend from "./components/Legend"; import QRCode from "./components/QRCode"; import { QuestionGroupChart } from "./components/QuestionGroupChart"; import { config } from "./config"; -import { CategoryMetadata, fetchCategoryMetadata } from "./lib/metadata"; -import { fetchGoogleSheet, ResponseData } from "./lib/parser"; +import { getSampleData } from "./lib/data"; +import { fetchCategoryMetadata } from "./lib/metadata"; function App() { - const [data, setData] = useState([]); - const [categoryMetadata, setCategoryMetadata] = useState( - [] - ); + const { data: categoryMetadata } = useQuery({ + queryKey: ["categoryMetadata"], + queryFn: fetchCategoryMetadata, + }); + const { data, isPending, isError } = useQuery({ + queryKey: ["responses"], + queryFn: getSampleData, + refetchInterval: 2 * 1000, // Refresh every 5 seconds + }); - useEffect(() => { - fetchGoogleSheet().then(setData); - // setData(getSampleData()); - - fetchCategoryMetadata().then(setCategoryMetadata); - }, []); + if (isPending) return
Loading...
; + if (isError) return
Error loading data
; if (!data.length) return null; diff --git a/src/lib/data.ts b/src/lib/data.ts index 9758b83..8b3e158 100644 --- a/src/lib/data.ts +++ b/src/lib/data.ts @@ -1,6 +1,6 @@ import { ResponseData } from "./parser"; -export function getSampleData(): ResponseData[] { +export function getSampleData(): Promise { const questions = [ "Service Quality", "Value for Money", @@ -24,5 +24,8 @@ export function getSampleData(): ResponseData[] { }); } }); - return sampleData; + // Simulate a delay to mimic fetching actual data + return new Promise((resolve) => { + setTimeout(() => resolve(sampleData), 500); + }); } diff --git a/src/main.tsx b/src/main.tsx index 4d1e5ad..4ff3918 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,10 +1,19 @@ +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; + import App from "./App"; + import "./styles/index.scss"; +const queryClient = new QueryClient(); + createRoot(document.getElementById("root")!).render( - + + + + );