diff --git a/apps/api/src/proxy/proxy.controller.ts b/apps/api/src/proxy/proxy.controller.ts index 44c9289..8e1be8b 100644 --- a/apps/api/src/proxy/proxy.controller.ts +++ b/apps/api/src/proxy/proxy.controller.ts @@ -74,8 +74,10 @@ export class ProxyController { return (Object.keys(queryTTL) as Array).reduce( (acc, queryName) => { const queries = list.filter((x) => x.split(' ').at(0) === queryName); - const ttl = queryTTL[queryName]; - acc[queryName] = { queries, ttl }; + if (queries.length) { + const ttl = queryTTL[queryName]; + acc[queryName] = { queries, ttl }; + } return acc; }, diff --git a/apps/web/Components/Admin/Admin.module.css b/apps/web/Components/Admin/Admin.module.css deleted file mode 100644 index 2305fa0..0000000 --- a/apps/web/Components/Admin/Admin.module.css +++ /dev/null @@ -1,3 +0,0 @@ -.collapse { - flex-grow: 1; -} diff --git a/apps/web/Components/Admin/AdminRow.tsx b/apps/web/Components/Admin/AdminRow.tsx deleted file mode 100644 index 7740347..0000000 --- a/apps/web/Components/Admin/AdminRow.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import style from './Admin.module.css'; -import { getQueryValue } from '@/api/cache/query'; -import type { ResponseQueries } from '@/api/cache/types'; -import type { CollapseProps } from 'antd'; -import { Button, Collapse } from 'antd'; -import { useState } from 'react'; -import styled from 'styled-components'; - -type IProps = { - readonly data: string; - readonly index: number; - readonly name: string; - readonly onClick: (name: string, key: string) => void; -}; - -const Wrapper = styled.div` - display: flex; - justify-content: space-between; -`; - -export function AdminRow(props: IProps) { - const { data, index, name, onClick } = props; - const defaultItems: CollapseProps['items'] = [ - { - children:

{data}

, - key: index.toString(), - label: data, - }, - ]; - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [queryText, setQueryText] = useState({}); - const [items, setItems] = useState(defaultItems); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [key, setKey] = useState(data); - - const handleClick = () => { - onClick(name, key); - }; - - const handleChange = async () => { - const query = await getQueryValue(key); - setQueryText(query); - setItems( - [...items].map((obj) => ({ - ...obj, - children:

{JSON.stringify(query)}

, - })) - ); - }; - - return ( - - - - - ); -} diff --git a/apps/web/Components/Admin/AdminRows.tsx b/apps/web/Components/Admin/AdminRows.tsx deleted file mode 100644 index 6b2fa5c..0000000 --- a/apps/web/Components/Admin/AdminRows.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { AdminRow } from './AdminRow'; -import type { CollapseProps } from 'antd'; -import { Collapse } from 'ui/elements'; - -export function AdminRows({ - index, - name, - onClick, - queries, -}: { - readonly index: number; - readonly name: string; - readonly onClick: (name: string, key: string) => void; - readonly queries: string[]; -}) { - if (queries.length === 0) { - const items: CollapseProps['items'] = [ - { - children: null, - key: index.toString(), - label: name, - }, - ]; - - return ; - } - - const rows: CollapseProps['items'] = [ - { - children: queries.map((x, ind) => ( - - )), - key: index.toString(), - label: name, - }, - ]; - - return ; -} diff --git a/apps/web/Components/Admin/AdminTable.tsx b/apps/web/Components/Admin/AdminTable.tsx deleted file mode 100644 index 9ddd494..0000000 --- a/apps/web/Components/Admin/AdminTable.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { AdminRows } from './AdminRows'; -import { deleleteQueriesByKey, deleteQuery, getQueries } from '@/api/cache/query'; -import type { ResponseQueries } from '@/api/cache/types'; -import { useEffect, useState } from 'react'; -import type { QueryItem } from 'shared/types/cache'; -import styled from 'styled-components'; -import { Button } from 'ui/elements'; - -const Wrapper = styled.div` - display: flex; - justify-content: space-between; - > * { - &:first-child { - flex-grow: 1; - } - } -`; - -export function AdminTable() { - const [data, setData] = useState({}); - - const handleClick = async (name: string, key: string) => { - await deleteQuery(key); - - const index = data[name].queries.indexOf(key); - const updatedQuery: QueryItem = { ...data[name] }; - - if (index >= 0 && updatedQuery.queries.length > 1) { - setData({ ...data, [name]: { ...data[name], queries: data[name].queries.splice(index, 1) } }); - } else if (index === 0) { - setData({ ...data, [name]: { ...data[name], queries: [] } }); - } - }; - - const handleClickQueriesGroup = async (name: string) => { - await deleleteQueriesByKey(name); - if (data[name].queries.length > 0) { - const updatedQuery = { ...data[name], queries: [] }; - setData({ ...data, [name]: updatedQuery }); - } - }; - - useEffect(() => { - const getRows = async () => { - const queryList = await getQueries(); - - setData(queryList); - }; - - getRows(); - }, []); - - return ( - <> - {Object.keys(data).map((key, index) => ( - - - - - ))} - - ); -} diff --git a/apps/web/Components/Admin/Cache.tsx b/apps/web/Components/Admin/Cache.tsx new file mode 100644 index 0000000..2d1f8f4 --- /dev/null +++ b/apps/web/Components/Admin/Cache.tsx @@ -0,0 +1,130 @@ +// import { AdminRows } from './AdminRows'; +import Background from '../Layout/Background'; +import * as cacheApi from '@/api/cache/query'; +import { min } from '@/styles/mq'; +import { useQuery } from '@tanstack/react-query'; +import { memo, useMemo, useState } from 'react'; +import type { QueryItem } from 'shared/types/cache'; +import styled from 'styled-components'; +import { Button, Collapse, Divider } from 'ui/elements'; +import { Flex } from 'ui/grid'; + +type QueryProps = { + readonly handleDeleteQuery: () => void; + readonly queryKey: string; +}; + +const StyledPre = styled.pre` + max-height: 300px; + overflow-y: auto; +`; + +const Query = memo(({ handleDeleteQuery, queryKey }: QueryProps) => { + const { data, refetch } = useQuery({ + enabled: false, + queryFn: ({ signal }) => signal && cacheApi.getQueryValue(queryKey, { signal }), + queryKey: ['admin', 'cache', 'query', queryKey], + refetchOnWindowFocus: false, + }); + + const [activeKey, setActiveKey] = useState(undefined); + + const content = ( + <> + {JSON.stringify(data, null, 2)} + + + + + ); + + return ( + { + if (activeKey) { + setActiveKey(undefined); + } else { + setActiveKey(queryKey); + + refetch(); + } + }} + /> + ); +}); + +type QueryListProps = QueryItem; + +const QueryList = memo(({ queries }: QueryListProps) => { + const [deletedQueries, setDeletedQueries] = useState([]); + + const activeQueries = useMemo( + () => queries.filter((queryKey) => !deletedQueries.includes(queryKey)), + [deletedQueries, queries] + ); + + function deleteQuery(queryName: string) { + cacheApi.deleteQuery(queryName).then(() => setDeletedQueries([...deletedQueries, queryName])); + } + + return activeQueries.map((queryKey) => ( + { + deleteQuery(queryKey); + }} + /> + )); +}); + +const Wrapper = styled(Background)` + padding: 4px 6px; + width: 100%; + + ${min('tablet')} { + min-height: 790px; + } + + ${min('laptop')} { + padding: 4px 6px 10px; + width: 1024px; + } +`; + +export function CacheQueries() { + const { data: queries } = useQuery({ + queryFn: ({ signal }) => signal && cacheApi.getQueries({ signal }), + queryKey: ['admin', 'cache', 'queries'], + refetchOnWindowFocus: false, + }); + + if (!queries) { + return
Загрузка...
; + } + + return ( + + Управление кэшем + ({ + children: , + key: queryGroupName, + label: queryGroupName, + }))} + /> + + ); +} diff --git a/apps/web/Components/Admin/index.ts b/apps/web/Components/Admin/index.ts deleted file mode 100644 index b912125..0000000 --- a/apps/web/Components/Admin/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './AdminRow'; -export * from './AdminRows'; -export * from './AdminTable'; diff --git a/apps/web/Components/Admin/index.tsx b/apps/web/Components/Admin/index.tsx new file mode 100644 index 0000000..58a913a --- /dev/null +++ b/apps/web/Components/Admin/index.tsx @@ -0,0 +1,9 @@ +import { CacheQueries } from './Cache'; + +export function AdminPanel() { + return ( +
+ +
+ ); +} diff --git a/apps/web/Components/Layout/Page.jsx b/apps/web/Components/Layout/Page.jsx index 33f641e..fcd4bd6 100644 --- a/apps/web/Components/Layout/Page.jsx +++ b/apps/web/Components/Layout/Page.jsx @@ -22,3 +22,18 @@ export const Grid = styled(Box)` margin: 8px 10% !important; } `; + +export const AdminGrid = styled(Box)` + display: flex; + flex-direction: column; + gap: 10px; + + ${min('laptop')} { + display: grid; + place-items: center; + } + + ${min('desktop-xl')} { + margin: 8px 10% !important; + } +`; diff --git a/apps/web/api/cache/query.ts b/apps/web/api/cache/query.ts index 61745b5..8997697 100644 --- a/apps/web/api/cache/query.ts +++ b/apps/web/api/cache/query.ts @@ -11,8 +11,8 @@ const { URL_CACHE_GET_QUERY_VALUE, } = getUrls(); -export function getQueries() { - return withHandleError(axios.get(URL_CACHE_GET_QUERIES)).then( +export function getQueries({ signal }: { signal: AbortSignal }) { + return withHandleError(axios.get(URL_CACHE_GET_QUERIES, { signal })).then( ({ data }) => data ); } @@ -31,7 +31,7 @@ export function reset() { return withHandleError(axios.delete(URL_CACHE_RESET_QUERIES)).then(({ data }) => data); } -export function deleleteQueriesByKey(queriesGroup: string) { +export function deleteQueriesByKey(queriesGroup: string) { return withHandleError( axios.delete(URL_CACHE_DELETE_QUERIES_BY_KEY, { params: { @@ -41,12 +41,13 @@ export function deleleteQueriesByKey(queriesGroup: string) { ).then(({ data }) => data); } -export function getQueryValue(queryKey: string) { +export function getQueryValue(queryKey: string, { signal }: { signal: AbortSignal }) { return withHandleError( - axios.get(URL_CACHE_GET_QUERY_VALUE, { + axios.get(URL_CACHE_GET_QUERY_VALUE, { params: { queryKey, }, + signal, }) ).then(({ data }) => data); } diff --git a/apps/web/pages/admin.jsx b/apps/web/pages/admin.jsx index c674fde..47c6fad 100644 --- a/apps/web/pages/admin.jsx +++ b/apps/web/pages/admin.jsx @@ -1,47 +1,23 @@ -import { makeGetServerSideProps } from '.'; -import { AdminTable } from '@/Components/Admin'; +import { getQueries } from '@/api/cache/query'; +import { getUser } from '@/api/user/query'; +import initializeApollo from '@/apollo/client'; +import { AdminPanel } from '@/Components/Admin'; import { Error } from '@/Components/Common/Error'; -import Background from '@/Components/Layout/Background'; -import { Grid } from '@/Components/Layout/Page'; +import { AdminGrid } from '@/Components/Layout/Page'; import { unlimitedRoles } from '@/config/users'; -import * as hooks from '@/process/hooks'; -import { useStore } from '@/stores/hooks'; -import { min } from '@/styles/mq'; +import * as CRMTypes from '@/graphql/crm.types'; +import { dehydrate, QueryClient } from '@tanstack/react-query'; import Head from 'next/head'; -import styled from 'styled-components'; function Content() { - const store = useStore(); - store.$process.add('Unlimited'); - - hooks.useSentryScope(); - hooks.useMainData(); - hooks.useGetUsers(); - hooks.useInsuranceData(); - hooks.useReactions(); - - const Wrapper = styled(Background)` - padding: 4px 6px; - - ${min('tablet')} { - min-height: 790px; - } - - ${min('laptop')} { - padding: 4px 6px 10px; - } - `; - return ( - + - Админка + Панель управления - - - - + + ); } @@ -51,6 +27,62 @@ export default function Admin(props) { return ; } -export const getServerSideProps = makeGetServerSideProps({ - roles: unlimitedRoles, -}); +/** @type {import('next').GetServerSideProps} */ +export async function getServerSideProps({ req }) { + const { cookie = '' } = req.headers; + + const queryClient = new QueryClient(); + + const user = await queryClient.fetchQuery(['user'], ({ signal }) => + getUser({ + headers: { + cookie, + }, + signal, + }) + ); + + const apolloClient = initializeApollo(); + + try { + const { + data: { systemuser }, + } = await apolloClient.query({ + fetchPolicy: 'network-only', + query: CRMTypes.GetSystemUserDocument, + variables: { + domainname: user.domainName, + }, + }); + + if (!systemuser?.roles?.some((x) => x?.name && unlimitedRoles.includes(x.name))) { + return { + props: { + initialQueryState: dehydrate(queryClient), + statusCode: 403, + }, + }; + } + + await queryClient.prefetchQuery(['admin', 'cache', 'queries'], ({ signal }) => + getQueries({ signal }) + ); + + return { + props: { + calculation: {}, + initialApolloState: apolloClient.cache.extract(), + initialQueryState: dehydrate(queryClient), + statusCode: 200, + }, + }; + } catch (error) { + return { + props: { + error: JSON.stringify(error), + initialQueryState: dehydrate(queryClient), + statusCode: 500, + }, + }; + } +}