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