From 522178128dcac5dce7bb75eedf78df66f9f148c2 Mon Sep 17 00:00:00 2001 From: Wes Date: Thu, 15 Aug 2024 10:29:34 -0700 Subject: [PATCH] feat: add tanstack query to frontend --- frontend/.eslintrc.cjs | 80 ++++++++ frontend/package-lock.json | 175 ++++++++++++++++++ frontend/package.json | 3 + frontend/src/App.tsx | 24 +-- frontend/src/api/modules/use-modules.ts | 45 +++++ .../src/{hooks => api/schema}/use-schema.ts | 9 +- frontend/src/components/CodeEditor.tsx | 2 +- frontend/src/components/DarkModeSwitch.tsx | 11 +- frontend/src/hooks/use-dark-mode.ts | 16 ++ frontend/src/layout/Layout.tsx | 2 +- frontend/src/layout/VSCodeLayout.tsx | 76 ++++++++ frontend/src/layout/navigation/Navigation.tsx | 9 +- frontend/src/main.tsx | 10 +- .../{AppProviders.tsx => app-providers.tsx} | 12 +- frontend/src/providers/modules-provider.tsx | 2 +- .../src/providers/react-query-provider.tsx | 14 ++ frontend/src/providers/routing-provider.tsx | 29 +++ frontend/src/providers/settings-provider.tsx | 36 ++++ 18 files changed, 500 insertions(+), 55 deletions(-) create mode 100644 frontend/.eslintrc.cjs create mode 100644 frontend/src/api/modules/use-modules.ts rename frontend/src/{hooks => api/schema}/use-schema.ts (86%) create mode 100644 frontend/src/hooks/use-dark-mode.ts create mode 100644 frontend/src/layout/VSCodeLayout.tsx rename frontend/src/providers/{AppProviders.tsx => app-providers.tsx} (52%) create mode 100644 frontend/src/providers/react-query-provider.tsx create mode 100644 frontend/src/providers/routing-provider.tsx create mode 100644 frontend/src/providers/settings-provider.tsx diff --git a/frontend/.eslintrc.cjs b/frontend/.eslintrc.cjs new file mode 100644 index 000000000..0809b5424 --- /dev/null +++ b/frontend/.eslintrc.cjs @@ -0,0 +1,80 @@ +module.exports = { + env: { + browser: true, + es2021: true, + }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react/recommended', + 'plugin:storybook/recommended', + ], + overrides: [ + { + env: { + node: true, + }, + files: ['.eslintrc.{js,cjs}'], + parserOptions: { + sourceType: 'script', + }, + }, + ], + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + }, + plugins: ['@typescript-eslint', 'react'], + rules: { + 'semi': ['error', 'never'], + 'quotes': ['error', 'single', { 'avoidEscape': true, 'allowTemplateLiterals': true }], + 'jsx-quotes': ['error', 'prefer-single'], + 'no-trailing-spaces': 'error', + 'no-multiple-empty-lines': ['error', { 'max': 1, 'maxEOF': 0, 'maxBOF': 0 }], + 'eol-last': ['error', 'always'], + 'max-len': ['error', { 'code': 120, 'tabWidth': 4, 'ignoreUrls': true, 'ignoreComments': false, 'ignoreRegExpLiterals': true, 'ignoreStrings': true, 'ignoreTemplateLiterals': true }], + 'react/react-in-jsx-scope': 'off', + 'react/jsx-uses-react': 'off', + 'func-style': ['error', 'expression'], + 'react/prop-types': 'off', + '@typescript-eslint/consistent-type-definitions': ['error', 'interface'], + '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }], + '@typescript-eslint/ban-ts-comment': [ + 'error', + { + 'ts-ignore': 'allow-with-description', + }, + ], + 'indent': ['error', 2, { + 'SwitchCase': 1, + 'VariableDeclarator': 1, + 'outerIIFEBody': 1, + 'MemberExpression': 1, + 'FunctionDeclaration': { + 'parameters': 1, + 'body': 1, + }, + 'FunctionExpression': { + 'parameters': 1, + 'body': 1, + }, + 'CallExpression': { + 'arguments': 1, + }, + 'ArrayExpression': 1, + 'ObjectExpression': 1, + 'ImportDeclaration': 1, + 'flatTernaryExpressions': false, + 'ignoreComments': false, + }], + 'react/jsx-indent': ['error', 2], + 'react/jsx-indent-props': ['error', 2], + 'no-multi-spaces': ['error'], + }, + settings: { + react: { + version: 'detect', + }, + }, +} diff --git a/frontend/package-lock.json b/frontend/package-lock.json index c1c8656ce..dd7d3543c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -19,6 +19,8 @@ "@headlessui/react": "2.1.2", "@heroicons/react": "2.1.5", "@tailwindcss/forms": "^0.5.6", + "@tanstack/react-query": "^5.51.23", + "@tanstack/react-query-devtools": "^5.51.23", "@uiw/codemirror-theme-atomone": "^4.22.0", "@uiw/codemirror-theme-github": "^4.22.0", "@vitejs/plugin-react": "^4.0.4", @@ -48,6 +50,7 @@ "@storybook/react": "^8.2.7", "@storybook/react-vite": "^8.2.7", "@storybook/test": "^8.2.7", + "@tanstack/eslint-plugin-query": "^5.51.15", "@types/react": "18.3.3", "@types/react-dom": "18.3.0", "buffer": "^6.0.3", @@ -6120,6 +6123,178 @@ "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1" } }, + "node_modules/@tanstack/eslint-plugin-query": { + "version": "5.51.15", + "resolved": "https://registry.npmjs.org/@tanstack/eslint-plugin-query/-/eslint-plugin-query-5.51.15.tgz", + "integrity": "sha512-btX03EOGvNxTGJDqHMmQwfSt/hp93Z8I4FNBijoyEdDnjGi4jVjpGP7nEi9LaMnHFsylucptVGb6GQngWs07bA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "8.0.0-alpha.30" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "eslint": "^8 || ^9" + } + }, + "node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/scope-manager": { + "version": "8.0.0-alpha.30", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.0.0-alpha.30.tgz", + "integrity": "sha512-FGW/iPWGyPFamAVZ60oCAthMqQrqafUGebF8UKuq/ha+e9SVG6YhJoRzurlQXOVf8dHfOhJ0ADMXyFnMc53clg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.0.0-alpha.30", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.30" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/types": { + "version": "8.0.0-alpha.30", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.0.0-alpha.30.tgz", + "integrity": "sha512-4WzLlw27SO9pK9UFj/Hu7WGo8WveT0SEiIpFVsV2WwtQmLps6kouwtVCB8GJPZKJyurhZhcqCoQVQFmpv441Vg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.0.0-alpha.30", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.0.0-alpha.30.tgz", + "integrity": "sha512-WSXbc9ZcXI+7yC+6q95u77i8FXz6HOLsw3ST+vMUlFy1lFbXyFL/3e6HDKQCm2Clt0krnoCPiTGvIn+GkYPn4Q==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "8.0.0-alpha.30", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.30", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/utils": { + "version": "8.0.0-alpha.30", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.0.0-alpha.30.tgz", + "integrity": "sha512-rfhqfLqFyXhHNDwMnHiVGxl/Z2q/3guQ1jLlGQ0hi9Rb7inmwz42crM+NnLPR+2vEnwyw1P/g7fnQgQ3qvFx4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.0.0-alpha.30", + "@typescript-eslint/types": "8.0.0-alpha.30", + "@typescript-eslint/typescript-estree": "8.0.0-alpha.30" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, + "node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.0.0-alpha.30", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.0.0-alpha.30.tgz", + "integrity": "sha512-XZuNurZxBqmr6ZIRIwWFq7j5RZd6ZlkId/HZEWyfciK+CWoyOxSF9Pv2VXH9Rlu2ZG2PfbhLz2Veszl4Pfn7yA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.0.0-alpha.30", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@tanstack/query-core": { + "version": "5.51.21", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.51.21.tgz", + "integrity": "sha512-POQxm42IUp6n89kKWF4IZi18v3fxQWFRolvBA6phNVmA8psdfB1MvDnGacCJdS+EOX12w/CyHM62z//rHmYmvw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/query-devtools": { + "version": "5.51.16", + "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.51.16.tgz", + "integrity": "sha512-ajwuq4WnkNCMj/Hy3KR8d3RtZ6PSKc1dD2vs2T408MdjgKzQ3klVoL6zDgVO7X+5jlb5zfgcO3thh4ojPhfIaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.51.23", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.51.23.tgz", + "integrity": "sha512-CfJCfX45nnVIZjQBRYYtvVMIsGgWLKLYC4xcUiYEey671n1alvTZoCBaU9B85O8mF/tx9LPyrI04A6Bs2THv4A==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.51.21" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18.0.0" + } + }, + "node_modules/@tanstack/react-query-devtools": { + "version": "5.51.23", + "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.51.23.tgz", + "integrity": "sha512-XpHrdyfUPGULIyJ1K7UvhAcK+KjMJdw4NjmRjryoj3XEgfAU5qU1rz8gIFvGc3gTGT07yIseGo7GEll/ICfJfQ==", + "license": "MIT", + "dependencies": { + "@tanstack/query-devtools": "5.51.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "@tanstack/react-query": "^5.51.23", + "react": "^18 || ^19" + } + }, "node_modules/@tanstack/react-virtual": { "version": "3.8.1", "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.8.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 719941e98..4dd336065 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -30,6 +30,8 @@ "@headlessui/react": "2.1.2", "@heroicons/react": "2.1.5", "@tailwindcss/forms": "^0.5.6", + "@tanstack/react-query": "^5.51.23", + "@tanstack/react-query-devtools": "^5.51.23", "@uiw/codemirror-theme-atomone": "^4.22.0", "@uiw/codemirror-theme-github": "^4.22.0", "@vitejs/plugin-react": "^4.0.4", @@ -59,6 +61,7 @@ "@storybook/react": "^8.2.7", "@storybook/react-vite": "^8.2.7", "@storybook/test": "^8.2.7", + "@tanstack/eslint-plugin-query": "^5.51.15", "@types/react": "18.3.3", "@types/react-dom": "18.3.0", "buffer": "^6.0.3", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 359f231ca..62ddfad82 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,25 +1,5 @@ -import { Navigate, Route, Routes } from 'react-router-dom' -import { ConsolePage } from './features/console/ConsolePage.tsx' -import { DeploymentPage } from './features/deployments/DeploymentPage.tsx' -import { DeploymentsPage } from './features/deployments/DeploymentsPage.tsx' -import { TimelinePage } from './features/timeline/TimelinePage.tsx' -import { VerbPage } from './features/verbs/VerbPage.tsx' -import { Layout } from './layout/Layout.tsx' -import { NotFoundPage } from './layout/NotFoundPage.tsx' +import { AppProvider } from './providers/app-providers.tsx' export const App = () => { - return ( - - }> - } /> - } /> - - } /> - } /> - } /> - } /> - - } /> - - ) + return } diff --git a/frontend/src/api/modules/use-modules.ts b/frontend/src/api/modules/use-modules.ts new file mode 100644 index 000000000..dbfe02d95 --- /dev/null +++ b/frontend/src/api/modules/use-modules.ts @@ -0,0 +1,45 @@ +import { ConsoleService } from '../../protos/xyz/block/ftl/v1/console/console_connect' +import { GetModulesResponse } from '../../protos/xyz/block/ftl/v1/console/console_pb' + +import { Code, ConnectError } from '@connectrpc/connect' +import { useClient } from '../../hooks/use-client' + +const fetchModules = async (client: ConsoleService, isVisible: boolean): Promise => { + if (!isVisible) { + throw new Error('Component is not visible') + } + + const abortController = new AbortController() + + try { + const modules = await client.getModules({}, { signal: abortController.signal }) + return modules ?? [] + } catch (error) { + if (error instanceof ConnectError) { + if (error.code !== Code.Canceled) { + console.error('fetchModules - Connect error:', error) + } + } else { + console.error('fetchModules:', error) + } + throw error + } finally { + abortController.abort() + } +} + +export const useModules = () => { + const client = useClient(ConsoleService) + const isVisible = useVisibility() + const schema = useSchema() + + return useQuery( + ['modules', schema, isVisible], // The query key, include schema and isVisible as dependencies + () => fetchModules(client, isVisible), + { + enabled: isVisible, // Only run the query when the component is visible + refetchOnWindowFocus: false, // Optional: Disable refetching on window focus + staleTime: 1000 * 60 * 5, // Optional: Cache data for 5 minutes + } + ) +} diff --git a/frontend/src/hooks/use-schema.ts b/frontend/src/api/schema/use-schema.ts similarity index 86% rename from frontend/src/hooks/use-schema.ts rename to frontend/src/api/schema/use-schema.ts index 5fea31bb6..e1d979a89 100644 --- a/frontend/src/hooks/use-schema.ts +++ b/frontend/src/api/schema/use-schema.ts @@ -1,9 +1,9 @@ import { Code, ConnectError } from '@connectrpc/connect' import { useEffect, useState } from 'react' -import { useClient } from '../hooks/use-client' -import { useVisibility } from '../hooks/use-visibility.ts' -import { ControllerService } from '../protos/xyz/block/ftl/v1/ftl_connect.ts' -import { DeploymentChangeType, type PullSchemaResponse } from '../protos/xyz/block/ftl/v1/ftl_pb' +import { useClient } from '../../hooks/use-client.ts' +import { useVisibility } from '../../hooks/use-visibility.ts' +import { ControllerService } from '../../protos/xyz/block/ftl/v1/ftl_connect.ts' +import { DeploymentChangeType, type PullSchemaResponse } from '../../protos/xyz/block/ftl/v1/ftl_pb.ts' export const useSchema = () => { const client = useClient(ControllerService) @@ -28,6 +28,7 @@ export const useSchema = () => { }, )) { const moduleName = response.moduleName ?? '' + console.log(`${response.changeType} ${moduleName}`) switch (response.changeType) { case DeploymentChangeType.DEPLOYMENT_ADDED: schemaMap.set(moduleName, response) diff --git a/frontend/src/components/CodeEditor.tsx b/frontend/src/components/CodeEditor.tsx index 4c5fb8881..cd4646af5 100644 --- a/frontend/src/components/CodeEditor.tsx +++ b/frontend/src/components/CodeEditor.tsx @@ -14,7 +14,7 @@ import { defaultKeymap } from '@codemirror/commands' import { handleRefresh, jsonSchemaHover, jsonSchemaLinter, stateExtensions } from 'codemirror-json-schema' import { json5, json5ParseLinter } from 'codemirror-json5' import { useCallback, useEffect, useRef } from 'react' -import { useDarkMode } from '../providers/dark-mode-provider' +import { useDarkMode } from '../hooks/use-dark-mode' const commonExtensions = [ gutter({ class: 'CodeMirror-lint-markers' }), diff --git a/frontend/src/components/DarkModeSwitch.tsx b/frontend/src/components/DarkModeSwitch.tsx index a03460363..741e3e777 100644 --- a/frontend/src/components/DarkModeSwitch.tsx +++ b/frontend/src/components/DarkModeSwitch.tsx @@ -1,20 +1,11 @@ import { Switch } from '@headlessui/react' import { MoonIcon, SunIcon } from '@heroicons/react/20/solid' -import { useEffect } from 'react' -import { useDarkMode } from '../providers/dark-mode-provider' import { classNames } from '../utils/react.utils' +import { useDarkMode } from '../hooks/use-dark-mode' export const DarkModeSwitch = () => { const { isDarkMode, setDarkMode } = useDarkMode() - useEffect(() => { - if (isDarkMode) { - document.documentElement.classList.add('dark') - } else { - document.documentElement.classList.remove('dark') - } - }, [isDarkMode]) - return ( { + const [isDarkMode, setDarkMode] = useLocalStorage('dark-mode', false) + + useEffect(() => { + if (isDarkMode) { + document.documentElement.classList.add('dark') + } else { + document.documentElement.classList.remove('dark') + } + }, [isDarkMode]) + + return { isDarkMode, setDarkMode } +} diff --git a/frontend/src/layout/Layout.tsx b/frontend/src/layout/Layout.tsx index f5abaf3b4..a8ed4e765 100644 --- a/frontend/src/layout/Layout.tsx +++ b/frontend/src/layout/Layout.tsx @@ -24,7 +24,7 @@ export const Layout = () => { FTL - diff --git a/frontend/src/layout/VSCodeLayout.tsx b/frontend/src/layout/VSCodeLayout.tsx new file mode 100644 index 000000000..ae46126bd --- /dev/null +++ b/frontend/src/layout/VSCodeLayout.tsx @@ -0,0 +1,76 @@ +import { useState } from 'react' + +export const Layout = () => { + const [leftPaneWidth, setLeftPaneWidth] = useState(200) + const [rightPaneWidth, setRightPaneWidth] = useState(200) + const [bottomPaneHeight, setBottomPaneHeight] = useState(100) + + const handleMouseDown = (e: React.MouseEvent, direction: string) => { + const startX = e.clientX + const startY = e.clientY + const startLeftPaneWidth = leftPaneWidth + const startRightPaneWidth = rightPaneWidth + const startBottomPaneHeight = bottomPaneHeight + + const handleMouseMove = (e: MouseEvent) => { + if (direction === 'left') { + const newLeftPaneWidth = startLeftPaneWidth + e.clientX - startX + setLeftPaneWidth(newLeftPaneWidth > 100 ? newLeftPaneWidth : 100) + } else if (direction === 'right') { + const newRightPaneWidth = startRightPaneWidth - (e.clientX - startX) + setRightPaneWidth(newRightPaneWidth > 100 ? newRightPaneWidth : 100) + } else if (direction === 'bottom') { + const newBottomPaneHeight = startBottomPaneHeight - (e.clientY - startY) + setBottomPaneHeight(newBottomPaneHeight > 50 ? newBottomPaneHeight : 50) + } + } + + const handleMouseUp = () => { + document.removeEventListener('mousemove', handleMouseMove) + document.removeEventListener('mouseup', handleMouseUp) + } + + document.addEventListener('mousemove', handleMouseMove) + document.addEventListener('mouseup', handleMouseUp) + } + + return ( +
+
+
+ {/* Activity Bar */} +
+
+
+ {/* Left Pane */} +
handleMouseDown(e, 'left')} + /> +
+
+
+
+ {/* Main Editor Area */} +
+
+ {/* Right Pane */} +
handleMouseDown(e, 'right')} + /> +
+
+
+ {/* Bottom Pane */} +
handleMouseDown(e, 'bottom')} + /> +
+
+
+
+
+ ) +} diff --git a/frontend/src/layout/navigation/Navigation.tsx b/frontend/src/layout/navigation/Navigation.tsx index ace036164..952be361a 100644 --- a/frontend/src/layout/navigation/Navigation.tsx +++ b/frontend/src/layout/navigation/Navigation.tsx @@ -27,13 +27,18 @@ export const Navigation = ({ <> FTL - )} {isCollapsed && ( - )} diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 537c3acd7..f7e405634 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -1,18 +1,12 @@ import React from 'react' import ReactDOM from 'react-dom/client' -import { BrowserRouter } from 'react-router-dom' import './index.css' -import { Buffer } from 'buffer' -import { AppProviders } from './providers/AppProviders.tsx' - -globalThis.Buffer = Buffer +import { App } from './App.tsx' const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement) root.render( - - - + , ) diff --git a/frontend/src/providers/AppProviders.tsx b/frontend/src/providers/app-providers.tsx similarity index 52% rename from frontend/src/providers/AppProviders.tsx rename to frontend/src/providers/app-providers.tsx index 2773728a8..18af33157 100644 --- a/frontend/src/providers/AppProviders.tsx +++ b/frontend/src/providers/app-providers.tsx @@ -1,16 +1,16 @@ -import { App } from '../App' -import { DarkModeProvider } from './dark-mode-provider' import { ModulesProvider } from './modules-provider' import { NotificationsProvider } from './notifications-provider' +import { ReactQueryProvider } from './react-query-provider' +import { RoutingProvider } from './routing-provider' -export const AppProviders = () => { +export const AppProvider = () => { return ( - + - + - + ) } diff --git a/frontend/src/providers/modules-provider.tsx b/frontend/src/providers/modules-provider.tsx index e227b4710..a1f8e9887 100644 --- a/frontend/src/providers/modules-provider.tsx +++ b/frontend/src/providers/modules-provider.tsx @@ -1,7 +1,7 @@ import { Code, ConnectError } from '@connectrpc/connect' import { type PropsWithChildren, createContext, useEffect, useState } from 'react' import { useClient } from '../hooks/use-client' -import { useSchema } from '../hooks/use-schema' +import { useSchema } from '../api/schema/use-schema' import { useVisibility } from '../hooks/use-visibility' import { ConsoleService } from '../protos/xyz/block/ftl/v1/console/console_connect' import { GetModulesResponse } from '../protos/xyz/block/ftl/v1/console/console_pb' diff --git a/frontend/src/providers/react-query-provider.tsx b/frontend/src/providers/react-query-provider.tsx new file mode 100644 index 000000000..6a2b5e07c --- /dev/null +++ b/frontend/src/providers/react-query-provider.tsx @@ -0,0 +1,14 @@ +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { ReactQueryDevtools } from '@tanstack/react-query-devtools' +import { PropsWithChildren } from 'react' + +const queryClient = new QueryClient() + +export const ReactQueryProvider = ({ children }: PropsWithChildren) => { + return ( + + {children} + + + ) +} diff --git a/frontend/src/providers/routing-provider.tsx b/frontend/src/providers/routing-provider.tsx new file mode 100644 index 000000000..6e2c2a2fa --- /dev/null +++ b/frontend/src/providers/routing-provider.tsx @@ -0,0 +1,29 @@ +import { createBrowserRouter, createRoutesFromElements, Navigate, Route, RouterProvider } from 'react-router-dom' +import { Layout } from '../layout' +import { TimelinePage } from '../features/timeline/TimelinePage' +import { DeploymentsPage } from '../features/deployments/DeploymentsPage' +import { DeploymentPage } from '../features/deployments/DeploymentPage' +import { VerbPage } from '../features/verbs/VerbPage' +import { ConsolePage } from '../features/console/ConsolePage' +import { NotFoundPage } from '../layout/NotFoundPage' + +const router = createBrowserRouter( + createRoutesFromElements( + <> + }> + } /> + } /> + + } /> + } /> + } /> + } /> + + } /> + + ) +) + +export const RoutingProvider = () => { + return +} diff --git a/frontend/src/providers/settings-provider.tsx b/frontend/src/providers/settings-provider.tsx new file mode 100644 index 000000000..a681c306c --- /dev/null +++ b/frontend/src/providers/settings-provider.tsx @@ -0,0 +1,36 @@ +import useLocalStorage from '../hooks/use-local-storage' +import { createContext, PropsWithChildren, useContext, useEffect } from 'react' + +interface SettingsContextProps { + isDarkMode: boolean; + setDarkMode: (value: boolean) => void; + language: string; + setLanguage: (value: string) => void; + // Add more settings as needed +} + +const SettingsContext = createContext(undefined) + +export const useSettings = () => { + const context = useContext(SettingsContext) + if (!context) { + throw new Error('useSettings must be used within a SettingsProvider') + } + return context +} + +export const SettingsProvider = ({ children }: PropsWithChildren) => { + const [isDarkMode, setDarkMode] = useLocalStorage('dark-mode', false) + const [language, setLanguage] = useLocalStorage('language', 'en') // Example of another setting + + // You can use useEffect to sync settings with other parts of the app if necessary + useEffect(() => { + document.documentElement.classList.toggle('dark', isDarkMode) + }, [isDarkMode]) + + return ( + + {children} + + ) +}