diff --git a/apps/web/Components/Layout/Navigation.jsx b/apps/web/Components/Layout/Navigation.jsx index ef27e57..3f975ea 100644 --- a/apps/web/Components/Layout/Navigation.jsx +++ b/apps/web/Components/Layout/Navigation.jsx @@ -1,3 +1,5 @@ +import { NavigationContext } from '@/context/navigation'; +import { useContext } from 'react'; import styled from 'styled-components'; const Container = styled.div` @@ -22,11 +24,31 @@ const TabButton = styled.button` `; export function NavigationBar() { + const { setCurrentTab, tabsList } = useContext(NavigationContext); + return ( - Tab1 - Tab2 - Tab3 + {tabsList.map(({ key, title }) => ( + setCurrentTab(key)}> + {title} + + ))} ); } + +function Tab({ children, visible }) { + if (!visible) return null; + + return children; +} + +export function Tabs({ tabs }) { + const { currentTab } = useContext(NavigationContext); + + return tabs.map(({ Component, key }) => ( + + + + )); +} diff --git a/apps/web/Components/Layout/index.jsx b/apps/web/Components/Layout/index.jsx index 405cc40..9034d15 100644 --- a/apps/web/Components/Layout/index.jsx +++ b/apps/web/Components/Layout/index.jsx @@ -2,6 +2,7 @@ import Header from './Header'; import { AppMenu } from './Menu'; import { NavigationBar } from './Navigation'; import { screens } from '@/config/ui'; +import { NavigationProvider } from '@/context/navigation'; import { max, min } from '@/styles/mq'; import dynamic from 'next/dynamic'; import styled from 'styled-components'; @@ -24,13 +25,13 @@ const Main = styled.main` export default function Layout({ children, user }) { return ( - <> +
{user?.admin ? : false}
{children}
- + ); } diff --git a/apps/web/context/navigation.tsx b/apps/web/context/navigation.tsx new file mode 100644 index 0000000..b16aa4c --- /dev/null +++ b/apps/web/context/navigation.tsx @@ -0,0 +1,27 @@ +import { createContext, useMemo, useState } from 'react'; + +type Tab = { + key: string; + title: string; +}; + +type NavigationContextType = { + currentTab: string; + setCurrentTab: (tab: string) => void; + setTabsList: (list: Tab[]) => void; + tabsList: Tab[]; +}; + +export const NavigationContext = createContext({} as NavigationContextType); + +export function NavigationProvider({ children }: { readonly children: React.ReactNode }) { + const [currentTab, setCurrentTab] = useState(''); + const [tabsList, setTabsList] = useState([]); + + const value = useMemo( + () => ({ currentTab, setCurrentTab, setTabsList, tabsList }), + [currentTab, setCurrentTab, setTabsList, tabsList] + ); + + return {children}; +} diff --git a/apps/web/package.json b/apps/web/package.json index 2bbdd3d..086abc9 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -33,6 +33,7 @@ "radash": "^11.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-responsive": "^10.0.0", "styled-components": "^5.3.11", "superjson": "^2.2.1", "tools": "workspace:*", diff --git a/apps/web/pages/index.jsx b/apps/web/pages/index.jsx index a070b48..f5d93e1 100644 --- a/apps/web/pages/index.jsx +++ b/apps/web/pages/index.jsx @@ -1,11 +1,39 @@ import initializeApollo from '@/apollo/client'; import * as Calculation from '@/Components/Calculation'; import { Error } from '@/Components/Common/Error'; +import { Tabs } from '@/Components/Layout/Navigation'; +import { screens } from '@/config/ui'; +import { NavigationContext } from '@/context/navigation'; import * as hooks from '@/process/hooks'; import { getPageTitle } from '@/utils/page'; import { makeGetUserType } from '@/utils/user'; import { dehydrate, QueryClient } from '@tanstack/react-query'; +import dynamic from 'next/dynamic'; import Head from 'next/head'; +import { useContext, useEffect } from 'react'; + +const MediaQuery = dynamic(() => import('react-responsive'), { + ssr: false, +}); + +const tabs = [ + { + Component: () => , + key: 'settings', + title: 'Расчет', + }, + { + Component: () => , + key: 'form', + title: 'Параметры', + }, + + { + Component: () => , + key: 'output', + title: 'Результаты', + }, +]; function Content() { hooks.useSentryScope(); @@ -13,15 +41,32 @@ function Content() { hooks.useInsuranceData(); hooks.useReactions(); + const { setCurrentTab, setTabsList } = useContext(NavigationContext); + + useEffect(() => { + setTabsList(tabs); + setCurrentTab('settings'); + }, [setCurrentTab, setTabsList]); + return ( - + <> {getPageTitle()} - - - - + + {(match) => { + if (match) return ; + + return ( + + + + + + ); + }} + + ); } diff --git a/apps/web/pages/unlimited.jsx b/apps/web/pages/unlimited.jsx index 26d0ff1..8287f39 100644 --- a/apps/web/pages/unlimited.jsx +++ b/apps/web/pages/unlimited.jsx @@ -1,11 +1,39 @@ import initializeApollo from '@/apollo/client'; import * as Calculation from '@/Components/Calculation'; import { Error } from '@/Components/Common/Error'; +import { Tabs } from '@/Components/Layout/Navigation'; +import { screens } from '@/config/ui'; +import { NavigationContext } from '@/context/navigation'; import * as hooks from '@/process/hooks'; import { getPageTitle } from '@/utils/page'; import { makeGetUserType } from '@/utils/user'; import { dehydrate, QueryClient } from '@tanstack/react-query'; +import dynamic from 'next/dynamic'; import Head from 'next/head'; +import { useContext, useEffect } from 'react'; + +const MediaQuery = dynamic(() => import('react-responsive'), { + ssr: false, +}); + +const tabs = [ + { + Component: () => , + key: 'settings', + title: 'Расчет', + }, + { + Component: () => , + key: 'form', + title: 'Параметры', + }, + + { + Component: () => , + key: 'output', + title: 'Результаты', + }, +]; function Content() { hooks.useSentryScope(); @@ -14,15 +42,32 @@ function Content() { hooks.useInsuranceData(); hooks.useReactions(); + const { setCurrentTab, setTabsList } = useContext(NavigationContext); + + useEffect(() => { + setTabsList(tabs); + setCurrentTab('settings'); + }, [setCurrentTab, setTabsList]); + return ( - + <> {getPageTitle('Без ограничений')} - - - - + + {(match) => { + if (match) return ; + + return ( + + + + + + ); + }} + + ); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f9c8aef..5ef1a75 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -177,6 +177,9 @@ importers: react-dom: specifier: ^18.2.0 version: 18.2.0(react@18.2.0) + react-responsive: + specifier: ^10.0.0 + version: 10.0.0(react@18.2.0) styled-components: specifier: ^5.3.11 version: 5.3.11(@babel/core@7.23.9)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) @@ -5899,6 +5902,10 @@ packages: engines: {node: '>=4'} dev: false + /css-mediaquery@0.1.2: + resolution: {integrity: sha512-COtn4EROW5dBGlE/4PiKnh6rZpAPxDeFLaEEwt4i10jpDMFt2EhQGS79QmmrO+iKCHv0PU/HrOWEhijFd1x99Q==} + dev: false + /css-to-react-native@3.2.0: resolution: {integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==} dependencies: @@ -6956,7 +6963,7 @@ packages: '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.57.0)(typescript@5.3.3) '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.3.3) eslint: 8.57.0 - jest: 29.7.0(@types/node@20.11.20)(ts-node@10.9.2) + jest: 29.7.0(@types/node@18.19.18)(ts-node@10.9.2) transitivePeerDependencies: - supports-color - typescript @@ -8200,7 +8207,7 @@ packages: '@graphql-tools/json-file-loader': 7.4.18(graphql@16.8.1) '@graphql-tools/load': 7.8.14(graphql@16.8.1) '@graphql-tools/merge': 8.4.2(graphql@16.8.1) - '@graphql-tools/url-loader': 7.17.18(@types/node@18.19.18)(graphql@16.8.1) + '@graphql-tools/url-loader': 7.17.18(@types/node@20.11.20)(graphql@16.8.1) '@graphql-tools/utils': 9.2.1(graphql@16.8.1) cosmiconfig: 8.0.0 graphql: 16.8.1 @@ -8474,6 +8481,10 @@ packages: hasBin: true dev: true + /hyphenate-style-name@1.0.5: + resolution: {integrity: sha512-fedL7PRwmeVkgyhu9hLeTBaI6wcGk7JGJswdaRsa5aUbkXI1kr1xZwTPBtaYPpwf56878iDek6VbVnuWMebJmw==} + dev: false + /iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} @@ -9320,7 +9331,7 @@ packages: pretty-format: 29.7.0 slash: 3.0.0 strip-json-comments: 3.1.1 - ts-node: 10.9.2(@types/node@20.11.20)(typescript@5.3.3) + ts-node: 10.9.2(@types/node@18.19.18)(typescript@5.3.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -10223,6 +10234,12 @@ packages: object-visit: 1.0.1 dev: true + /matchmediaquery@0.4.2: + resolution: {integrity: sha512-wrZpoT50ehYOudhDjt/YvUJc6eUzcdFPdmbizfgvswCKNHD1/OBOHYJpHie+HXpu6bSkEGieFMYk6VuutaiRfA==} + dependencies: + css-mediaquery: 0.1.2 + dev: false + /media-typer@0.3.0: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} @@ -11829,6 +11846,19 @@ packages: /react-is@18.2.0: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + /react-responsive@10.0.0(react@18.2.0): + resolution: {integrity: sha512-N6/UiRLGQyGUqrarhBZmrSmHi2FXSD++N5VbSKsBBvWfG0ZV7asvUBluSv5lSzdMyEVjzZ6Y8DL4OHABiztDOg==} + engines: {node: '>=14'} + peerDependencies: + react: '>=16.8.0' + dependencies: + hyphenate-style-name: 1.0.5 + matchmediaquery: 0.4.2 + prop-types: 15.8.1 + react: 18.2.0 + shallow-equal: 3.1.0 + dev: false + /react@18.2.0: resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} engines: {node: '>=0.10.0'} @@ -12438,6 +12468,10 @@ packages: /setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + /shallow-equal@3.1.0: + resolution: {integrity: sha512-pfVOw8QZIXpMbhBWvzBISicvToTiM5WBF1EeAUZDDSb5Dt29yl4AYbyywbJFSEsRUMr7gJaxqCdr4L3tQf9wVg==} + dev: false + /shallowequal@1.1.0: resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==} dev: false @@ -13241,7 +13275,7 @@ packages: '@babel/core': 7.23.9 bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@20.11.20)(ts-node@10.9.2) + jest: 29.7.0(@types/node@18.19.18)(ts-node@10.9.2) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2