From 9427ef75abf659f24455da71ae8b71b0bb6a9f69 Mon Sep 17 00:00:00 2001 From: balaji-jr Date: Fri, 23 Feb 2024 19:42:46 +0530 Subject: [PATCH 01/17] WIP - overall design enhancements --- src/components/Button/Button.module.css | 33 ++ src/components/Button/IconButton.tsx | 31 ++ src/components/Button/ToggleButton.tsx | 10 +- src/components/Header/HelpModal.tsx | 110 ++++++ src/components/Header/PrimaryHeader.tsx | 61 ++-- src/components/Header/Querier.tsx | 2 +- src/components/Header/RefreshInterval.tsx | 10 +- src/components/Header/RefreshNow.tsx | 14 +- src/components/Header/StreamDropdown.tsx | 43 +++ src/components/Header/SubHeader.tsx | 316 ++++++++++++++++-- src/components/Header/TimeRange.tsx | 20 +- .../Header/styles/Header.module.css | 15 +- .../Header/styles/HelpModal.module.css | 111 ++++++ .../Header/styles/LogQuery.module.css | 109 ++++++ .../Header/styles/QueryBuilder.module.css | 5 +- src/components/Navbar/index.tsx | 302 ++++++++++------- .../Navbar/styles/Navbar.module.css | 27 +- src/constants/routes.ts | 12 + src/constants/theme.ts | 5 +- src/constants/timeConstants.ts | 18 +- src/hooks/useCurrentRoute.ts | 16 + src/hooks/useQueryLogs.ts | 2 +- src/layouts/MainLayout/Context.tsx | 52 ++- src/layouts/MainLayout/index.tsx | 13 +- src/pages/Config/index.tsx | 12 +- src/pages/LiveTail/LogTable.tsx | 2 +- src/pages/Logs/AlertsModal.tsx | 54 +++ src/pages/Logs/CarouselSlide.tsx | 2 +- src/pages/Logs/Context.tsx | 84 ++++- src/pages/Logs/DeleteStreamModal.tsx | 56 ++++ src/pages/Logs/FilterQueryBuilder.tsx | 279 ++++++++++++++++ src/pages/Logs/HeaderPagination.tsx | 2 +- src/pages/Logs/LogRow.tsx | 2 +- src/pages/Logs/LogTable.tsx | 16 +- src/pages/Logs/PrimaryToolbar.tsx | 59 ++++ src/pages/Logs/Querier.tsx | 153 +++++++++ src/pages/Logs/QueryCodeEditor.tsx | 175 ++++------ src/pages/Logs/QueryEditor.tsx | 37 +- src/pages/Logs/RetentionModal.tsx | 54 +++ src/pages/Logs/SQLQueryBuilder.tsx | 0 src/pages/Logs/SecondaryToolbar.tsx | 84 +++++ src/pages/Logs/ViewLog.tsx | 2 +- src/pages/Logs/index.tsx | 35 +- src/pages/Logs/styles/Querier.module.css | 182 ++++++++++ src/pages/Logs/styles/QueryCode.module.css | 8 + src/pages/Logs/styles/Toolbar.module.css | 42 +++ src/providers/QueryFilterProvider.tsx | 20 +- src/routes/elements.tsx | 4 +- src/utils/sanitiseSqlString.ts | 2 +- 49 files changed, 2306 insertions(+), 397 deletions(-) create mode 100644 src/components/Button/IconButton.tsx create mode 100644 src/components/Header/HelpModal.tsx create mode 100644 src/components/Header/StreamDropdown.tsx create mode 100644 src/components/Header/styles/HelpModal.module.css create mode 100644 src/hooks/useCurrentRoute.ts create mode 100644 src/pages/Logs/AlertsModal.tsx create mode 100644 src/pages/Logs/DeleteStreamModal.tsx create mode 100644 src/pages/Logs/FilterQueryBuilder.tsx create mode 100644 src/pages/Logs/PrimaryToolbar.tsx create mode 100644 src/pages/Logs/Querier.tsx create mode 100644 src/pages/Logs/RetentionModal.tsx create mode 100644 src/pages/Logs/SQLQueryBuilder.tsx create mode 100644 src/pages/Logs/SecondaryToolbar.tsx create mode 100644 src/pages/Logs/styles/Querier.module.css create mode 100644 src/pages/Logs/styles/Toolbar.module.css diff --git a/src/components/Button/Button.module.css b/src/components/Button/Button.module.css index dd021477..a421a163 100644 --- a/src/components/Button/Button.module.css +++ b/src/components/Button/Button.module.css @@ -20,3 +20,36 @@ background-color: #545BEB; color: #FFFFFF; } + +.iconBtn { + background: #fff; + padding: 0; + width: 36px; + color: #211F1F; + border: 1px #e9ecef solid; + border-radius: rem(8px); + margin-right: 0.675rem; + + &.iconBtnActive { + color: white; + background-color: var(--mantine-color-brandPrimary-4); + } +} + + + +.iconBtnIcon { + color: var(--mantine-color-brandPrimary-6); +} + +.iconBtnLabel { + font-size: 0.8rem; +} + +.iconBtn:hover { + background-color: #E0E0E0; + &.iconBtnActive { + color: white; + background-color: var(--mantine-color-brandPrimary-4); + } +} \ No newline at end of file diff --git a/src/components/Button/IconButton.tsx b/src/components/Button/IconButton.tsx new file mode 100644 index 00000000..642048e0 --- /dev/null +++ b/src/components/Button/IconButton.tsx @@ -0,0 +1,31 @@ +import { Button, Stack, Tooltip, px } from '@mantine/core'; +import type { FC, ReactNode } from 'react'; +import classes from './Button.module.css' +import { IconSettings, TablerIconsProps } from '@tabler/icons-react'; +import { Text } from '@mantine/core'; +import React from 'react'; + +type IconButtonProps = { + onClick?: () => void; + renderIcon: () => ReactNode; + icon?: ReactNode; + active?: boolean; + tooltipLabel?: string; +} + +const IconButton: FC = (props) => { + const { iconBtn } = classes; + const { renderIcon, tooltipLabel } = props; + const Wrapper = tooltipLabel ? Tooltip : React.Fragment + return ( + + + + ); +}; + +export default IconButton; diff --git a/src/components/Button/ToggleButton.tsx b/src/components/Button/ToggleButton.tsx index 9edaf360..099d017c 100644 --- a/src/components/Button/ToggleButton.tsx +++ b/src/components/Button/ToggleButton.tsx @@ -7,17 +7,19 @@ type ToggleButtonProps = ButtonProps & { toggled: boolean; renderIcon?: () => ReactNode; label?: string; + iconPosition?: 'left' | 'right'; + customClassName?: string | null; }; export const ToggleButton: FC = (props) => { - const { onClick, toggled, label = '', renderIcon } = props; + const { onClick, toggled, label = '', renderIcon, customClassName = '' } = props; const { toggleBtn, toggleBtnActive } = classes; - + const iconPosition = props.iconPosition === 'right' ? 'rightSection' : 'leftSection' return ( ); diff --git a/src/components/Header/HelpModal.tsx b/src/components/Header/HelpModal.tsx new file mode 100644 index 00000000..0bf69f87 --- /dev/null +++ b/src/components/Header/HelpModal.tsx @@ -0,0 +1,110 @@ +import { Box, Button, Modal, Text, Tooltip, px } from '@mantine/core'; +import { FC, useEffect, useMemo } from 'react'; +import { useAbout } from '@/hooks/useGetAbout'; +import { IconAlertCircle, IconBook2, IconBrandGithub, IconBrandSlack, IconBusinessplan } from '@tabler/icons-react'; +import { useHeaderContext } from '@/layouts/MainLayout/Context'; +import styles from './styles/HelpModal.module.css' + +const helpResources = [ + { + icon: IconBusinessplan, + title: 'Production support', + description: 'Get production support', + href: 'mailto:sales@parseable.io?subject=Production%20Support%20Query', //https://www.parseable.io/pricing + }, + { + icon: IconBrandSlack, + title: 'Slack', + description: 'Join the Slack community', + href: 'https://join.slack.com/t/parseable/shared_invite/zt-23t505gz7-zX4T10OvkS8RAhnme4gDZQ', + }, + { + icon: IconBrandGithub, + title: 'GitHub', + description: 'Find resources on GitHub', + href: 'https://github.com/parseablehq/parseable', + }, + { + icon: IconBook2, + title: 'Documentation', + description: 'Refer the documentation', + href: 'https://www.parseable.com/docs', + }, +]; + +type HelpCardProps = { + data: (typeof helpResources)[number]; +}; + +const HelpCard: FC = (props) => { + const { data } = props; + + const classes = styles; + const { HelpIconBox } = classes; + + return ( + + + + ); +}; + +type HelpModalProps = { + opened: boolean; + close(): void; +}; + +const HelpModal: FC = (props) => { + const { opened, close } = props; + const { + state: { subInstanceConfig }, + } = useHeaderContext(); + + const { getAboutData, getAboutIsError, getAboutIsLoading } = useAbout(); + + const llmStatus = useMemo(() => { + let status = 'LLM API Key not set'; + if (getAboutData?.data?.llmActive) { + status = `${getAboutData?.data.llmProvider} configured`; + } + return status; + }, [getAboutData?.data?.llmActive]); + + useEffect(() => { + if (getAboutData?.data) { + subInstanceConfig.set(getAboutData?.data); + } + }, [getAboutData?.data]); + + const classes = styles; + const { + container, + aboutTitle, + aboutDescription, + actionBtn, + helpIconContainer, + aboutTextBox, + aboutTextKey, + aboutTextValue, + aboutTextInnerBox, + actionBtnRed, + } = classes; + + return ( + + + Need help? + Ensure uninterrupted deployment + + {helpResources.map((data) => ( + + ))} + + + + ); +}; + +export default HelpModal; diff --git a/src/components/Header/PrimaryHeader.tsx b/src/components/Header/PrimaryHeader.tsx index 8596af3f..c339e414 100644 --- a/src/components/Header/PrimaryHeader.tsx +++ b/src/components/Header/PrimaryHeader.tsx @@ -1,45 +1,48 @@ import logoInvert from '@/assets/images/brand/logo-invert.svg'; import { HOME_ROUTE } from '@/constants/routes'; -import { HEADER_HEIGHT, NAVBAR_WIDTH } from '@/constants/theme'; -import { Box, Button, Image, Tooltip } from '@mantine/core'; +import { HEADER_HEIGHT, NAVBAR_WIDTH, PRIMARY_HEADER_HEIGHT } from '@/constants/theme'; +import { Box, Button, Image, Stack, Tooltip } from '@mantine/core'; import { FC } from 'react'; -import styles from './styles/Header.module.css' - +import styles from './styles/Header.module.css'; +import { IconHelp, IconPremiumRights, IconStackPop } from '@tabler/icons-react'; +import { useHeaderContext } from '@/layouts/MainLayout/Context'; +import InfoModal from '../Navbar/infoModal'; +import HelpModal from './HelpModal'; const PrimaryHeader: FC = () => { const classes = styles; const { container, logoContainer, navContainer, imageSty, actionBtn } = classes; + const { + state: { maximized, helpModalOpen }, + methods: { toggleHelpModal }, + } = useHeaderContext(); + + if (maximized) return null; return ( - - + + + Parseable Logo - - - - - - - + + + + ); }; diff --git a/src/components/Header/Querier.tsx b/src/components/Header/Querier.tsx index e44ba5a1..c3ef359b 100644 --- a/src/components/Header/Querier.tsx +++ b/src/components/Header/Querier.tsx @@ -15,7 +15,7 @@ import { } from '@mantine/core'; import { useCallback } from 'react'; import classes from './styles/QueryBuilder.module.css'; -import { useLogsPageContext } from '@/pages/Logs/Context'; +import { useLogsPageContext } from '@/pages/Logs/context'; import { IconFilter, IconPlus } from '@tabler/icons-react'; import { operatorLabelMap, useQueryFilterContext } from '@/providers/QueryFilterProvider'; import { noValueOperators, textFieldOperators, numberFieldOperators } from '@/providers/QueryFilterProvider'; diff --git a/src/components/Header/RefreshInterval.tsx b/src/components/Header/RefreshInterval.tsx index b9f1f140..0e2be3fa 100644 --- a/src/components/Header/RefreshInterval.tsx +++ b/src/components/Header/RefreshInterval.tsx @@ -1,6 +1,6 @@ import useMountedState from '@/hooks/useMountedState'; import { useHeaderContext } from '@/layouts/MainLayout/Context'; -import { Button, Menu, Text, px } from '@mantine/core'; +import { Button, Menu, Text, Tooltip, px } from '@mantine/core'; import { IconRefresh, IconRefreshOff } from '@tabler/icons-react'; import ms from 'ms'; import type { FC } from 'react'; @@ -36,9 +36,11 @@ const RefreshInterval: FC = () => { return ( - + + + void; -} +}; const RefreshNow: FC = (props) => { const { refreshNowBtn } = classes; return ( - + + + ); }; diff --git a/src/components/Header/StreamDropdown.tsx b/src/components/Header/StreamDropdown.tsx new file mode 100644 index 00000000..e7e51866 --- /dev/null +++ b/src/components/Header/StreamDropdown.tsx @@ -0,0 +1,43 @@ +import { Select, Text } from '@mantine/core'; +import { useCallback, useEffect, useRef, useState } from 'react'; +import classes from './styles/LogQuery.module.css'; +import { IconCaretDown } from '@tabler/icons-react'; +import { useHeaderContext } from '@/layouts/MainLayout/Context'; +import { useNavigate, useParams } from 'react-router-dom'; + +const StreamDropdown = () => { + const { + state: { userSpecficStreams, subLogQuery }, + } = useHeaderContext(); + const { streamName } = useParams(); + + const selectedStream = subLogQuery.get().streamName; + const valueRef = useRef(selectedStream); + const navigate = useNavigate(); + + const handleChange: (value: string | null) => void = useCallback((value: string | null) => { + if (value === null) return; + + valueRef.current = value; + navigate(`/${value}/logs`); + }, []); + + useEffect(() => { + valueRef.current = streamName || null; + }, [streamName]) + + return ( + */} + {/* + + + + + + ); +}; + +type CombinatorToggleType = { + isOrSelected: boolean; + onCombinatorChange: (combinator: Combinator) => void; +}; + +const CombinatorToggle = (props: CombinatorToggleType) => { + const { onCombinatorChange, isOrSelected } = props; + return ( + + onCombinatorChange('or')}> + OR + + onCombinatorChange('and')}> + AND + + + ); +}; + +const RuleSet = (props: RuleSetProps) => { + const { state: queryBuilderState, methods: queryBuilderMethods } = useQueryFilterContext(); + const { fieldTypeMap, query } = queryBuilderState; + const { addRuleToGroup, updateGroupCombinator, updateParentCombinator } = queryBuilderMethods; + const { ruleSet } = props; + const { combinator: ruleSetCombinator, id, rules } = ruleSet; + + const onCombinatorChange = useCallback((combinator: Combinator) => updateGroupCombinator(id, combinator), []); + const addCondtionBtnHandler = useCallback(() => { + addRuleToGroup(id); + }, []); + + return ( + + + + {rules.map((rule) => { + return ; + })} + + + + + + + + + + + + ); +}; + +const AddRuleGroupBtn = ({ createRuleGroup }: { createRuleGroup: () => void }) => ( + + + + + + + Add + + + +); + +type RuleSetPillProps = { + ruleSet: RuleGroupTypeOverride; +}; + +const RuleSetPills = (props: RuleSetPillProps) => { + const { ruleSet } = props; + const { rules, combinator } = ruleSet; + const { + methods: { deleteRuleFromGroup }, + } = useQueryFilterContext(); + + return ( + + {rules.map((rule, index) => { + const shouldShowCombinatorPill = rules.length !== 1 && index + 1 !== rules.length; + const operatorLabel = operatorLabelMap[rule.operator] || rule.operator; + return ( + + deleteRuleFromGroup(ruleSet.id, rule.id)}> + {rule.field} {operatorLabel} {rule.value} + + {shouldShowCombinatorPill && {combinator}} + + ); + })} + + ); +}; + +export const QueryPills = (props: QueryPillProps) => { + const { state: {query}, methods: queryBuilderMethods } = useQueryFilterContext(); + const { combinator, rules: ruleSets } = query; + return ( + + + {ruleSets.map((ruleSet, index) => { + const shouldShowCombinatorPill = ruleSets.length !== 1 && index + 1 !== ruleSets.length; + return ( + + + {shouldShowCombinatorPill && {combinator}} + + ); + })} + + + ); +}; + +const FilterBtnPlaceholder = () => { + return ( + + + Click to add filter + + ); +}; + +export const FilterQueryBuilder = () => { + const { state: queryBuilderState, methods: queryBuilderMethods } = useQueryFilterContext(); + const { query, isSumbitDisabled } = queryBuilderState; + const { createRuleGroup, clearFilters, applyQuery } = queryBuilderMethods; + const { + methods: { + makeExportData, + toggleShowQueryEditor, + openDeleteModal, + openAlertsModal, + openRetentionModal, + toggleLiveTail, + toggleCustQuerySearchMode, + toggleBuilderModal, + closeBuilderModal + }, + state: { + custQuerySearchState: { isQuerySearchActive, mode, viewMode }, + liveTailToggled, + builderModalOpen, + }, + } = useLogsPageContext(); + const isFiltersApplied = isQuerySearchActive && mode === 'filters'; + + return ( + + + + {query.rules.map((ruleSet) => { + return ; + })} + + + + + + + + + ); +}; diff --git a/src/pages/Logs/HeaderPagination.tsx b/src/pages/Logs/HeaderPagination.tsx index e1ce6e68..1de25895 100644 --- a/src/pages/Logs/HeaderPagination.tsx +++ b/src/pages/Logs/HeaderPagination.tsx @@ -6,7 +6,7 @@ import { Box, Button, Text, Tooltip, px } from '@mantine/core'; import dayjs from 'dayjs'; import { FC, useEffect } from 'react'; import FillCarousel from './CarouselSlide'; -import { useLogsPageContext } from './Context'; +import { useLogsPageContext } from './context'; import Loading from '@/components/Loading'; import { IconZoomIn, IconZoomOut } from '@tabler/icons-react'; import headerPaginationStyles from './styles/HeaderPagination.module.css'; diff --git a/src/pages/Logs/LogRow.tsx b/src/pages/Logs/LogRow.tsx index 2f9529a9..1e9dd442 100644 --- a/src/pages/Logs/LogRow.tsx +++ b/src/pages/Logs/LogRow.tsx @@ -2,7 +2,7 @@ import { parseLogData } from '@/utils'; import { Box, px } from '@mantine/core'; import { IconArrowNarrowRight } from '@tabler/icons-react'; import { FC, Fragment } from 'react'; -import { useLogsPageContext } from './Context'; +import { useLogsPageContext } from './context'; import { Log } from '@/@types/parseable/api/query'; import tableStyles from './styles/Logs.module.css' diff --git a/src/pages/Logs/LogTable.tsx b/src/pages/Logs/LogTable.tsx index 05556459..db60fc7d 100644 --- a/src/pages/Logs/LogTable.tsx +++ b/src/pages/Logs/LogTable.tsx @@ -19,7 +19,7 @@ import { } from '@mantine/core'; import { useCallback, useEffect, useMemo, useRef } from 'react'; import type { FC } from 'react'; -import { LOG_QUERY_LIMITS, useLogsPageContext, LOAD_LIMIT as loadLimit } from './Context'; +import { LOG_QUERY_LIMITS, useLogsPageContext, LOAD_LIMIT as loadLimit } from './context'; import LogRow from './LogRow'; import useMountedState from '@/hooks/useMountedState'; import { IconSelector, IconGripVertical, IconPin, IconPinFilled, IconSettings } from '@tabler/icons-react'; @@ -34,7 +34,8 @@ import { Log, SortOrder } from '@/@types/parseable/api/query'; import { usePagination } from '@mantine/hooks'; import { LogStreamSchemaData } from '@/@types/parseable/api/stream'; import tableStyles from './styles/Logs.module.css'; -import { HEADER_HEIGHT } from '@/constants/theme'; +import { HEADER_HEIGHT, LOGS_PRIMARY_TOOLBAR_HEIGHT, LOGS_SECONDARY_TOOLBAR_HEIGHT, PRIMARY_HEADER_HEIGHT } from '@/constants/theme'; +import { IconCodeCircle } from '@tabler/icons-react'; const skipFields = ['p_metadata', 'p_tags']; @@ -66,7 +67,7 @@ const LogTable: FC = () => { methods: { setPageOffset, resetQuerySearch }, } = useLogsPageContext(); const { - state: { subLogSearch, subLogQuery, subRefreshInterval, subLogSelectedTimeRange }, + state: { subLogSearch, subLogQuery, subRefreshInterval, subLogSelectedTimeRange, maximized }, } = useHeaderContext(); const [refreshInterval, setRefreshInterval] = useMountedState(null); const [logStreamError, setLogStreamError] = useMountedState(null); @@ -340,19 +341,22 @@ const LogTable: FC = () => { } }, [pinnedContianerRef, pinnedColumns]); + const primaryHeaderHeight = !maximized ? PRIMARY_HEADER_HEIGHT + LOGS_PRIMARY_TOOLBAR_HEIGHT + LOGS_SECONDARY_TOOLBAR_HEIGHT : 0; + return ( {!(logStreamError || logStreamSchemaError || logsError) ? ( Boolean(tableHeaders.length) && Boolean(pageLogData?.data.length) ? ( - + + style={{ display: 'flex', flexDirection: 'row', maxHeight: `calc(100vh - ${primaryHeaderHeight}px )` }}> ; +const renderSettingsIcon = () => ; +const renderLiveTailIcon = () => ; +const renderDeleteIcon = () => ; + +const PrimaryToolbar = () => { + const { + methods: { + makeExportData, + toggleShowQueryEditor, + openDeleteModal, + openAlertsModal, + openRetentionModal, + toggleLiveTail, + }, + state: { + custQuerySearchState: { isQuerySearchActive, mode }, + liveTailToggled, + }, + } = useLogsPageContext(); + const { + state: { subLogQuery, maximized, userSpecificAccessMap }, + methods: { resetTimeInterval, toggleMaximize }, + } = useHeaderContext(); + const exportHandler = (fileType: string | null) => { + const query = subLogQuery.get(); + const filename = `${query.streamName}-logs`; + if (fileType === 'CSV') { + downloadDataAsCSV(makeExportData('CSV'), filename); + } else if (fileType === 'JSON') { + downloadDataAsJson(makeExportData('JSON'), filename); + } + }; + return ( + + + + + + + {userSpecificAccessMap.hasDeleteAccess && ( + + )} + + + ); +}; + +export default PrimaryToolbar; diff --git a/src/pages/Logs/Querier.tsx b/src/pages/Logs/Querier.tsx new file mode 100644 index 00000000..6e1c0a77 --- /dev/null +++ b/src/pages/Logs/Querier.tsx @@ -0,0 +1,153 @@ +import { Button, Group, Menu, Modal, ScrollArea, Stack, px } from '@mantine/core'; +import { useLogsPageContext } from './context'; +import { ToggleButton } from '@/components/Button/ToggleButton'; +import { IconChevronDown, IconCodeCircle, IconFilter } from '@tabler/icons-react'; +import classes from './styles/Querier.module.css'; +import { Text } from '@mantine/core'; +import { useQueryFilterContext } from '@/providers/QueryFilterProvider'; +import { FilterQueryBuilder, QueryPills } from './FilterQueryBuilder'; +import { useCallback } from 'react'; +import { QueryEditor, AppliedSQLQuery } from './QueryEditor'; + +const getLabel = (mode: string | null) => { + return mode === 'filters' ? 'Filters' : mode === 'sql' ? 'SQL' : ''; +}; + +const FilterPlaceholder = () => { + return ( + + + Click to add filter + + ); +}; + +const SQLEditorPlaceholder = () => { + return ( + + + Click to write query + + ); +}; + +const ModalTitle = ({ title }: { title: string }) => { + return {title}; +}; + +const QuerierModal = () => { + const { + methods: { + makeExportData, + toggleShowQueryEditor, + openDeleteModal, + openAlertsModal, + openRetentionModal, + toggleLiveTail, + toggleCustQuerySearchMode, + toggleBuilderModal, + closeBuilderModal + }, + state: { + custQuerySearchState: { isQuerySearchActive, mode, viewMode }, + liveTailToggled, + builderModalOpen, + }, + } = useLogsPageContext(); + + const { state: queryFilterState, methods: queryFilterMethods } = useQueryFilterContext(); + const { query, isSumbitDisabled, appliedQuery } = queryFilterState; + const { createRuleGroup, clearFilters, applyQuery } = queryFilterMethods; + + const clearContextHandler = mode === 'filters' ? clearFilters : mode === 'sql' ? () => {} : () => {}; + const applyContextHandler = mode === 'filters' ? applyQuery : mode === 'sql' ? () => {} : () => {}; + const disableSubmit = mode === 'filters' ? isSumbitDisabled : mode === 'sql' ? true : true; + + const handleClear = useCallback(() => { + clearContextHandler(); + closeBuilderModal(); + }, []) + + const handleApply = useCallback(() => { + applyContextHandler(); + closeBuilderModal(); + }, []) + + return ( + }> + + {/* */} + {viewMode === 'filters' ? : } + {/* */} + + + ); +}; + +const Querier = () => { + const { + methods: { + makeExportData, + toggleShowQueryEditor, + openDeleteModal, + openAlertsModal, + openRetentionModal, + toggleLiveTail, + toggleCustQuerySearchMode, + toggleBuilderModal, + }, + state: { + custQuerySearchState: { isQuerySearchActive, mode, viewMode }, + liveTailToggled, + }, + } = useLogsPageContext(); + const isFiltersApplied = mode === 'filters' && isQuerySearchActive; + const isSqlSearchActive = mode === 'sql' && isQuerySearchActive; + return ( + + + + +
+ {}} + toggled={false} + renderIcon={() => } + label={getLabel(viewMode)} + iconPosition="right" + customClassName={classes.modeButton} + /> +
+
+ + toggleCustQuerySearchMode('filters')} + style={{ padding: '0.5rem 2.25rem 0.5rem 0.75rem' }}> + Filters + + toggleCustQuerySearchMode('sql')} + style={{ padding: '0.5rem 2.25rem 0.5rem 0.75rem' }}> + SQL + + +
+ + { viewMode === 'filters' && + (isFiltersApplied ? : ) + } + { viewMode === 'sql' && + (isSqlSearchActive ? : ) + } + +
+ ); +}; + +export default Querier; diff --git a/src/pages/Logs/QueryCodeEditor.tsx b/src/pages/Logs/QueryCodeEditor.tsx index 518e7b5d..6ea67a65 100644 --- a/src/pages/Logs/QueryCodeEditor.tsx +++ b/src/pages/Logs/QueryCodeEditor.tsx @@ -1,16 +1,15 @@ import React, { FC, MutableRefObject, useCallback, useEffect } from 'react'; import Editor from '@monaco-editor/react'; import { useHeaderContext } from '@/layouts/MainLayout/Context'; -import { Box, Button, Flex, Text, TextInput, Tooltip, px } from '@mantine/core'; +import { Box, Button, Flex, ScrollArea, Stack, Text, TextInput, Tooltip, px } from '@mantine/core'; import { ErrorMarker, errChecker } from './ErrorMarker'; -import { IconPlayerPlayFilled, IconRotate } from '@tabler/icons-react'; import useMountedState from '@/hooks/useMountedState'; import { notify } from '@/utils/notification'; import { usePostLLM } from '@/hooks/usePostLLM'; import { sanitiseSqlString } from '@/utils/sanitiseSqlString'; -import { LOAD_LIMIT, useLogsPageContext } from '../Logs/Context'; +import { LOAD_LIMIT, useLogsPageContext } from './context'; import { Field } from '@/@types/parseable/dataType'; -import queryCodeStyles from './styles/QueryCode.module.css' +import queryCodeStyles from './styles/QueryCode.module.css'; type QueryCodeEditorProps = { inputRef: MutableRefObject; @@ -39,10 +38,10 @@ const QueryCodeEditor: FC = (props) => { } = useHeaderContext(); const { state: { - custQuerySearchState: { isQuerySearchActive }, + custQuerySearchState: { isQuerySearchActive, mode }, subLogStreamSchema, }, - methods: { resetQuerySearch, setCustSearchQuery }, + methods: { resetQuerySearch, setCustSearchQuery, closeBuilderModal }, } = useLogsPageContext(); const fields = subLogStreamSchema.get()?.fields || []; @@ -55,11 +54,12 @@ const QueryCodeEditor: FC = (props) => { const { data: resAIQuery, postLLMQuery } = usePostLLM(); const currentStreamName = subLogQuery.get().streamName; const isLlmActive = !!subInstanceConfig.get()?.llmActive; + const isSqlSearchActive = isQuerySearchActive && mode === 'sql' const updateQuery = useCallback((query: string) => { props.inputRef.current = query; - setQuery(query) - }, []) + setQuery(query); + }, []); const handleAIGenerate = useCallback(() => { if (!aiQuery?.length) { @@ -73,7 +73,7 @@ const QueryCodeEditor: FC = (props) => { if (resAIQuery) { const warningMsg = '-- LLM generated query is experimental and may produce incorrect answers\n-- Always verify the generated SQL before executing\n\n'; - updateQuery(warningMsg + resAIQuery); + updateQuery(warningMsg + resAIQuery); } }, [resAIQuery]); @@ -86,7 +86,7 @@ const QueryCodeEditor: FC = (props) => { useEffect(() => { if (currentStreamName !== localStreamName) { setlocalStreamName(currentStreamName); - const query = `SELECT * FROM ${currentStreamName} LIMIT ${LOAD_LIMIT}; ` + const query = `SELECT * FROM ${currentStreamName} LIMIT ${LOAD_LIMIT}; `; updateQuery(query); } setlocalLlmActive(isLlmActive); @@ -108,104 +108,63 @@ const QueryCodeEditor: FC = (props) => { const query = sanitiseSqlString(inputQuery); const parsedQuery = query.replace(/(\r\n|\n|\r)/gm, ''); setCustSearchQuery(parsedQuery, 'sql'); + closeBuilderModal(); }; - const classes = queryCodeStyles; - const { container, runQueryBtn, textContext, clearQueryBtn } = classes; - return ( - - - Search Query - - - - - - - + + + + {localLlmActive ? ( + setAiQuery(e.target.value)} + placeholder="Enter plain text to generate SQL query using OpenAI" + rightSectionWidth={'auto'} + rightSection={ + + } + /> + ) : ( + + + Know More: How to enable SQL generation with OpenAI ? + + + )} - - - {localLlmActive ? ( - setAiQuery(e.target.value)} - placeholder="Enter plain text to generate SQL query using OpenAI" - rightSectionWidth={'auto'} - style={{ - '& .mantine-Input-input': { - border: 'none', - borderRadius: 0, - backgroundColor: 'rgba(84,91,235,.2)', - '::placeholder': {}, - }, - '& .mantine-TextInput-rightSection ': { - height: '100%', - }, + + + - ✨ Generate - - } + onMount={handleEditorDidMount} /> - ) : ( - - - Know More: How to enable SQL generation with OpenAI ? - - - )} - - - - - - - - +
+ + + + + + ); }; @@ -225,12 +184,20 @@ const SchemaList = (props: { currentStreamName: string; fields: Field[] }) => { {leftColumns.map((config, index) => { - return {`${config}\n\n`}; + return ( + {`${config}\n\n`} + ); })} {rightColumns.map((config, index) => { - return {`${config}\n\n`}; + return ( + {`${config}\n\n`} + ); })} diff --git a/src/pages/Logs/QueryEditor.tsx b/src/pages/Logs/QueryEditor.tsx index bc5d7afb..111f2b49 100644 --- a/src/pages/Logs/QueryEditor.tsx +++ b/src/pages/Logs/QueryEditor.tsx @@ -1,23 +1,14 @@ -import { Box, Drawer } from '@mantine/core'; import type { FC } from 'react'; -import { LOAD_LIMIT, useLogsPageContext } from './Context'; +import { LOAD_LIMIT, useLogsPageContext } from './context'; import React, { useEffect } from 'react'; import { useHeaderContext } from '@/layouts/MainLayout/Context'; import QueryCodeEditor from './QueryCodeEditor'; -import viewLogStyles from './styles/ViewLogs.module.css' +import { CodeHighlight } from '@mantine/code-highlight'; -const QueryEditor: FC = () => { - const { - state: { - custQuerySearchState: { showQueryEditor }, - }, - methods: { toggleShowQueryEditor }, - } = useLogsPageContext(); +export const QueryEditor: FC = () => { const { state: { subLogQuery }, } = useHeaderContext(); - const onClose = () => toggleShowQueryEditor(); - const classes = viewLogStyles; const inputRef = React.useRef(); // to store input value even after the editor unmounts const currentStreamName = subLogQuery.get().streamName; useEffect(() => { @@ -29,13 +20,21 @@ const QueryEditor: FC = () => { } }, [currentStreamName]); + return ; +}; + +export const AppliedSQLQuery = () => { + const { + state: { + custQuerySearchState: { custSearchQuery }, + }, + } = useLogsPageContext(); return ( - - - - - + ); }; - -export default QueryEditor; diff --git a/src/pages/Logs/RetentionModal.tsx b/src/pages/Logs/RetentionModal.tsx new file mode 100644 index 00000000..9a1fb65b --- /dev/null +++ b/src/pages/Logs/RetentionModal.tsx @@ -0,0 +1,54 @@ +import { Box, Button, Group, Modal, Stack, TextInput } from '@mantine/core'; +import { useLogsPageContext } from './context'; +import { Text } from '@mantine/core'; +import classes from './styles/Logs.module.css'; +import { Editor } from '@monaco-editor/react'; +import { useAlertsEditor } from '@/hooks/useAlertsEditor'; +import { useRetentionEditor } from '@/hooks/useRetentionEditor'; + +const ModalTitle = () => { + return Retention; +}; + +const RententionModal = () => { + const { + state: { retentionModalOpen, currentStream }, + methods: { closeRetentionModal }, + } = useLogsPageContext(); + const { handleRetentionQueryChange, submitRetentionQuery, getLogRetentionData } = useRetentionEditor(currentStream); + return ( + }> + + + + + + + + + + ); +}; + +export default RententionModal; diff --git a/src/pages/Logs/SQLQueryBuilder.tsx b/src/pages/Logs/SQLQueryBuilder.tsx new file mode 100644 index 00000000..e69de29b diff --git a/src/pages/Logs/SecondaryToolbar.tsx b/src/pages/Logs/SecondaryToolbar.tsx new file mode 100644 index 00000000..d021aa7e --- /dev/null +++ b/src/pages/Logs/SecondaryToolbar.tsx @@ -0,0 +1,84 @@ +import { Menu, Stack, px } from '@mantine/core'; +import StreamDropdown from '@/components/Header/StreamDropdown'; +import IconButton from '@/components/Button/IconButton'; +import { useLogsPageContext } from './context'; +import { useHeaderContext } from '@/layouts/MainLayout/Context'; +import { downloadDataAsCSV, downloadDataAsJson } from '@/utils/exportHelpers'; +import classes from './styles/Toolbar.module.css'; +import { IconBolt, IconCodeCircle, IconDownload, IconExclamationCircle, IconMaximize, IconSettings, IconTrash } from '@tabler/icons-react'; +import { LOGS_PRIMARY_TOOLBAR_HEIGHT, LOGS_SECONDARY_TOOLBAR_HEIGHT } from '@/constants/theme'; +import { ToggleButton } from '@/components/Button/ToggleButton'; +import TimeRange from '@/components/Header/TimeRange'; +import RefreshInterval from '@/components/Header/RefreshInterval'; +import RefreshNow from '@/components/Header/RefreshNow'; +import StreamingButton from '@/components/Header/StreamingButton'; +import Querier from './Querier'; + +const renderExportIcon = () => ; +const renderMaximizeIcon = () => ; + +const SecondaryToolbar = () => { + const { + methods: { + makeExportData, + toggleShowQueryEditor, + openDeleteModal, + openAlertsModal, + openRetentionModal, + toggleLiveTail, + }, + state: { + custQuerySearchState: { isQuerySearchActive, mode }, + liveTailToggled, + }, + } = useLogsPageContext(); + const { + state: { subLogQuery, maximized, userSpecificAccessMap }, + methods: { resetTimeInterval, toggleMaximize }, + } = useHeaderContext(); + const exportHandler = (fileType: string | null) => { + const query = subLogQuery.get(); + const filename = `${query.streamName}-logs`; + if (fileType === 'CSV') { + downloadDataAsCSV(makeExportData('CSV'), filename); + } else if (fileType === 'JSON') { + downloadDataAsJson(makeExportData('JSON'), filename); + } + }; + return ( + + {!liveTailToggled && ( + + + + + + +
+ +
+
+ + exportHandler('CSV')} style={{ padding: '0.5rem 2.25rem 0.5rem 0.75rem' }}> + CSV + + exportHandler('JSON')} style={{ padding: '0.5rem 2.25rem 0.5rem 0.75rem' }}> + JSON + + +
+ + +
+ )} + {liveTailToggled && ( + + + + + )} +
+ ); +}; + +export default SecondaryToolbar; \ No newline at end of file diff --git a/src/pages/Logs/ViewLog.tsx b/src/pages/Logs/ViewLog.tsx index 62475d3d..3adeb985 100644 --- a/src/pages/Logs/ViewLog.tsx +++ b/src/pages/Logs/ViewLog.tsx @@ -2,7 +2,7 @@ import useMountedState from '@/hooks/useMountedState'; import { Box, Chip, CloseButton, Divider, Drawer, Text, px } from '@mantine/core'; import type { FC } from 'react'; import { useEffect, Fragment, useMemo } from 'react'; -import { useLogsPageContext } from './Context'; +import { useLogsPageContext } from './context'; import dayjs from 'dayjs'; import viewLogStyles from './styles/ViewLogs.module.css' import { CodeHighlight } from '@mantine/code-highlight'; diff --git a/src/pages/Logs/index.tsx b/src/pages/Logs/index.tsx index aa7d804c..5b82e263 100644 --- a/src/pages/Logs/index.tsx +++ b/src/pages/Logs/index.tsx @@ -1,20 +1,41 @@ import { Box } from '@mantine/core'; import { useDocumentTitle } from '@mantine/hooks'; -import { FC } from 'react'; -import LogTable from './LogTable'; +import { FC, useEffect } from 'react'; +import StaticLogTable from './LogTable'; +import LiveLogTable from '../LiveTail/LogTable' import ViewLog from './ViewLog'; -import QueryEditor from './QueryEditor'; -// import HeaderPagination from './HeaderPagination'; +import DeleteStreamModal from './DeleteStreamModal'; +import AlertsModal from './AlertsModal'; +import RententionModal from './RetentionModal'; +import { useLogsPageContext } from './context'; +import PrimaryToolbar from './PrimaryToolbar'; +import SecondaryToolbar from './SecondaryToolbar'; +import { useHeaderContext } from '@/layouts/MainLayout/Context'; const Logs: FC = () => { useDocumentTitle('Parseable | Logs'); + const { + state: { liveTailToggled }, + } = useLogsPageContext(); + const { + state: { maximized }, + } = useHeaderContext(); return ( - {/* */} - + + + + {!maximized && ( + <> + + + + )} + {/* {liveTailToggled ? : } */} + {/* TODO: need to move the live logtable into the Logs folder */} + - ); }; diff --git a/src/pages/Logs/styles/Querier.module.css b/src/pages/Logs/styles/Querier.module.css new file mode 100644 index 00000000..f4ecbfc0 --- /dev/null +++ b/src/pages/Logs/styles/Querier.module.css @@ -0,0 +1,182 @@ +.filterContainer { + padding-left: 0.6rem; + background-color: white; + color: #211F1F; + cursor: pointer; + flex: 1; + flex-direction: row; + overflow-y: hidden; + align-items: center; +} + +.container { + background-color: white; + color: #211F1F; + border: 1px var(--mantine-color-gray-3 ) solid; + border-radius: rem(8px); + cursor: pointer; + flex: 1; + overflow-y: hidden; + flex-direction: row; + margin-right: 0.675rem; +} + +.modeContainer { + width: 5rem; + height: 100%; + text-align: center; + align-items: center; + justify-content: center; + border-right: 1px solid var(--mantine-color-gray-4); +} + +.modeLabel { + /* color: white; */ + font-weight: 600; +} + +.modeButton { + border-top-right-radius: 0px !important; + border-bottom-right-radius: 0px !important; + width: 6rem; + border-top: none !important; + border-left: none !important; + border-bottom: none !important; + margin: 0px !important; +} + +.placeholderText { + color: var(--mantine-color-gray-5); + font-size: 1rem; + font-weight: 500; +} + +.addRuleContainer { + border: rem(2px) solid var(--mantine-color-gray-3); + height: 6rem; + width: 100%; + border-style: dashed; + border-spacing: 400px; + cursor: pointer; + align-items: center; + justify-content: center; + border-radius: rem(6px); + color: var(--mantine-primary-color-3); + font-weight: 400; +} + +.ruleSet { + border-radius: rem(6px); + padding: 1rem; + width: 100%; + background-color: var(--mantine-color-gray-1); +} + +.toggleBtnContainer { + flex-direction: row; + border: 1px solid black; + width: fit-content; + border-radius: rem(6px); + background-color: white; + border-color: transparent; + display: flex; + flex-direction: row; + cursor: pointer; + align-self: flex-end; +} + +.toggleBtnText { + padding: 0.2rem 0.6rem; + border-radius: rem(6px); + font-weight: 500; + font-size: small; + &.toggleBtnActive { + background-color: var(--mantine-primary-color-4) !important; + color: white; + } +} + +.ruleContainer { + flex-direction: row; + width: 100%; + align-items: center; +} + +.deleteRulebtn { + cursor: pointer; +} + +.addConditionBtn { + width: fit-content; + font-size: small; +} + +.parentCombinatorToggleContainer { + width: fit-content; + background-color: var(--mantine-color-gray-2); + border-radius: rem(6px); + position: 'relative'; + margin-left: 16px; + border: 1px solid var(--mantine-color-gray-4); + padding: 0.2rem; +} + +.ruleSetConnector { + width: 0; + height: 100%; + margin-left: 60px; + border: 1px solid var(--mantine-color-gray-4); +} + +.modalHeader { + font-weight: 500; + font-size: large; + margin-bottom: 1rem; +} + +.queryBuilderBtn { + background-color: white; + padding: 4px 12px; + color: #211F1F; + border: 1px var(--mantine-color-gray-4) solid; + border-radius: rem(8px); + cursor: pointer; + height: 2.2rem; + overflow: auto; + flex: 1; + margin-right: 0.625rem; +} + +.parentCombinatorPill { + color: white; + background: var(--mantine-color-teal-4); + text-transform: uppercase; + font-weight: 700; +} + +.childCombinatorPill { + color: white; + background: var(--mantine-color-indigo-4); + text-transform: uppercase; + font-weight: 700; +} + +.footer { + flex-direction: row; + justify-content: flex-end; + padding: 1.5rem; + padding-right: 0rem; + padding-top: 0.75rem; +} + +.queryBuilderContainer { + border-top-left-radius: rem(6px); + border-top-right-radius: rem(6px); + /* border-bottom: 1px solid var(--mantine-color-gray-3); */ +} + +.queryBuilderBtnPlaceholder { + color: var(--mantine-color-gray-5); + font-size: 1rem; + font-weight: 500; +} diff --git a/src/pages/Logs/styles/QueryCode.module.css b/src/pages/Logs/styles/QueryCode.module.css index e7c52353..2edc53b9 100644 --- a/src/pages/Logs/styles/QueryCode.module.css +++ b/src/pages/Logs/styles/QueryCode.module.css @@ -54,3 +54,11 @@ font-size: 1rem; font-weight: 600; } + +.footer { + border-top: 1px solid var(--mantine-color-gray-4) ; + flex-direction: row; + justify-content: flex-end; + padding: 1.5rem; + padding-right: 0rem; +} \ No newline at end of file diff --git a/src/pages/Logs/styles/Toolbar.module.css b/src/pages/Logs/styles/Toolbar.module.css new file mode 100644 index 00000000..5b8e70a4 --- /dev/null +++ b/src/pages/Logs/styles/Toolbar.module.css @@ -0,0 +1,42 @@ + +.streamSelectDescription { + font-size: 12px; + font-weight: 500; + color: var(--mantine-color-gray-6); +} + +.streamInput { + border: none; + padding-left: 0; + padding-right: 0; + height: 50px; + font-size: 24px; + font-weight: 600; + margin-top: -10px; + background-color: transparent; + cursor: pointer; + width: 200px; +} + +.chevronDown { + color: var(--mantine-color-gray-9); +} + +.streamSelect { + margin-left: 0.625rem; +} + +.logsPrimaryToolbar { + padding: 0.625rem 0; + border-bottom: 1px solid var(--mantine-color-gray-3); + width: 100%; + flex-direction: row; + justify-content: space-between; + align-items: center; +} + +.logsSecondaryToolbar { + width: '100%'; + flex-direction: row; + padding: 1rem 0.625rem; +} \ No newline at end of file diff --git a/src/providers/QueryFilterProvider.tsx b/src/providers/QueryFilterProvider.tsx index 46ca283c..5ca94eee 100644 --- a/src/providers/QueryFilterProvider.tsx +++ b/src/providers/QueryFilterProvider.tsx @@ -1,5 +1,5 @@ import { useHeaderContext } from '@/layouts/MainLayout/Context'; -import { useLogsPageContext } from '@/pages/Logs/Context'; +import { useLogsPageContext } from '@/pages/Logs/context'; import { generateRandomId } from '@/utils'; import { ReactNode, createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; import { Field, RuleGroupType, RuleType, formatQuery } from 'react-querybuilder'; @@ -154,11 +154,10 @@ const defaultQuery = { }; const QueryFilterProvider = (props: QueryFilterProviderProps) => { - const [isModalOpen, setModalOpen] = useState(false); const [isSumbitDisabled, setSubmitDisabled] = useState(true); const { - state: { subLogStreamSchema, custQuerySearchState }, - methods: { setCustSearchQuery, resetQuerySearch }, + state: { subLogStreamSchema, custQuerySearchState, builderModalOpen: isModalOpen }, + methods: { setCustSearchQuery, resetQuerySearch, closeBuilderModal }, } = useLogsPageContext(); const { state: { subAppContext }, @@ -250,14 +249,6 @@ const QueryFilterProvider = (props: QueryFilterProviderProps) => { }); }, []); - const openBuilderModal = useCallback(() => { - return setModalOpen(true); - }, []); - - const closeBuilderModal = useCallback(() => { - return setModalOpen(false); - }, []); - const updateParentCombinator = useCallback((combinator: Combinator) => { return setQuery((prev) => { return { ...prev, combinator: combinator }; @@ -274,12 +265,12 @@ const QueryFilterProvider = (props: QueryFilterProviderProps) => { const parsedQuery = parseQuery(); setCustSearchQuery(parsedQuery, 'filters'); setAppliedQuery(query); - setModalOpen(false); + closeBuilderModal() }, [query]); const clearFilters = useCallback(() => { resetQuerySearch(); - setModalOpen(false); + closeBuilderModal(); setAppliedQuery(defaultQuery); setQuery(defaultQuery); }, []); @@ -361,7 +352,6 @@ const QueryFilterProvider = (props: QueryFilterProviderProps) => { applyQuery, clearFilters, closeBuilderModal, - openBuilderModal, }; const value = useMemo(() => ({ state, methods }), [state, methods]); diff --git a/src/routes/elements.tsx b/src/routes/elements.tsx index 0eb92e7b..258bd3e7 100644 --- a/src/routes/elements.tsx +++ b/src/routes/elements.tsx @@ -14,7 +14,7 @@ import { } from '@/components/Header/SubHeader'; // page-wise providers -import LogsPageProvider from '@/pages/Logs/Context'; +import LogsPageProvider from '@/pages/Logs/context'; import StatsPageProvider from '@/pages/Stats/Context'; // component-wise providers @@ -46,7 +46,7 @@ export const LogsElement: FC = () => { - + {/* */} diff --git a/src/utils/sanitiseSqlString.ts b/src/utils/sanitiseSqlString.ts index d41dc84d..c85e1ddd 100644 --- a/src/utils/sanitiseSqlString.ts +++ b/src/utils/sanitiseSqlString.ts @@ -1,5 +1,5 @@ import { notify } from './notification'; -import { LOAD_LIMIT } from '@/pages/Logs/Context'; +import { LOAD_LIMIT } from '@/pages/Logs/context'; export const sanitiseSqlString = (sqlString: string): string => { const withoutComments = sqlString.replace(/--.*$/gm, ''); From e43ea337393a99536aa3d0123a21a273c8ea7723 Mon Sep 17 00:00:00 2001 From: balaji-jr Date: Tue, 27 Feb 2024 13:06:00 +0530 Subject: [PATCH 02/17] upto event timeline chart --- package.json | 1 + pnpm-lock.yaml | 211 +++++++++++++++ src/components/Navbar/UserModal.tsx | 46 ++++ src/components/Navbar/index.tsx | 248 ++++-------------- src/components/Navbar/infoModal.tsx | 60 +---- .../Navbar/styles/Navbar.module.css | 5 + src/layouts/MainLayout/Context.tsx | 14 +- src/main.tsx | 1 + src/pages/LiveTail/styles/Logs.module.css | 1 + src/pages/Logs/AlertsModal.tsx | 22 +- src/pages/Logs/Context.tsx | 116 ++++---- src/pages/Logs/EventTimeLineGraph.tsx | 67 +++++ src/pages/Logs/LogTable.tsx | 55 +++- src/pages/Logs/PrimaryToolbar.tsx | 42 +-- src/pages/Logs/RetentionModal.tsx | 42 ++- src/pages/Logs/SecondaryToolbar.tsx | 86 +++--- src/pages/Logs/index.tsx | 27 +- .../Logs/styles/EventTimeLineGraph.module.css | 5 + src/pages/Logs/styles/Logs.module.css | 4 + 19 files changed, 633 insertions(+), 420 deletions(-) create mode 100644 src/components/Navbar/UserModal.tsx create mode 100644 src/pages/Logs/EventTimeLineGraph.tsx create mode 100644 src/pages/Logs/styles/EventTimeLineGraph.module.css diff --git a/package.json b/package.json index a36f67ed..c4df4c91 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@apache-arrow/ts": "^14.0.2", "@emotion/react": "^11.11.1", "@mantine/carousel": "^7.5.1", + "@mantine/charts": "^7.5.3", "@mantine/code-highlight": "^7.5.1", "@mantine/core": "^7.5.1", "@mantine/dates": "^7.5.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5bef580c..d97d47aa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ dependencies: '@mantine/carousel': specifier: ^7.5.1 version: 7.5.1(@mantine/core@7.5.1)(@mantine/hooks@7.5.1)(embla-carousel-react@7.1.0)(react-dom@18.2.0)(react@18.2.0) + '@mantine/charts': + specifier: ^7.5.3 + version: 7.5.3(@mantine/core@7.5.1)(@mantine/hooks@7.5.1)(react-dom@18.2.0)(react@18.2.0)(recharts@2.12.1) '@mantine/code-highlight': specifier: ^7.5.1 version: 7.5.1(@mantine/core@7.5.1)(@mantine/hooks@7.5.1)(react-dom@18.2.0)(react@18.2.0) @@ -651,6 +654,22 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /@mantine/charts@7.5.3(@mantine/core@7.5.1)(@mantine/hooks@7.5.1)(react-dom@18.2.0)(react@18.2.0)(recharts@2.12.1): + resolution: {integrity: sha512-TgoBVACbmAxCHZQOL/K8DlijPZ4Ogv8S4hVXdxFgwUnKUzvnLZanaan2Vu3s7111HYCY2qHwgJwuCNtKTGuARQ==} + peerDependencies: + '@mantine/core': 7.5.3 + '@mantine/hooks': 7.5.3 + react: ^18.2.0 + react-dom: ^18.2.0 + recharts: ^2.10.3 + dependencies: + '@mantine/core': 7.5.1(@mantine/hooks@7.5.1)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0) + '@mantine/hooks': 7.5.1(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + recharts: 2.12.1(react-dom@18.2.0)(react@18.2.0) + dev: false + /@mantine/code-highlight@7.5.1(@mantine/core@7.5.1)(@mantine/hooks@7.5.1)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-zCdJ911r7WacTC8aQb66oB299jhcnQCQ8uS1kXYioUvJ/lOc0aiFh659KokmUvIL6ZdowCvffwaodiUFDZcqBw==} peerDependencies: @@ -971,6 +990,48 @@ packages: resolution: {integrity: sha512-n7RlEEJ+4x4TS7ZQddTmNSxP+zziEG0TNsMfiRIxcIVXt71ENJ9ojeXmGO3wPoTdn7pJcU2xc3CJYMktNT6DPg==} dev: false + /@types/d3-array@3.2.1: + resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==} + dev: false + + /@types/d3-color@3.1.3: + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} + dev: false + + /@types/d3-ease@3.0.2: + resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} + dev: false + + /@types/d3-interpolate@3.0.4: + resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} + dependencies: + '@types/d3-color': 3.1.3 + dev: false + + /@types/d3-path@3.1.0: + resolution: {integrity: sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==} + dev: false + + /@types/d3-scale@4.0.8: + resolution: {integrity: sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==} + dependencies: + '@types/d3-time': 3.0.3 + dev: false + + /@types/d3-shape@3.1.6: + resolution: {integrity: sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==} + dependencies: + '@types/d3-path': 3.1.0 + dev: false + + /@types/d3-time@3.0.3: + resolution: {integrity: sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==} + dev: false + + /@types/d3-timer@3.0.2: + resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} + dev: false + /@types/hoist-non-react-statics@3.3.1: resolution: {integrity: sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==} dependencies: @@ -1533,6 +1594,77 @@ packages: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} dev: false + /d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + dependencies: + internmap: 2.0.3 + dev: false + + /d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + dev: false + + /d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + dev: false + + /d3-format@3.1.0: + resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==} + engines: {node: '>=12'} + dev: false + + /d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + dependencies: + d3-color: 3.1.0 + dev: false + + /d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + dev: false + + /d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.0 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + dev: false + + /d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + dependencies: + d3-path: 3.1.0 + dev: false + + /d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + dependencies: + d3-time: 3.1.0 + dev: false + + /d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + dependencies: + d3-array: 3.2.4 + dev: false + + /d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + dev: false + /dayjs@1.11.10: resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==} dev: false @@ -1560,6 +1692,10 @@ packages: ms: 2.1.2 dev: true + /decimal.js-light@2.5.1: + resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} + dev: false + /deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true @@ -1987,6 +2123,10 @@ packages: engines: {node: '>=0.10.0'} dev: true + /eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + dev: false + /execa@4.1.0: resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==} engines: {node: '>=10'} @@ -2009,6 +2149,11 @@ packages: resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} dev: true + /fast-equals@5.0.1: + resolution: {integrity: sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==} + engines: {node: '>=6.0.0'} + dev: false + /fast-glob@3.2.12: resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==} engines: {node: '>=8.6.0'} @@ -2342,6 +2487,11 @@ packages: side-channel: 1.0.4 dev: true + /internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + dev: false + /invariant@2.2.4: resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} dependencies: @@ -2589,6 +2739,10 @@ packages: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} dev: true + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: false + /long@5.2.3: resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} dev: false @@ -3234,6 +3388,19 @@ packages: react: 18.2.0 dev: false + /react-smooth@4.0.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-2NMXOBY1uVUQx1jBeENGA497HK20y6CPGYL1ZnJLeoQ8rrc3UfmOM82sRxtzpcoCkUMy4CS0RGylfuVhuFjBgg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + fast-equals: 5.0.1 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-transition-group: 4.4.5(react-dom@18.2.0)(react@18.2.0) + dev: false + /react-style-singleton@2.2.1(@types/react@18.2.14)(react@18.2.0): resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==} engines: {node: '>=10'} @@ -3299,6 +3466,31 @@ packages: loose-envify: 1.4.0 dev: false + /recharts-scale@0.4.5: + resolution: {integrity: sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==} + dependencies: + decimal.js-light: 2.5.1 + dev: false + + /recharts@2.12.1(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-35vUCEBPf+pM+iVgSgVTn86faKya5pc4JO6cYJL63qOK2zDEyzDn20Tdj+CDI/3z+VcpKyQ8ZBQ9OiQ+vuAbjg==} + engines: {node: '>=14'} + peerDependencies: + react: ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 + dependencies: + clsx: 2.0.0 + eventemitter3: 4.0.7 + lodash: 4.17.21 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-is: 16.13.1 + react-smooth: 4.0.0(react-dom@18.2.0)(react@18.2.0) + recharts-scale: 0.4.5 + tiny-invariant: 1.3.1 + victory-vendor: 36.9.1 + dev: false + /redux@4.2.1: resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==} dependencies: @@ -3748,6 +3940,25 @@ packages: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true + /victory-vendor@36.9.1: + resolution: {integrity: sha512-+pZIP+U3pEJdDCeFmsXwHzV7vNHQC/eIbHklfe2ZCZqayYRH7lQbHcVgsJ0XOOv27hWs4jH4MONgXxHMObTMSA==} + dependencies: + '@types/d3-array': 3.2.1 + '@types/d3-ease': 3.0.2 + '@types/d3-interpolate': 3.0.4 + '@types/d3-scale': 4.0.8 + '@types/d3-shape': 3.1.6 + '@types/d3-time': 3.0.3 + '@types/d3-timer': 3.0.2 + d3-array: 3.2.4 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-scale: 4.0.2 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-timer: 3.0.1 + dev: false + /vite@4.3.9(@types/node@20.3.2): resolution: {integrity: sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==} engines: {node: ^14.18.0 || >=16.0.0} diff --git a/src/components/Navbar/UserModal.tsx b/src/components/Navbar/UserModal.tsx new file mode 100644 index 00000000..bb82e489 --- /dev/null +++ b/src/components/Navbar/UserModal.tsx @@ -0,0 +1,46 @@ +import { Box, Button, Group, Modal, Stack, TextInput } from '@mantine/core'; +import { Text } from '@mantine/core'; +import { Editor } from '@monaco-editor/react'; +import { useAlertsEditor } from '@/hooks/useAlertsEditor'; + +const ModalTitle = () => { + return User; +}; + +type UserInfoProps = { + username: string; + previlage: string; +} + +type UserModalProps = { + opened: boolean; + onClose: () => void; + userData: {[key: string]: string} +} + +const UserInfo = ({username, previlage}) => { + return ( + + + + ) +} + +const UserModal = (props: UserModalProps) => { + return ( + }> + + + + + + + ); +}; + +export default UserModal; diff --git a/src/components/Navbar/index.tsx b/src/components/Navbar/index.tsx index 815185c1..566fefa8 100644 --- a/src/components/Navbar/index.tsx +++ b/src/components/Navbar/index.tsx @@ -1,51 +1,20 @@ -import { - NavLink, - Select, - Modal, - Button, - TextInput, - Group, - Box, - rem, - Stack, - ActionIcon, - ThemeIcon, - Text, -} from '@mantine/core'; -import { - IconReportAnalytics, - IconFileAlert, - IconReload, - IconLogout, - IconUser, - IconBinaryTree2, - IconTableShortcut, - IconSettings, - IconTrash, - IconInfoCircle, - IconUserCog, - IconTimelineEvent, - IconHome, -} from '@tabler/icons-react'; -import { FC, useCallback, useEffect, useState } from 'react'; -import { useLocation, useParams, RouteMatch, matchRoutes } from 'react-router-dom'; -import { notifications } from '@mantine/notifications'; +import { Box, Stack, Text } from '@mantine/core'; +import { IconLogout, IconUser, IconBinaryTree2, IconInfoCircle, IconUserCog, IconHome } from '@tabler/icons-react'; +import { FC, useCallback, useEffect } from 'react'; +import { useLocation, useParams } from 'react-router-dom'; import { useNavigate } from 'react-router-dom'; import { useHeaderContext } from '@/layouts/MainLayout/Context'; -import useMountedState from '@/hooks/useMountedState'; import { useDisclosure } from '@mantine/hooks'; import { HOME_ROUTE, LOGS_ROUTE, USERS_MANAGEMENT_ROUTE } from '@/constants/routes'; import InfoModal from './infoModal'; import { getStreamsSepcificAccess, getUserSepcificStreams } from './rolesHandler'; -import { LogStreamData } from '@/@types/parseable/api/stream'; import Cookies from 'js-cookie'; import { useUser } from '@/hooks/useUser'; import { useLogStream } from '@/hooks/useLogStream'; -import { signOutHandler } from '@/utils'; import styles from './styles/Navbar.module.css'; import useCurrentRoute from '@/hooks/useCurrentRoute'; -import { HEADER_HEIGHT, NAVBAR_WIDTH, PRIMARY_HEADER_HEIGHT } from '@/constants/theme'; -import { heights } from '../Mantine/sizing'; +import { NAVBAR_WIDTH, PRIMARY_HEADER_HEIGHT } from '@/constants/theme'; +import UserModal from './UserModal'; const navItems = [ { @@ -68,93 +37,43 @@ const navItems = [ }, ]; -const generateUserAcccessMap = (accessRoles: string[] | null) => { - return ['hasUserAccess'].reduce((acc, accessRequirement) => { - if (accessRequirement === 'hasUserAccess') { - return { ...acc, [accessRequirement]: accessRoles?.some((access: string) => access === 'ListUser') || false }; - } else { - return { ...acc, [accessRequirement]: false }; - } - }, {}); -}; +const navActions = [ + { + icon: IconUser, + label: 'User', + key: 'user', + }, + { + icon: IconInfoCircle, + label: 'About', + key: 'about', + }, + { + icon: IconLogout, + label: 'Logout', + key: 'logout', + }, +]; const Navbar: FC = () => { const navigate = useNavigate(); const { streamName } = useParams(); const location = useLocation(); const currentRoute = useCurrentRoute(); - const username = Cookies.get('username'); - const { state: { subAppContext, maximized, userSpecficStreams, userSpecificAccessMap }, - methods: { streamChangeCleanup, setUserRoles, setSelectedStream, setUserSpecficStreams, updateUserSpecificAccess }, + methods: { streamChangeCleanup, setUserRoles, setUserSpecficStreams, updateUserSpecificAccess }, } = useHeaderContext(); const selectedStream = subAppContext.get().selectedStream; - const [currentPage, setCurrentPage] = useMountedState('/'); - const [deleteStream, setDeleteStream] = useMountedState(''); + const [userModalOpened, { toggle: toggleUserModal }] = useDisclosure(false); + const [infoModalOpened, { toggle: toggleInfoModal }] = useDisclosure(false); - const [opened, { open, close }] = useDisclosure(false); - - const { deleteLogStreamMutation, getLogStreamListData, getLogStreamListIsError, getLogStreamListRefetch } = - useLogStream(); + const { getLogStreamListData } = useLogStream(); const { getUserRolesData, getUserRolesMutation } = useUser(); - // useEffect(() => { - // if (location.pathname.split('/')[2]) { - // setCurrentPage(`/${location.pathname.split('/')[2]}`); - // } - // if (location.pathname === '/') { - // setSelectedStream(''); - // setCurrentPage('/'); - // updateUserSpecificAccess(getStreamsSepcificAccess(getUserRolesData?.data)); - // } else if (userSpecficStreams && userSpecficStreams.length === 0) { - // setSelectedStream(''); - // navigate('/'); - // } else if (streamName) { - // if (streamName === deleteStream && userSpecficStreams) { - // setDeleteStream(''); - // handleChange(userSpecficStreams[0].name); - // } else if (userSpecficStreams && !userSpecficStreams.find((stream: any) => stream.name === streamName)) { - // notifications.show({ - // id: 'getLogStreamListIsError-data', - // color: 'red', - // title: 'Error occurred', - // message: `${streamName} stream not found`, - // icon: , - // autoClose: 5000, - // }); - // handleChange(userSpecficStreams[0].name); - // } else if (userSpecficStreams?.find((stream: any) => stream.name === streamName)) { - // handleChange(streamName); - // } - // } else if (userSpecficStreams && Boolean(userSpecficStreams.length)) { - // if (location.pathname === USERS_MANAGEMENT_ROUTE) { - // handleChangeWithoutRiderection(userSpecficStreams[0].name, location.pathname); - // navigate('/users'); - // } else { - // handleChange(userSpecficStreams[0].name); - // } - // } - // }, [userSpecficStreams]); - - // const handleChange = (value: string, page: string = currentPage) => { - // const targetPage = page === '/' ? '/logs' : page; - // handleChangeWithoutRiderection(value, targetPage); - // updateUserSpecificAccess(getStreamsSepcificAccess(getUserRolesData?.data, value)); - // if (page !== '/users') { - // // navigate(`/${value}${targetPage}`); - // } - // }; - - // const handleChangeWithoutRiderection = (value: string, page: string = currentPage) => { - // setSelectedStream(value); - // setCurrentPage(page); - // streamChangeCleanup(value); - // }; - useEffect(() => { if (getLogStreamListData?.data && getLogStreamListData?.data.length > 0 && getUserRolesData?.data) { getUserRolesData?.data && setUserRoles(getUserRolesData?.data); // TODO: move user context main context @@ -226,105 +145,26 @@ const Navbar: FC = () => { ); })} - {/* } - className={styles.streamsBtn} - /> */} - {/* - - - - - - ); -}; - -type CombinatorToggleType = { - isOrSelected: boolean; - onCombinatorChange: (combinator: Combinator) => void; -}; - -const CombinatorToggle = (props: CombinatorToggleType) => { - const { onCombinatorChange, isOrSelected } = props; - return ( - - onCombinatorChange('or')}> - OR - - onCombinatorChange('and')}> - AND - - - ); -}; - -const RuleSet = (props: RuleSetProps) => { - const { state: queryBuilderState, methods: queryBuilderMethods } = useQueryFilterContext(); - const { fieldTypeMap, query } = queryBuilderState; - const { addRuleToGroup, updateGroupCombinator, updateParentCombinator } = queryBuilderMethods; - const { ruleSet } = props; - const { combinator: ruleSetCombinator, id, rules } = ruleSet; - - const onCombinatorChange = useCallback((combinator: Combinator) => updateGroupCombinator(id, combinator), []); - const addCondtionBtnHandler = useCallback(() => { - addRuleToGroup(id); - }, []); - - return ( - - - - {rules.map((rule) => { - return ; - })} - - - - - - - - - - - - ); -}; - -const AddRuleGroupBtn = ({ createRuleGroup }: { createRuleGroup: () => void }) => ( - - - - - - - Add - - - -); - -type QueryPillProps = { - query: QueryType; -}; - -type RuleSetPillProps = { - ruleSet: RuleGroupTypeOverride; -}; - -const RuleSetPills = (props: RuleSetPillProps) => { - const { ruleSet } = props; - const { rules, combinator } = ruleSet; - const { - methods: { deleteRuleFromGroup }, - } = useQueryFilterContext(); - - return ( - - {rules.map((rule, index) => { - const shouldShowCombinatorPill = rules.length !== 1 && index + 1 !== rules.length; - const operatorLabel = operatorLabelMap[rule.operator] || rule.operator; - return ( - - deleteRuleFromGroup(ruleSet.id, rule.id)}> - {rule.field} {operatorLabel} {rule.value} - - {shouldShowCombinatorPill && {combinator}} - - ); - })} - - ); -}; - -const QueryPills = (props: QueryPillProps) => { - const { query } = props; - const { combinator, rules: ruleSets } = query; - return ( - - - {ruleSets.map((ruleSet, index) => { - const shouldShowCombinatorPill = ruleSets.length !== 1 && index + 1 !== ruleSets.length; - return ( - - - {shouldShowCombinatorPill && {combinator}} - - ); - })} - - - ); -}; - -const FilterBtnPlaceholder = () => { - return ( - - - Click to add filter - - ); -}; - -const ModalTitle = () => { - return Filters; -}; - -const Querier = () => { - const { state: queryBuilderState, methods: queryBuilderMethods } = useQueryFilterContext(); - const { isModalOpen, query, isSumbitDisabled, appliedQuery } = queryBuilderState; - const { createRuleGroup, clearFilters, applyQuery, closeBuilderModal, openBuilderModal } = queryBuilderMethods; - const { - state: { custQuerySearchState }, - methods: {}, - } = useLogsPageContext(); - - const isFiltersApplied = custQuerySearchState.mode === 'filters' && custQuerySearchState.isQuerySearchActive; - return ( - <> - - {!isFiltersApplied ? : } - - }> - - - - {query.rules.map((ruleSet) => { - return ; - })} - - - - - - - - - - - ); -}; - -export default Querier; diff --git a/src/components/Header/StreamDropdown.tsx b/src/components/Header/StreamDropdown.tsx index e7e51866..6fee99b7 100644 --- a/src/components/Header/StreamDropdown.tsx +++ b/src/components/Header/StreamDropdown.tsx @@ -1,7 +1,6 @@ -import { Select, Text } from '@mantine/core'; -import { useCallback, useEffect, useRef, useState } from 'react'; +import { Select } from '@mantine/core'; +import { useCallback, useEffect, useRef } from 'react'; import classes from './styles/LogQuery.module.css'; -import { IconCaretDown } from '@tabler/icons-react'; import { useHeaderContext } from '@/layouts/MainLayout/Context'; import { useNavigate, useParams } from 'react-router-dom'; diff --git a/src/components/Header/SubHeader.tsx b/src/components/Header/SubHeader.tsx index e17ee134..44887a07 100644 --- a/src/components/Header/SubHeader.tsx +++ b/src/components/Header/SubHeader.tsx @@ -1,4 +1,4 @@ -import { ActionIcon, Box, Button, Group, Menu, Modal, ScrollArea, Select, Stack, Text, ThemeIcon, px } from '@mantine/core'; +import { Box, Group, Menu, Modal, Stack, Text, px } from '@mantine/core'; import { type FC } from 'react'; import HeaderBreadcrumbs from './HeaderBreadcrumbs'; import RefreshInterval from './RefreshInterval'; @@ -8,40 +8,16 @@ import ReloadUser from './ReloadUser'; import DocsUser from './UserDocs'; import StreamingButton from './StreamingButton'; import LiveTailFilter from './LiveTailFilter'; -import { - HEADER_HEIGHT, - LOGS_PRIMARY_TOOLBAR_HEIGHT, - LOGS_SECONDARY_TOOLBAR_HEIGHT, - PRIMARY_HEADER_HEIGHT, -} from '@/constants/theme'; +import { HEADER_HEIGHT, LOGS_SECONDARY_TOOLBAR_HEIGHT } from '@/constants/theme'; import { downloadDataAsCSV, downloadDataAsJson } from '@/utils/exportHelpers'; import { useLogsPageContext } from '@/pages/Logs/context'; import { useHeaderContext } from '@/layouts/MainLayout/Context'; import { ToggleButton } from '../Button/ToggleButton'; -import { - IconAlertCircle, - IconArrowAutofitWidth, - IconArrowsMaximize, - IconArrowsMinimize, - IconBolt, - IconChevronDown, - IconCircleDot, - IconCodeCircle, - IconDownload, - IconExclamationCircle, - IconFileSpreadsheet, - IconFilter, - IconMaximize, - IconSettings, - IconTrash, - IconX, -} from '@tabler/icons-react'; +import { IconChevronDown, IconCodeCircle, IconDownload, IconFilter, IconMaximize } from '@tabler/icons-react'; import styles from './styles/LogQuery.module.css'; import headerStyles from './styles/Header.module.css'; -import Querier from './Querier'; import { useStatsPageContext } from '@/pages/Stats/Context'; import IconButton from '../Button/IconButton'; -import StreamDropdown from './StreamDropdown'; type HeaderLayoutProps = { children: React.ReactNode; @@ -112,56 +88,6 @@ export const LiveTailHeader: FC = () => { const renderExportIcon = () => ; const renderMaximizeIcon = () => ; -const renderAlertsIcon = () => ; -const renderSettingsIcon = () => ; -const renderLiveTailIcon = () => ; -const renderDeleteIcon = () => ; - -type IconButtonProps = { - onClick: () => void; -}; - -const AlertsIcon = (props: IconButtonProps) => { - const classes = styles; - return ( - - - {/* Alerts */} - - ); -}; - -const RetentionIcon = (props: IconButtonProps) => { - const classes = styles; - return ( - - - {/* Retention */} - - ); -}; - -const DeleteIcon = (props: IconButtonProps) => { - const classes = styles; - return ( - - - {/* Delete */} - - ); -}; - -const LiveModeIcon = (props: IconButtonProps) => { - const classes = styles; - return ( - - {/* */} - - {/* */} - {/* Live Tail */} - - ); -}; const FilterPlaceholder = () => { const classes = styles; @@ -170,8 +96,8 @@ const FilterPlaceholder = () => { Click to add filter - ) -} + ); +}; const SQLEditorPlaceholder = () => { const classes = styles; @@ -180,81 +106,42 @@ const SQLEditorPlaceholder = () => { Click to write query - ) -} + ); +}; -const ModalTitle = ({title}: {title: string}) => { +const ModalTitle = ({ title }: { title: string }) => { return {title}; }; const QuerierModal = () => { - const classes = styles; - const { - methods: { - makeExportData, - toggleShowQueryEditor, - openDeleteModal, - openAlertsModal, - openRetentionModal, - toggleLiveTail, - toggleCustQuerySearchMode, - toggleBuilderModal - }, + methods: { toggleBuilderModal }, state: { - custQuerySearchState: { isQuerySearchActive, mode }, - liveTailToggled, - builderModalOpen + custQuerySearchState: { mode }, + builderModalOpen, }, } = useLogsPageContext(); return ( }> - {/* - - - {query.rules.map((ruleSet) => { - return ; - })} - - - - - - - - */} - - ) -} + opened={builderModalOpen} + onClose={toggleBuilderModal} + size="auto" + centered + styles={{ body: { padding: '0 0.5rem' }, header: { padding: '1rem', paddingBottom: '0' } }} + title={}> + ); +}; const getLabel = (mode: string | null) => { return mode === 'filters' ? 'Filters' : mode === 'sql' ? 'SQL' : ''; -} +}; const Chooser = () => { const classes = styles; const { - methods: { - makeExportData, - toggleShowQueryEditor, - openDeleteModal, - openAlertsModal, - openRetentionModal, - toggleLiveTail, - toggleCustQuerySearchMode, - toggleBuilderModal - }, + methods: { toggleCustQuerySearchMode, toggleBuilderModal }, state: { - custQuerySearchState: { isQuerySearchActive, mode }, - liveTailToggled, + custQuerySearchState: { mode }, }, } = useLogsPageContext(); return ( @@ -285,32 +172,24 @@ const Chooser = () => {
- + {mode === 'filters' ? : mode === 'sql' ? : <>} ); -} +}; export const LogsHeader: FC = () => { const classes = styles; - const { container, innerContainer } = classes; const { - methods: { - makeExportData, - toggleShowQueryEditor, - openDeleteModal, - openAlertsModal, - openRetentionModal, - toggleLiveTail, - }, + methods: { makeExportData, toggleShowQueryEditor }, state: { custQuerySearchState: { isQuerySearchActive, mode }, liveTailToggled, }, } = useLogsPageContext(); const { - state: { subLogQuery, maximized, userSpecificAccessMap }, + state: { subLogQuery, maximized }, methods: { resetTimeInterval, toggleMaximize }, } = useHeaderContext(); const exportHandler = (fileType: string | null) => { @@ -327,7 +206,7 @@ export const LogsHeader: FC = () => { return ( - + {!liveTailToggled && ( @@ -366,40 +245,6 @@ export const LogsHeader: FC = () => { )} - - {/* - - - - - } - label="SQL" - /> - - - - -
- -
-
- - exportHandler('CSV')} style={{ padding: '0.5rem 2.25rem 0.5rem 0.75rem' }}> - CSV - - exportHandler('JSON')} style={{ padding: '0.5rem 2.25rem 0.5rem 0.75rem' }}> - JSON - - -
- -
-
-
-
*/}
); }; diff --git a/src/constants/routes.ts b/src/constants/routes.ts index 355b15f4..e23b1e37 100644 --- a/src/constants/routes.ts +++ b/src/constants/routes.ts @@ -18,4 +18,4 @@ export const PATHS = { config: '/:streamName/config', users: '/users', oidcNotConfigured: '/oidc-not-configured' -} \ No newline at end of file +} as {[key: string]: string} \ No newline at end of file diff --git a/src/hooks/useCurrentRoute.ts b/src/hooks/useCurrentRoute.ts index 1e7df1a6..e948cee1 100644 --- a/src/hooks/useCurrentRoute.ts +++ b/src/hooks/useCurrentRoute.ts @@ -8,6 +8,7 @@ const routes = Object.keys(PATHS).map((key: string) => { const useCurrentRoute = () => { const location = useLocation(); + // @ts-ignore const [{ route }] = matchRoutes(routes, location); return route.path; diff --git a/src/layouts/MainLayout/index.tsx b/src/layouts/MainLayout/index.tsx index 403fa208..52596ffb 100644 --- a/src/layouts/MainLayout/index.tsx +++ b/src/layouts/MainLayout/index.tsx @@ -1,6 +1,6 @@ import { PrimaryHeader } from '@/components/Header'; import Navbar from '@/components/Navbar'; -import { HEADER_HEIGHT, NAVBAR_WIDTH, PRIMARY_HEADER_HEIGHT } from '@/constants/theme'; +import { NAVBAR_WIDTH, PRIMARY_HEADER_HEIGHT } from '@/constants/theme'; import { Box } from '@mantine/core'; import type { FC } from 'react'; import { Outlet } from 'react-router-dom'; diff --git a/src/pages/Logs/Context.tsx b/src/pages/Logs/Context.tsx index dd93c19b..fef5373f 100644 --- a/src/pages/Logs/Context.tsx +++ b/src/pages/Logs/Context.tsx @@ -55,6 +55,10 @@ interface LogsPageContextMethods { openRetentionModal: () => void; toggleLiveTail: () => void; closeAlertsModal: () => void; + toggleBuilderModal: () => void; + toggleCustQuerySearchMode: (mode: custQuerySearchMode) => void; + closeBuilderModal: () => void; + closeDeleteModal: () => void; } interface LogsPageContextValue { diff --git a/src/pages/Logs/DeleteStreamModal.tsx b/src/pages/Logs/DeleteStreamModal.tsx index 38b89935..4f6449a6 100644 --- a/src/pages/Logs/DeleteStreamModal.tsx +++ b/src/pages/Logs/DeleteStreamModal.tsx @@ -10,7 +10,7 @@ const DeleteStreamModal = () => { methods: { closeDeleteModal }, } = useLogsPageContext(); const [confirmInputValue, setConfirmInputValue] = useState(''); - const handleInputChange = useCallback((e) => { + const handleInputChange = useCallback((e: React.ChangeEvent) => { setConfirmInputValue(e.target.value); }, []); diff --git a/src/pages/Logs/FilterQueryBuilder.tsx b/src/pages/Logs/FilterQueryBuilder.tsx index 4c451dac..621324eb 100644 --- a/src/pages/Logs/FilterQueryBuilder.tsx +++ b/src/pages/Logs/FilterQueryBuilder.tsx @@ -1,11 +1,8 @@ import { Button, Group, - Menu, - Modal, ScrollArea, Stack, - px, Box, ThemeIcon, Select, @@ -15,8 +12,7 @@ import { ActionIcon, } from '@mantine/core'; import { useLogsPageContext } from './context'; -import { ToggleButton } from '@/components/Button/ToggleButton'; -import { IconChevronDown, IconCodeCircle, IconFilter, IconPlus } from '@tabler/icons-react'; +import { IconFilter, IconPlus } from '@tabler/icons-react'; import classes from './styles/Querier.module.css'; import { Text } from '@mantine/core'; import { useQueryFilterContext, operatorLabelMap } from '@/providers/QueryFilterProvider'; @@ -32,7 +28,7 @@ export const FilterPlaceholder = () => { import { useCallback } from 'react'; import { noValueOperators, textFieldOperators, numberFieldOperators } from '@/providers/QueryFilterProvider'; -import { RuleTypeOverride, RuleGroupTypeOverride, QueryType, Combinator } from '@/providers/QueryFilterProvider'; +import { RuleTypeOverride, RuleGroupTypeOverride, Combinator } from '@/providers/QueryFilterProvider'; type RuleSetProps = { ruleSet: RuleGroupTypeOverride; @@ -205,8 +201,10 @@ const RuleSetPills = (props: RuleSetPillProps) => { ); }; -export const QueryPills = (props: QueryPillProps) => { - const { state: {query}, methods: queryBuilderMethods } = useQueryFilterContext(); +export const QueryPills = () => { + const { + state: { query }, + } = useQueryFilterContext(); const { combinator, rules: ruleSets } = query; return ( @@ -225,42 +223,20 @@ export const QueryPills = (props: QueryPillProps) => { ); }; -const FilterBtnPlaceholder = () => { - return ( - - - Click to add filter - - ); -}; - export const FilterQueryBuilder = () => { const { state: queryBuilderState, methods: queryBuilderMethods } = useQueryFilterContext(); - const { query, isSumbitDisabled } = queryBuilderState; + const { query, isSumbitDisabled } = queryBuilderState; const { createRuleGroup, clearFilters, applyQuery } = queryBuilderMethods; const { - methods: { - makeExportData, - toggleShowQueryEditor, - openDeleteModal, - openAlertsModal, - openRetentionModal, - toggleLiveTail, - toggleCustQuerySearchMode, - toggleBuilderModal, - closeBuilderModal - }, state: { - custQuerySearchState: { isQuerySearchActive, mode, viewMode }, - liveTailToggled, - builderModalOpen, + custQuerySearchState: { isQuerySearchActive, mode }, }, } = useLogsPageContext(); const isFiltersApplied = isQuerySearchActive && mode === 'filters'; return ( - + {query.rules.map((ruleSet) => { return ; @@ -269,7 +245,9 @@ export const FilterQueryBuilder = () => { - + diff --git a/src/pages/Logs/LogTable.tsx b/src/pages/Logs/LogTable.tsx index a6c90d0e..778d9755 100644 --- a/src/pages/Logs/LogTable.tsx +++ b/src/pages/Logs/LogTable.tsx @@ -405,7 +405,6 @@ const LogTable: FC = () => { className={tableStyles.container} style={{ maxHeight: `calc(100vh - ${primaryHeaderHeight}px )`, - // transition: 'width 1s ease-in-out', }}> {!(logStreamError || logStreamSchemaError || logsError) ? ( diff --git a/src/pages/Logs/Querier.tsx b/src/pages/Logs/Querier.tsx index 6e1c0a77..5e6550ff 100644 --- a/src/pages/Logs/Querier.tsx +++ b/src/pages/Logs/Querier.tsx @@ -1,12 +1,10 @@ -import { Button, Group, Menu, Modal, ScrollArea, Stack, px } from '@mantine/core'; +import { Group, Menu, Modal, Stack, px } from '@mantine/core'; import { useLogsPageContext } from './context'; import { ToggleButton } from '@/components/Button/ToggleButton'; import { IconChevronDown, IconCodeCircle, IconFilter } from '@tabler/icons-react'; import classes from './styles/Querier.module.css'; import { Text } from '@mantine/core'; -import { useQueryFilterContext } from '@/providers/QueryFilterProvider'; import { FilterQueryBuilder, QueryPills } from './FilterQueryBuilder'; -import { useCallback } from 'react'; import { QueryEditor, AppliedSQLQuery } from './QueryEditor'; const getLabel = (mode: string | null) => { @@ -37,42 +35,13 @@ const ModalTitle = ({ title }: { title: string }) => { const QuerierModal = () => { const { - methods: { - makeExportData, - toggleShowQueryEditor, - openDeleteModal, - openAlertsModal, - openRetentionModal, - toggleLiveTail, - toggleCustQuerySearchMode, - toggleBuilderModal, - closeBuilderModal - }, + methods: { toggleBuilderModal }, state: { - custQuerySearchState: { isQuerySearchActive, mode, viewMode }, - liveTailToggled, + custQuerySearchState: { viewMode }, builderModalOpen, }, } = useLogsPageContext(); - const { state: queryFilterState, methods: queryFilterMethods } = useQueryFilterContext(); - const { query, isSumbitDisabled, appliedQuery } = queryFilterState; - const { createRuleGroup, clearFilters, applyQuery } = queryFilterMethods; - - const clearContextHandler = mode === 'filters' ? clearFilters : mode === 'sql' ? () => {} : () => {}; - const applyContextHandler = mode === 'filters' ? applyQuery : mode === 'sql' ? () => {} : () => {}; - const disableSubmit = mode === 'filters' ? isSumbitDisabled : mode === 'sql' ? true : true; - - const handleClear = useCallback(() => { - clearContextHandler(); - closeBuilderModal(); - }, []) - - const handleApply = useCallback(() => { - applyContextHandler(); - closeBuilderModal(); - }, []) - return ( { styles={{ body: { padding: '0 0.5rem' }, header: { padding: '1rem', paddingBottom: '0' } }} title={}> - {/* */} - {viewMode === 'filters' ? : } - {/* */} + {viewMode === 'filters' ? : } ); @@ -92,19 +59,9 @@ const QuerierModal = () => { const Querier = () => { const { - methods: { - makeExportData, - toggleShowQueryEditor, - openDeleteModal, - openAlertsModal, - openRetentionModal, - toggleLiveTail, - toggleCustQuerySearchMode, - toggleBuilderModal, - }, + methods: { toggleCustQuerySearchMode, toggleBuilderModal }, state: { custQuerySearchState: { isQuerySearchActive, mode, viewMode }, - liveTailToggled, }, } = useLogsPageContext(); const isFiltersApplied = mode === 'filters' && isQuerySearchActive; @@ -139,12 +96,8 @@ const Querier = () => { - { viewMode === 'filters' && - (isFiltersApplied ? : ) - } - { viewMode === 'sql' && - (isSqlSearchActive ? : ) - } + {viewMode === 'filters' && (isFiltersApplied ? : )} + {viewMode === 'sql' && (isSqlSearchActive ? : )} ); diff --git a/src/pages/Logs/QueryCodeEditor.tsx b/src/pages/Logs/QueryCodeEditor.tsx index 6ea67a65..f206b298 100644 --- a/src/pages/Logs/QueryCodeEditor.tsx +++ b/src/pages/Logs/QueryCodeEditor.tsx @@ -1,7 +1,7 @@ import React, { FC, MutableRefObject, useCallback, useEffect } from 'react'; import Editor from '@monaco-editor/react'; import { useHeaderContext } from '@/layouts/MainLayout/Context'; -import { Box, Button, Flex, ScrollArea, Stack, Text, TextInput, Tooltip, px } from '@mantine/core'; +import { Box, Button, Flex, ScrollArea, Stack, Text, TextInput } from '@mantine/core'; import { ErrorMarker, errChecker } from './ErrorMarker'; import useMountedState from '@/hooks/useMountedState'; import { notify } from '@/utils/notification'; diff --git a/src/providers/QueryFilterProvider.tsx b/src/providers/QueryFilterProvider.tsx index 5ca94eee..33b87200 100644 --- a/src/providers/QueryFilterProvider.tsx +++ b/src/providers/QueryFilterProvider.tsx @@ -36,7 +36,6 @@ type QueryFilterContextMethods = { applyQuery: () => void; clearFilters: () => void; closeBuilderModal: () => void; - openBuilderModal: () => void; }; type QueryFilterContextValue = { diff --git a/src/routes/elements.tsx b/src/routes/elements.tsx index 258bd3e7..3110c478 100644 --- a/src/routes/elements.tsx +++ b/src/routes/elements.tsx @@ -8,7 +8,6 @@ import { ConfigHeader, HomeHeader, LiveTailHeader, - LogsHeader, StatsHeader, UsersManagementHeader, } from '@/components/Header/SubHeader'; From 8725e9edad77aecb1c347a3ee3424a52f5bfb5ca Mon Sep 17 00:00:00 2001 From: balaji-jr Date: Tue, 27 Feb 2024 14:40:38 +0530 Subject: [PATCH 05/17] more ts error fixes --- src/components/Navbar/UserModal.tsx | 28 ++++++++++++++-------------- src/pages/Logs/LogTable.tsx | 2 +- src/pages/Logs/RetentionModal.tsx | 7 ++++--- src/pages/Logs/index.tsx | 5 ++++- 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/components/Navbar/UserModal.tsx b/src/components/Navbar/UserModal.tsx index bb82e489..464e9f61 100644 --- a/src/components/Navbar/UserModal.tsx +++ b/src/components/Navbar/UserModal.tsx @@ -1,16 +1,16 @@ -import { Box, Button, Group, Modal, Stack, TextInput } from '@mantine/core'; +import { Box, Modal, Stack } from '@mantine/core'; import { Text } from '@mantine/core'; -import { Editor } from '@monaco-editor/react'; -import { useAlertsEditor } from '@/hooks/useAlertsEditor'; +// import { Editor } from '@monaco-editor/react'; +// import { useAlertsEditor } from '@/hooks/useAlertsEditor'; const ModalTitle = () => { return User; }; -type UserInfoProps = { - username: string; - previlage: string; -} +// type UserInfoProps = { +// username: string; +// previlage: string; +// } type UserModalProps = { opened: boolean; @@ -18,13 +18,13 @@ type UserModalProps = { userData: {[key: string]: string} } -const UserInfo = ({username, previlage}) => { - return ( - - - - ) -} +// const UserInfo = ({username, previlage}) => { +// return ( +// +// +// +// ) +// } const UserModal = (props: UserModalProps) => { return ( diff --git a/src/pages/Logs/LogTable.tsx b/src/pages/Logs/LogTable.tsx index 778d9755..4041929b 100644 --- a/src/pages/Logs/LogTable.tsx +++ b/src/pages/Logs/LogTable.tsx @@ -399,7 +399,7 @@ const LogTable: FC = () => { : 0; const totalCount = Array.isArray(fetchQueryMutation?.data) ? fetchQueryMutation.data[0]?.count : null; - const loadedCount = Array.isArray(pageLogData?.data) ? pageLogData.data.length : null; + const loadedCount = pageLogData?.data.length || null return ( { return Settings; @@ -13,14 +12,16 @@ type RetentionModalProps = { data: any; handleChange: (value: string | undefined) => void; handleSubmit: () => void; + handleCacheToggle: () => void; + isCacheEnabled: boolean; }; const RententionModal = (props: RetentionModalProps) => { const { - state: { retentionModalOpen, currentStream }, + state: { retentionModalOpen }, methods: { closeRetentionModal }, } = useLogsPageContext(); - const { handleCacheToggle, isCacheEnabled } = useCacheToggle(currentStream); + const { isCacheEnabled, handleCacheToggle } = props; const switchStyles = { track: isCacheEnabled ? classes.trackStyle : {}, }; diff --git a/src/pages/Logs/index.tsx b/src/pages/Logs/index.tsx index 5592003d..6184e826 100644 --- a/src/pages/Logs/index.tsx +++ b/src/pages/Logs/index.tsx @@ -13,6 +13,7 @@ import SecondaryToolbar from './SecondaryToolbar'; import { useHeaderContext } from '@/layouts/MainLayout/Context'; import { useAlertsEditor } from '@/hooks/useAlertsEditor'; import { useRetentionEditor } from '@/hooks/useRetentionEditor'; +import { useCacheToggle } from '@/hooks/useCacheToggle'; const Logs: FC = () => { useDocumentTitle('Parseable | Logs'); @@ -25,16 +26,18 @@ const Logs: FC = () => { const { handleAlertQueryChange, submitAlertQuery, getLogAlertData } = useAlertsEditor(currentStream); const { handleRetentionQueryChange, submitRetentionQuery, getLogRetentionData } = useRetentionEditor(currentStream); + const { handleCacheToggle, isCacheEnabled } = useCacheToggle(currentStream); return ( - {/* */} {!maximized && ( <> From 37de6d140664faa5bf61ea055a47359cd874a4f2 Mon Sep 17 00:00:00 2001 From: balaji-jr Date: Tue, 27 Feb 2024 18:05:16 +0530 Subject: [PATCH 06/17] minor feedbacks --- src/components/Header/SubHeader.tsx | 177 +----------------- src/pages/LiveTail/LogTable.tsx | 19 +- src/pages/Logs/Context.tsx | 19 +- src/pages/Logs/EventTimeLineGraph.tsx | 52 +++-- src/pages/Logs/FilterQueryBuilder.tsx | 4 +- src/pages/Logs/Querier.tsx | 5 +- src/pages/Logs/QueryCodeEditor.tsx | 23 +-- src/pages/Logs/QueryEditor.tsx | 24 +-- src/pages/Logs/SQLQueryBuilder.tsx | 0 .../Logs/styles/EventTimeLineGraph.module.css | 14 ++ src/routes/elements.tsx | 1 - 11 files changed, 97 insertions(+), 241 deletions(-) delete mode 100644 src/pages/Logs/SQLQueryBuilder.tsx diff --git a/src/components/Header/SubHeader.tsx b/src/components/Header/SubHeader.tsx index 44887a07..e8dd7e2e 100644 --- a/src/components/Header/SubHeader.tsx +++ b/src/components/Header/SubHeader.tsx @@ -1,23 +1,15 @@ -import { Box, Group, Menu, Modal, Stack, Text, px } from '@mantine/core'; +import { Box } from '@mantine/core'; import { type FC } from 'react'; import HeaderBreadcrumbs from './HeaderBreadcrumbs'; -import RefreshInterval from './RefreshInterval'; import RefreshNow from './RefreshNow'; -import TimeRange from './TimeRange'; import ReloadUser from './ReloadUser'; import DocsUser from './UserDocs'; import StreamingButton from './StreamingButton'; import LiveTailFilter from './LiveTailFilter'; -import { HEADER_HEIGHT, LOGS_SECONDARY_TOOLBAR_HEIGHT } from '@/constants/theme'; -import { downloadDataAsCSV, downloadDataAsJson } from '@/utils/exportHelpers'; -import { useLogsPageContext } from '@/pages/Logs/context'; -import { useHeaderContext } from '@/layouts/MainLayout/Context'; -import { ToggleButton } from '../Button/ToggleButton'; -import { IconChevronDown, IconCodeCircle, IconDownload, IconFilter, IconMaximize } from '@tabler/icons-react'; +import { HEADER_HEIGHT } from '@/constants/theme'; import styles from './styles/LogQuery.module.css'; import headerStyles from './styles/Header.module.css'; import { useStatsPageContext } from '@/pages/Stats/Context'; -import IconButton from '../Button/IconButton'; type HeaderLayoutProps = { children: React.ReactNode; @@ -77,8 +69,6 @@ export const LiveTailHeader: FC = () => { - {/* */} - {/* */} @@ -86,169 +76,6 @@ export const LiveTailHeader: FC = () => { ); }; -const renderExportIcon = () => ; -const renderMaximizeIcon = () => ; - -const FilterPlaceholder = () => { - const classes = styles; - return ( - - - Click to add filter - - ); -}; - -const SQLEditorPlaceholder = () => { - const classes = styles; - return ( - - - Click to write query - - ); -}; - -const ModalTitle = ({ title }: { title: string }) => { - return {title}; -}; - -const QuerierModal = () => { - const { - methods: { toggleBuilderModal }, - state: { - custQuerySearchState: { mode }, - builderModalOpen, - }, - } = useLogsPageContext(); - return ( - }> - ); -}; - -const getLabel = (mode: string | null) => { - return mode === 'filters' ? 'Filters' : mode === 'sql' ? 'SQL' : ''; -}; - -const Chooser = () => { - const classes = styles; - const { - methods: { toggleCustQuerySearchMode, toggleBuilderModal }, - state: { - custQuerySearchState: { mode }, - }, - } = useLogsPageContext(); - return ( - - - -
- {}} - toggled={false} - renderIcon={() => } - label={getLabel(mode)} - iconPosition="right" - customClassName={classes.modeButton} - /> -
-
- - toggleCustQuerySearchMode('filters')} - style={{ padding: '0.5rem 2.25rem 0.5rem 0.75rem' }}> - Filters - - toggleCustQuerySearchMode('sql')} - style={{ padding: '0.5rem 2.25rem 0.5rem 0.75rem' }}> - SQL - - -
- - {mode === 'filters' ? : mode === 'sql' ? : <>} - -
- ); -}; - -export const LogsHeader: FC = () => { - const classes = styles; - const { - methods: { makeExportData, toggleShowQueryEditor }, - state: { - custQuerySearchState: { isQuerySearchActive, mode }, - liveTailToggled, - }, - } = useLogsPageContext(); - const { - state: { subLogQuery, maximized }, - methods: { resetTimeInterval, toggleMaximize }, - } = useHeaderContext(); - const exportHandler = (fileType: string | null) => { - const query = subLogQuery.get(); - const filename = `${query.streamName}-logs`; - if (fileType === 'CSV') { - downloadDataAsCSV(makeExportData('CSV'), filename); - } else if (fileType === 'JSON') { - downloadDataAsJson(makeExportData('JSON'), filename); - } - }; - - if (maximized) return null; - - return ( - - - - {!liveTailToggled && ( - - - } - label="SQL" - /> - - - - -
- -
-
- - exportHandler('CSV')} style={{ padding: '0.5rem 2.25rem 0.5rem 0.75rem' }}> - CSV - - exportHandler('JSON')} style={{ padding: '0.5rem 2.25rem 0.5rem 0.75rem' }}> - JSON - - -
- - -
- )} - {liveTailToggled && ( - - - - - )} -
-
- ); -}; - export const HomeHeader: FC = () => { const classes = styles; const { container, innerContainer } = classes; diff --git a/src/pages/LiveTail/LogTable.tsx b/src/pages/LiveTail/LogTable.tsx index 698f39aa..4a5084c2 100644 --- a/src/pages/LiveTail/LogTable.tsx +++ b/src/pages/LiveTail/LogTable.tsx @@ -8,11 +8,12 @@ import { useHeaderContext } from '@/layouts/MainLayout/Context'; import { useDoGetLiveTail } from '@/hooks/useDoGetLiveTail'; import EmptyBox from '@/components/Empty'; import styles from './styles/Logs.module.css'; +import { LOGS_PRIMARY_TOOLBAR_HEIGHT, LOGS_SECONDARY_TOOLBAR_HEIGHT, PRIMARY_HEADER_HEIGHT } from '@/constants/theme'; const LogTable: FC = () => { const { finalData: data, doGetLiveTail, resetData, abort, loading, schema } = useDoGetLiveTail(); const { - state: { subInstanceConfig, subLogQuery, subLiveTailsData }, + state: { subInstanceConfig, subLogQuery, subLiveTailsData, maximized }, } = useHeaderContext(); const [grpcPort, setGrpcPort] = useMountedState(subInstanceConfig.get()?.grpcPort ?? null); @@ -88,10 +89,20 @@ const LogTable: FC = () => { const { container, tableStyle, theadStyle, tableContainer, innerContainer } = classes; + const primaryHeaderHeight = !maximized + ? PRIMARY_HEADER_HEIGHT + LOGS_PRIMARY_TOOLBAR_HEIGHT + LOGS_SECONDARY_TOOLBAR_HEIGHT + : 0; + return ( - - - + + + {data.length > 0 ? ( ({ diff --git a/src/pages/Logs/Context.tsx b/src/pages/Logs/Context.tsx index fef5373f..6ed456b6 100644 --- a/src/pages/Logs/Context.tsx +++ b/src/pages/Logs/Context.tsx @@ -1,7 +1,7 @@ import type { Log } from '@/@types/parseable/api/query'; import useSubscribeState, { SubData } from '@/hooks/useSubscribeState'; -import type { Dispatch, FC, SetStateAction } from 'react'; -import { ReactNode, createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; +import type { Dispatch, FC, MutableRefObject, SetStateAction } from 'react'; +import React, { ReactNode, createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; import { LogStreamSchemaData } from '@/@types/parseable/api/stream'; import { sanitizeCSVData } from '@/utils/exportHelpers'; import { useHeaderContext } from '@/layouts/MainLayout/Context'; @@ -35,6 +35,7 @@ interface LogsPageContextState { maximized: boolean; liveTailToggled: boolean; builderModalOpen: boolean; + queryCodeEditorRef: MutableRefObject; } type LogQueryData = { @@ -90,6 +91,14 @@ const defaultCustQuerySearchState = { viewMode: 'filters', }; +const defaultCustSQLQuery = (streamName: string) => { + if (streamName && streamName.length > 0) { + return `SELECT * FROM ${streamName} LIMIT ${LOAD_LIMIT};` + } else { + return '' + } +} + const LogsPageProvider: FC = ({ children }) => { const { state: { subLogQuery }, @@ -113,12 +122,17 @@ const LogsPageProvider: FC = ({ children }) => { const [maximized, { toggle: toggleMaximize }] = useDisclosure(false); const [liveTailToggled, { toggle: toggleLiveTail }] = useDisclosure(false); const [builderModalOpen, { toggle: toggleBuilderModal, close: closeBuilderModal }] = useDisclosure(false); + const queryCodeEditorRef = React.useRef(defaultCustSQLQuery(subLogQuery.get().streamName)); // to store input value even after the editor unmounts // TODO: rm this after context refactor useEffect(() => { const streamlistener = subLogQuery.subscribe((state) => { if (state.streamName) { setCurrentStream(state.streamName); + const defaultSearchQuery = `SELECT * FROM ${state.streamName} LIMIT ${LOAD_LIMIT};`; + queryCodeEditorRef.current = defaultSearchQuery; + } else { + queryCodeEditorRef.current = ''; } }); @@ -144,6 +158,7 @@ const LogsPageProvider: FC = ({ children }) => { maximized, liveTailToggled, builderModalOpen, + queryCodeEditorRef }; // getters & setters diff --git a/src/pages/Logs/EventTimeLineGraph.tsx b/src/pages/Logs/EventTimeLineGraph.tsx index 46789ba0..27fc93c0 100644 --- a/src/pages/Logs/EventTimeLineGraph.tsx +++ b/src/pages/Logs/EventTimeLineGraph.tsx @@ -1,16 +1,26 @@ -import { Skeleton, Stack } from '@mantine/core'; +import { Skeleton, Stack, Text } from '@mantine/core'; import classes from './styles/EventTimeLineGraph.module.css'; import { useQueryResult } from '@/hooks/useQueryResult'; import { useEffect } from 'react'; import { useLogsPageContext } from './context'; import dayjs from 'dayjs'; -import {AreaChart} from '@mantine/charts' +import { AreaChart } from '@mantine/charts'; import { HumanizeNumber } from '@/utils/formatBytes'; const generateCountQuery = (streamName: string, startTime: string, endTime: string) => { return `SELECT DATE_TRUNC('minute', p_timestamp) AS minute_range, COUNT(*) AS log_count FROM ${streamName} WHERE p_timestamp BETWEEN '${startTime}' AND '${endTime}' GROUP BY minute_range ORDER BY minute_range`; }; +const NoDataView = () => { + return ( + + + No new events in the last 10 minutes. + + + ); +}; + const EventTimeLineGraph = () => { const { fetchQueryMutation } = useQueryResult(); const { @@ -28,16 +38,16 @@ const EventTimeLineGraph = () => { endTime: endTime.toDate(), access: [], }; - const query = generateCountQuery(currentStream, startTime.toISOString() , endTime.toISOString()) + const query = generateCountQuery(currentStream, startTime.toISOString(), endTime.toISOString()); fetchQueryMutation.mutate({ logsQuery, query, }); }, [currentStream]); - const graphData = fetchQueryMutation?.data - const isLoading = fetchQueryMutation.isLoading; - const hasData = Array.isArray(graphData) && graphData.length !== 0 + const graphData = fetchQueryMutation?.data; + const isLoading = fetchQueryMutation.isLoading; + const hasData = Array.isArray(graphData) && graphData.length !== 0; return ( @@ -46,19 +56,23 @@ const EventTimeLineGraph = () => { h="100%" w={isLoading ? '98%' : '100%'} style={isLoading ? { marginLeft: '1.8rem', alignSelf: 'center' } : !hasData ? { marginLeft: '1rem' } : {}}> - new Intl.NumberFormat('en-US').format(value)} - withXAxis={false} - withYAxis={hasData} - curveType="linear" - yAxisProps={{ tickCount: 2, tickFormatter: (value) => `${HumanizeNumber(value)}` }} - gridAxis="xy" - withGradient={false} - /> + {hasData ? ( + new Intl.NumberFormat('en-US').format(value)} + withXAxis={false} + withYAxis={hasData} + curveType="linear" + yAxisProps={{ tickCount: 2, tickFormatter: (value) => `${HumanizeNumber(value)}` }} + gridAxis="xy" + withGradient={false} + /> + ) : ( + + )} ); diff --git a/src/pages/Logs/FilterQueryBuilder.tsx b/src/pages/Logs/FilterQueryBuilder.tsx index 621324eb..9a748a0c 100644 --- a/src/pages/Logs/FilterQueryBuilder.tsx +++ b/src/pages/Logs/FilterQueryBuilder.tsx @@ -203,9 +203,9 @@ const RuleSetPills = (props: RuleSetPillProps) => { export const QueryPills = () => { const { - state: { query }, + state: { appliedQuery }, } = useQueryFilterContext(); - const { combinator, rules: ruleSets } = query; + const { combinator, rules: ruleSets } = appliedQuery; return ( diff --git a/src/pages/Logs/Querier.tsx b/src/pages/Logs/Querier.tsx index 5e6550ff..46c28db1 100644 --- a/src/pages/Logs/Querier.tsx +++ b/src/pages/Logs/Querier.tsx @@ -5,7 +5,8 @@ import { IconChevronDown, IconCodeCircle, IconFilter } from '@tabler/icons-react import classes from './styles/Querier.module.css'; import { Text } from '@mantine/core'; import { FilterQueryBuilder, QueryPills } from './FilterQueryBuilder'; -import { QueryEditor, AppliedSQLQuery } from './QueryEditor'; +import { AppliedSQLQuery } from './QueryEditor'; +import QueryCodeEditor from './QueryCodeEditor'; const getLabel = (mode: string | null) => { return mode === 'filters' ? 'Filters' : mode === 'sql' ? 'SQL' : ''; @@ -51,7 +52,7 @@ const QuerierModal = () => { styles={{ body: { padding: '0 0.5rem' }, header: { padding: '1rem', paddingBottom: '0' } }} title={}> - {viewMode === 'filters' ? : } + {viewMode === 'filters' ? : } ); diff --git a/src/pages/Logs/QueryCodeEditor.tsx b/src/pages/Logs/QueryCodeEditor.tsx index f206b298..565eade4 100644 --- a/src/pages/Logs/QueryCodeEditor.tsx +++ b/src/pages/Logs/QueryCodeEditor.tsx @@ -1,4 +1,4 @@ -import React, { FC, MutableRefObject, useCallback, useEffect } from 'react'; +import React, { FC, useCallback, useEffect } from 'react'; import Editor from '@monaco-editor/react'; import { useHeaderContext } from '@/layouts/MainLayout/Context'; import { Box, Button, Flex, ScrollArea, Stack, Text, TextInput } from '@mantine/core'; @@ -11,10 +11,6 @@ import { LOAD_LIMIT, useLogsPageContext } from './context'; import { Field } from '@/@types/parseable/dataType'; import queryCodeStyles from './styles/QueryCode.module.css'; -type QueryCodeEditorProps = { - inputRef: MutableRefObject; -}; - const genColumnConfig = (fields: Field[]) => { const columnConfig = { leftColumns: [], rightColumns: [] }; if (fields.length === 0) return columnConfig; @@ -32,7 +28,7 @@ const genColumnConfig = (fields: Field[]) => { }, columnConfig); }; -const QueryCodeEditor: FC = (props) => { +const QueryCodeEditor: FC = () => { const { state: { subLogQuery, subInstanceConfig }, } = useHeaderContext(); @@ -40,6 +36,7 @@ const QueryCodeEditor: FC = (props) => { state: { custQuerySearchState: { isQuerySearchActive, mode }, subLogStreamSchema, + queryCodeEditorRef, }, methods: { resetQuerySearch, setCustSearchQuery, closeBuilderModal }, } = useLogsPageContext(); @@ -54,10 +51,10 @@ const QueryCodeEditor: FC = (props) => { const { data: resAIQuery, postLLMQuery } = usePostLLM(); const currentStreamName = subLogQuery.get().streamName; const isLlmActive = !!subInstanceConfig.get()?.llmActive; - const isSqlSearchActive = isQuerySearchActive && mode === 'sql' + const isSqlSearchActive = isQuerySearchActive && mode === 'sql'; const updateQuery = useCallback((query: string) => { - props.inputRef.current = query; + queryCodeEditorRef.current = query; setQuery(query); }, []); @@ -93,14 +90,14 @@ const QueryCodeEditor: FC = (props) => { }, [currentStreamName, isLlmActive]); useEffect(() => { - updateQuery(props.inputRef.current); + updateQuery(queryCodeEditorRef.current); }, []); function handleEditorDidMount(editor: any, monaco: any) { editorRef.current = editor; monacoRef.current = monaco; editor.addCommand(monaco.KeyMod.CtrlCmd + monaco.KeyCode.Enter, async () => { - runQuery(props.inputRef.current); + runQuery(queryCodeEditorRef.current); }); } @@ -159,10 +156,10 @@ const QueryCodeEditor: FC = (props) => { - - +
); diff --git a/src/pages/Logs/QueryEditor.tsx b/src/pages/Logs/QueryEditor.tsx index 111f2b49..4f5e8766 100644 --- a/src/pages/Logs/QueryEditor.tsx +++ b/src/pages/Logs/QueryEditor.tsx @@ -1,28 +1,6 @@ -import type { FC } from 'react'; -import { LOAD_LIMIT, useLogsPageContext } from './context'; -import React, { useEffect } from 'react'; -import { useHeaderContext } from '@/layouts/MainLayout/Context'; -import QueryCodeEditor from './QueryCodeEditor'; +import { useLogsPageContext } from './context'; import { CodeHighlight } from '@mantine/code-highlight'; -export const QueryEditor: FC = () => { - const { - state: { subLogQuery }, - } = useHeaderContext(); - const inputRef = React.useRef(); // to store input value even after the editor unmounts - const currentStreamName = subLogQuery.get().streamName; - useEffect(() => { - if (currentStreamName) { - const defaultSearchQuery = `SELECT * FROM ${currentStreamName} LIMIT ${LOAD_LIMIT};`; - inputRef.current = defaultSearchQuery; - } else { - inputRef.current = ''; - } - }, [currentStreamName]); - - return ; -}; - export const AppliedSQLQuery = () => { const { state: { diff --git a/src/pages/Logs/SQLQueryBuilder.tsx b/src/pages/Logs/SQLQueryBuilder.tsx deleted file mode 100644 index e69de29b..00000000 diff --git a/src/pages/Logs/styles/EventTimeLineGraph.module.css b/src/pages/Logs/styles/EventTimeLineGraph.module.css index 498bfa25..1b826222 100644 --- a/src/pages/Logs/styles/EventTimeLineGraph.module.css +++ b/src/pages/Logs/styles/EventTimeLineGraph.module.css @@ -2,4 +2,18 @@ width: 100%; height: 100%; margin-left: -2.8rem; +} + +.noDataContainer { + width: 98%; + height: 100%; + align-items: center; + justify-content: center; + border: 1px dashed var(--mantine-color-gray-4); +} + +.noDataText { + text-align: center; + color: var(--mantine-color-gray-6); + font-size: var(--mantine-font-size-sm); } \ No newline at end of file diff --git a/src/routes/elements.tsx b/src/routes/elements.tsx index 3110c478..33d3df37 100644 --- a/src/routes/elements.tsx +++ b/src/routes/elements.tsx @@ -45,7 +45,6 @@ export const LogsElement: FC = () => { - {/* */} From efbd2b7d7a361955b874a2fb06468febc0e1d248 Mon Sep 17 00:00:00 2001 From: balaji-jr Date: Tue, 27 Feb 2024 18:07:21 +0530 Subject: [PATCH 07/17] fix table headers - show all columns when filter is active --- src/pages/Logs/LogTable.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pages/Logs/LogTable.tsx b/src/pages/Logs/LogTable.tsx index 4041929b..737e467c 100644 --- a/src/pages/Logs/LogTable.tsx +++ b/src/pages/Logs/LogTable.tsx @@ -52,11 +52,10 @@ const makeHeadersFromSchema = (schema: LogStreamSchemaData | null): string[] => const makeHeadersfromData = (schema: LogStreamSchemaData | null, custSearchQuery: string | null): string[] => { const allColumns = makeHeadersFromSchema(schema); - if (custSearchQuery === null) return allColumns; const selectClause = custSearchQuery.match(/SELECT(.*?)FROM/i)?.[1]; - if (!selectClause) return allColumns; + if (!selectClause || selectClause.includes('*')) return allColumns; const commonColumns = allColumns.filter((column) => selectClause.includes(column)); return commonColumns; @@ -399,7 +398,7 @@ const LogTable: FC = () => { : 0; const totalCount = Array.isArray(fetchQueryMutation?.data) ? fetchQueryMutation.data[0]?.count : null; - const loadedCount = pageLogData?.data.length || null + const loadedCount = pageLogData?.data.length || null; return ( Date: Tue, 27 Feb 2024 19:25:28 +0530 Subject: [PATCH 08/17] user info modal --- src/components/Navbar/UserModal.tsx | 46 ++++++++++++++++------------- src/components/Navbar/index.tsx | 12 ++++++-- 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/src/components/Navbar/UserModal.tsx b/src/components/Navbar/UserModal.tsx index 464e9f61..40d1b3a4 100644 --- a/src/components/Navbar/UserModal.tsx +++ b/src/components/Navbar/UserModal.tsx @@ -1,32 +1,25 @@ -import { Box, Modal, Stack } from '@mantine/core'; +import { useHeaderContext } from '@/layouts/MainLayout/Context'; +import { Modal, Stack } from '@mantine/core'; import { Text } from '@mantine/core'; -// import { Editor } from '@monaco-editor/react'; -// import { useAlertsEditor } from '@/hooks/useAlertsEditor'; +import Cookies from 'js-cookie'; const ModalTitle = () => { - return User; + return User Details; }; -// type UserInfoProps = { -// username: string; -// previlage: string; -// } - type UserModalProps = { opened: boolean; onClose: () => void; userData: {[key: string]: string} } -// const UserInfo = ({username, previlage}) => { -// return ( -// -// -// -// ) -// } - const UserModal = (props: UserModalProps) => { + const { + state: { subAppContext }, + } = useHeaderContext(); + const username = Cookies.get('username'); + + const userRoles = subAppContext.get().userRoles || {}; return ( { centered styles={{ body: { padding: '0 0.5rem' }, header: { padding: '1rem', paddingBottom: '0' } }} title={}> - - - - + + + Username: + {username} + + + Roles: + {Object.entries(userRoles).map(([key, value], index) => { + return ( + + {index + 1}. {key} ({value[0].privilege}) + + ); + })} + ); diff --git a/src/components/Navbar/index.tsx b/src/components/Navbar/index.tsx index 566fefa8..d8f6606f 100644 --- a/src/components/Navbar/index.tsx +++ b/src/components/Navbar/index.tsx @@ -15,6 +15,7 @@ import styles from './styles/Navbar.module.css'; import useCurrentRoute from '@/hooks/useCurrentRoute'; import { NAVBAR_WIDTH, PRIMARY_HEADER_HEIGHT } from '@/constants/theme'; import UserModal from './UserModal'; +import { signOutHandler } from '@/utils'; const navItems = [ { @@ -149,7 +150,14 @@ const Navbar: FC = () => { {navActions.map((navAction, index) => { const isActiveItem = false; - const onClick = navAction.key === 'about' ? toggleInfoModal : 'user' ? toggleUserModal : () => {}; + const onClick = + navAction.key === 'about' + ? toggleInfoModal + : navAction.key === 'user' + ? toggleUserModal + : navAction.key === 'logout' + ? signOutHandler + : () => {}; return ( { style={{ border: 'none', padding: '8px 0px' }} key={index}> - {navAction.label} + {navAction.key === "user" ? username : navAction.label} ); })} From 43c147a3749de78386724dae44c4011de3c4523a Mon Sep 17 00:00:00 2001 From: balaji-jr Date: Wed, 28 Feb 2024 10:20:23 +0530 Subject: [PATCH 09/17] file rename --- src/hooks/useCurrentRoute.ts | 9 ++++++--- src/hooks/useQueryLogs.ts | 2 +- src/pages/Logs/AlertsModal.tsx | 2 +- src/pages/Logs/CarouselSlide.tsx | 2 +- src/pages/Logs/DeleteStreamModal.tsx | 2 +- src/pages/Logs/EventTimeLineGraph.tsx | 2 +- src/pages/Logs/FilterQueryBuilder.tsx | 2 +- src/pages/Logs/HeaderPagination.tsx | 2 +- src/pages/Logs/LogRow.tsx | 2 +- src/pages/Logs/LogTable.tsx | 2 +- src/pages/Logs/PrimaryToolbar.tsx | 2 +- src/pages/Logs/Querier.tsx | 2 +- src/pages/Logs/QueryCodeEditor.tsx | 2 +- src/pages/Logs/QueryEditor.tsx | 2 +- src/pages/Logs/RetentionModal.tsx | 2 +- src/pages/Logs/SecondaryToolbar.tsx | 2 +- src/pages/Logs/ViewLog.tsx | 2 +- src/pages/Logs/index.tsx | 2 +- src/pages/Logs/{Context.tsx => logsContextProvider.tsx} | 0 src/providers/QueryFilterProvider.tsx | 2 +- src/routes/elements.tsx | 2 +- src/utils/sanitiseSqlString.ts | 2 +- 22 files changed, 26 insertions(+), 23 deletions(-) rename src/pages/Logs/{Context.tsx => logsContextProvider.tsx} (100%) diff --git a/src/hooks/useCurrentRoute.ts b/src/hooks/useCurrentRoute.ts index e948cee1..bc2bfe8d 100644 --- a/src/hooks/useCurrentRoute.ts +++ b/src/hooks/useCurrentRoute.ts @@ -8,10 +8,13 @@ const routes = Object.keys(PATHS).map((key: string) => { const useCurrentRoute = () => { const location = useLocation(); - // @ts-ignore - const [{ route }] = matchRoutes(routes, location); + const match = matchRoutes(routes, location); - return route.path; + if (!match) { + return ''; + } else { + return match[0].route.path; + } }; export default useCurrentRoute; \ No newline at end of file diff --git a/src/hooks/useQueryLogs.ts b/src/hooks/useQueryLogs.ts index 48b01daa..0ea6500c 100644 --- a/src/hooks/useQueryLogs.ts +++ b/src/hooks/useQueryLogs.ts @@ -3,7 +3,7 @@ import { getQueryLogs, getQueryResult } from '@/api/query'; import { StatusCodes } from 'http-status-codes'; import useMountedState from './useMountedState'; import { useCallback, useEffect, useMemo, useRef, useTransition } from 'react'; -import { LOG_QUERY_LIMITS, useLogsPageContext } from '@/pages/Logs/context'; +import { LOG_QUERY_LIMITS, useLogsPageContext } from '@/pages/Logs/logsContextProvider'; import { parseLogData } from '@/utils'; type QueryLogs = { diff --git a/src/pages/Logs/AlertsModal.tsx b/src/pages/Logs/AlertsModal.tsx index 7758070c..be9b1f5e 100644 --- a/src/pages/Logs/AlertsModal.tsx +++ b/src/pages/Logs/AlertsModal.tsx @@ -1,5 +1,5 @@ import { Box, Button, Modal, Stack } from '@mantine/core'; -import { useLogsPageContext } from './context'; +import { useLogsPageContext } from './logsContextProvider'; import { Text } from '@mantine/core'; import classes from './styles/Logs.module.css'; import { Editor } from '@monaco-editor/react'; diff --git a/src/pages/Logs/CarouselSlide.tsx b/src/pages/Logs/CarouselSlide.tsx index a7245b4d..0f229c73 100644 --- a/src/pages/Logs/CarouselSlide.tsx +++ b/src/pages/Logs/CarouselSlide.tsx @@ -3,7 +3,7 @@ import { useHeaderContext } from '@/layouts/MainLayout/Context'; import { Box, Button, Modal, Text, Tooltip } from '@mantine/core'; import dayjs from 'dayjs'; import { useEffect } from 'react'; -import { useLogsPageContext } from './context'; +import { useLogsPageContext } from './logsContextProvider'; import useMountedState from '@/hooks/useMountedState'; import { Carousel } from '@mantine/carousel'; import carouselStyles from './styles/CarouselSlide.module.css'; diff --git a/src/pages/Logs/DeleteStreamModal.tsx b/src/pages/Logs/DeleteStreamModal.tsx index 4f6449a6..3974d008 100644 --- a/src/pages/Logs/DeleteStreamModal.tsx +++ b/src/pages/Logs/DeleteStreamModal.tsx @@ -1,5 +1,5 @@ import { Button, Group, Modal, TextInput } from '@mantine/core'; -import { useLogsPageContext } from './context'; +import { useLogsPageContext } from './logsContextProvider'; import styles from './styles/Logs.module.css'; import { useCallback, useState } from 'react'; import { useLogStream } from '@/hooks/useLogStream'; diff --git a/src/pages/Logs/EventTimeLineGraph.tsx b/src/pages/Logs/EventTimeLineGraph.tsx index 27fc93c0..7b487117 100644 --- a/src/pages/Logs/EventTimeLineGraph.tsx +++ b/src/pages/Logs/EventTimeLineGraph.tsx @@ -2,7 +2,7 @@ import { Skeleton, Stack, Text } from '@mantine/core'; import classes from './styles/EventTimeLineGraph.module.css'; import { useQueryResult } from '@/hooks/useQueryResult'; import { useEffect } from 'react'; -import { useLogsPageContext } from './context'; +import { useLogsPageContext } from './logsContextProvider'; import dayjs from 'dayjs'; import { AreaChart } from '@mantine/charts'; import { HumanizeNumber } from '@/utils/formatBytes'; diff --git a/src/pages/Logs/FilterQueryBuilder.tsx b/src/pages/Logs/FilterQueryBuilder.tsx index 9a748a0c..28155e7a 100644 --- a/src/pages/Logs/FilterQueryBuilder.tsx +++ b/src/pages/Logs/FilterQueryBuilder.tsx @@ -11,7 +11,7 @@ import { Pill, ActionIcon, } from '@mantine/core'; -import { useLogsPageContext } from './context'; +import { useLogsPageContext } from './logsContextProvider'; import { IconFilter, IconPlus } from '@tabler/icons-react'; import classes from './styles/Querier.module.css'; import { Text } from '@mantine/core'; diff --git a/src/pages/Logs/HeaderPagination.tsx b/src/pages/Logs/HeaderPagination.tsx index 1de25895..342387d3 100644 --- a/src/pages/Logs/HeaderPagination.tsx +++ b/src/pages/Logs/HeaderPagination.tsx @@ -6,7 +6,7 @@ import { Box, Button, Text, Tooltip, px } from '@mantine/core'; import dayjs from 'dayjs'; import { FC, useEffect } from 'react'; import FillCarousel from './CarouselSlide'; -import { useLogsPageContext } from './context'; +import { useLogsPageContext } from './logsContextProvider'; import Loading from '@/components/Loading'; import { IconZoomIn, IconZoomOut } from '@tabler/icons-react'; import headerPaginationStyles from './styles/HeaderPagination.module.css'; diff --git a/src/pages/Logs/LogRow.tsx b/src/pages/Logs/LogRow.tsx index 1e9dd442..6a35c82d 100644 --- a/src/pages/Logs/LogRow.tsx +++ b/src/pages/Logs/LogRow.tsx @@ -2,7 +2,7 @@ import { parseLogData } from '@/utils'; import { Box, px } from '@mantine/core'; import { IconArrowNarrowRight } from '@tabler/icons-react'; import { FC, Fragment } from 'react'; -import { useLogsPageContext } from './context'; +import { useLogsPageContext } from './logsContextProvider'; import { Log } from '@/@types/parseable/api/query'; import tableStyles from './styles/Logs.module.css' diff --git a/src/pages/Logs/LogTable.tsx b/src/pages/Logs/LogTable.tsx index 737e467c..516f7bc1 100644 --- a/src/pages/Logs/LogTable.tsx +++ b/src/pages/Logs/LogTable.tsx @@ -20,7 +20,7 @@ import { } from '@mantine/core'; import { useCallback, useEffect, useMemo, useRef } from 'react'; import type { FC } from 'react'; -import { LOG_QUERY_LIMITS, useLogsPageContext, LOAD_LIMIT as loadLimit, LOAD_LIMIT } from './context'; +import { LOG_QUERY_LIMITS, useLogsPageContext, LOAD_LIMIT as loadLimit, LOAD_LIMIT } from './logsContextProvider'; import LogRow from './LogRow'; import useMountedState from '@/hooks/useMountedState'; import { IconSelector, IconGripVertical, IconPin, IconPinFilled, IconSettings } from '@tabler/icons-react'; diff --git a/src/pages/Logs/PrimaryToolbar.tsx b/src/pages/Logs/PrimaryToolbar.tsx index 02449807..5ff4c60c 100644 --- a/src/pages/Logs/PrimaryToolbar.tsx +++ b/src/pages/Logs/PrimaryToolbar.tsx @@ -1,7 +1,7 @@ import { Stack, px } from '@mantine/core'; import StreamDropdown from '@/components/Header/StreamDropdown'; import IconButton from '@/components/Button/IconButton'; -import { useLogsPageContext } from './context'; +import { useLogsPageContext } from './logsContextProvider'; import { useHeaderContext } from '@/layouts/MainLayout/Context'; import classes from './styles/Toolbar.module.css'; import { IconBolt, IconExclamationCircle, IconSettings, IconTrash } from '@tabler/icons-react'; diff --git a/src/pages/Logs/Querier.tsx b/src/pages/Logs/Querier.tsx index 46c28db1..4514227a 100644 --- a/src/pages/Logs/Querier.tsx +++ b/src/pages/Logs/Querier.tsx @@ -1,5 +1,5 @@ import { Group, Menu, Modal, Stack, px } from '@mantine/core'; -import { useLogsPageContext } from './context'; +import { useLogsPageContext } from './logsContextProvider'; import { ToggleButton } from '@/components/Button/ToggleButton'; import { IconChevronDown, IconCodeCircle, IconFilter } from '@tabler/icons-react'; import classes from './styles/Querier.module.css'; diff --git a/src/pages/Logs/QueryCodeEditor.tsx b/src/pages/Logs/QueryCodeEditor.tsx index 565eade4..9c1c009c 100644 --- a/src/pages/Logs/QueryCodeEditor.tsx +++ b/src/pages/Logs/QueryCodeEditor.tsx @@ -7,7 +7,7 @@ import useMountedState from '@/hooks/useMountedState'; import { notify } from '@/utils/notification'; import { usePostLLM } from '@/hooks/usePostLLM'; import { sanitiseSqlString } from '@/utils/sanitiseSqlString'; -import { LOAD_LIMIT, useLogsPageContext } from './context'; +import { LOAD_LIMIT, useLogsPageContext } from './logsContextProvider'; import { Field } from '@/@types/parseable/dataType'; import queryCodeStyles from './styles/QueryCode.module.css'; diff --git a/src/pages/Logs/QueryEditor.tsx b/src/pages/Logs/QueryEditor.tsx index 4f5e8766..4852604f 100644 --- a/src/pages/Logs/QueryEditor.tsx +++ b/src/pages/Logs/QueryEditor.tsx @@ -1,4 +1,4 @@ -import { useLogsPageContext } from './context'; +import { useLogsPageContext } from './logsContextProvider'; import { CodeHighlight } from '@mantine/code-highlight'; export const AppliedSQLQuery = () => { diff --git a/src/pages/Logs/RetentionModal.tsx b/src/pages/Logs/RetentionModal.tsx index 231354e7..2576d79c 100644 --- a/src/pages/Logs/RetentionModal.tsx +++ b/src/pages/Logs/RetentionModal.tsx @@ -1,5 +1,5 @@ import { Box, Button, Modal, Stack, Switch } from '@mantine/core'; -import { useLogsPageContext } from './context'; +import { useLogsPageContext } from './logsContextProvider'; import { Text } from '@mantine/core'; import classes from './styles/Logs.module.css'; import { Editor } from '@monaco-editor/react'; diff --git a/src/pages/Logs/SecondaryToolbar.tsx b/src/pages/Logs/SecondaryToolbar.tsx index 4002676e..7a8edee5 100644 --- a/src/pages/Logs/SecondaryToolbar.tsx +++ b/src/pages/Logs/SecondaryToolbar.tsx @@ -1,6 +1,6 @@ import { Menu, Stack, px } from '@mantine/core'; import IconButton from '@/components/Button/IconButton'; -import { useLogsPageContext } from './context'; +import { useLogsPageContext } from './logsContextProvider'; import { useHeaderContext } from '@/layouts/MainLayout/Context'; import { downloadDataAsCSV, downloadDataAsJson } from '@/utils/exportHelpers'; import classes from './styles/Toolbar.module.css'; diff --git a/src/pages/Logs/ViewLog.tsx b/src/pages/Logs/ViewLog.tsx index 3adeb985..aa9a7d07 100644 --- a/src/pages/Logs/ViewLog.tsx +++ b/src/pages/Logs/ViewLog.tsx @@ -2,7 +2,7 @@ import useMountedState from '@/hooks/useMountedState'; import { Box, Chip, CloseButton, Divider, Drawer, Text, px } from '@mantine/core'; import type { FC } from 'react'; import { useEffect, Fragment, useMemo } from 'react'; -import { useLogsPageContext } from './context'; +import { useLogsPageContext } from './logsContextProvider'; import dayjs from 'dayjs'; import viewLogStyles from './styles/ViewLogs.module.css' import { CodeHighlight } from '@mantine/code-highlight'; diff --git a/src/pages/Logs/index.tsx b/src/pages/Logs/index.tsx index 6184e826..9cef4972 100644 --- a/src/pages/Logs/index.tsx +++ b/src/pages/Logs/index.tsx @@ -7,7 +7,7 @@ import ViewLog from './ViewLog'; import DeleteStreamModal from './DeleteStreamModal'; import AlertsModal from './AlertsModal'; import RententionModal from './RetentionModal'; -import { useLogsPageContext } from './context'; +import { useLogsPageContext } from './logsContextProvider'; import PrimaryToolbar from './PrimaryToolbar'; import SecondaryToolbar from './SecondaryToolbar'; import { useHeaderContext } from '@/layouts/MainLayout/Context'; diff --git a/src/pages/Logs/Context.tsx b/src/pages/Logs/logsContextProvider.tsx similarity index 100% rename from src/pages/Logs/Context.tsx rename to src/pages/Logs/logsContextProvider.tsx diff --git a/src/providers/QueryFilterProvider.tsx b/src/providers/QueryFilterProvider.tsx index 33b87200..25154abf 100644 --- a/src/providers/QueryFilterProvider.tsx +++ b/src/providers/QueryFilterProvider.tsx @@ -1,5 +1,5 @@ import { useHeaderContext } from '@/layouts/MainLayout/Context'; -import { useLogsPageContext } from '@/pages/Logs/context'; +import { useLogsPageContext } from '@/pages/Logs/logsContextProvider'; import { generateRandomId } from '@/utils'; import { ReactNode, createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; import { Field, RuleGroupType, RuleType, formatQuery } from 'react-querybuilder'; diff --git a/src/routes/elements.tsx b/src/routes/elements.tsx index 33d3df37..d6ef24b9 100644 --- a/src/routes/elements.tsx +++ b/src/routes/elements.tsx @@ -13,7 +13,7 @@ import { } from '@/components/Header/SubHeader'; // page-wise providers -import LogsPageProvider from '@/pages/Logs/context'; +import LogsPageProvider from '@/pages/Logs/logsContextProvider'; import StatsPageProvider from '@/pages/Stats/Context'; // component-wise providers diff --git a/src/utils/sanitiseSqlString.ts b/src/utils/sanitiseSqlString.ts index c85e1ddd..ec8c8fbe 100644 --- a/src/utils/sanitiseSqlString.ts +++ b/src/utils/sanitiseSqlString.ts @@ -1,5 +1,5 @@ import { notify } from './notification'; -import { LOAD_LIMIT } from '@/pages/Logs/context'; +import { LOAD_LIMIT } from '@/pages/Logs/logsContextProvider'; export const sanitiseSqlString = (sqlString: string): string => { const withoutComments = sqlString.replace(/--.*$/gm, ''); From 76902765c6c22e6b95127ac87b1f126bc7acd99d Mon Sep 17 00:00:00 2001 From: balaji-jr Date: Wed, 28 Feb 2024 13:43:55 +0530 Subject: [PATCH 10/17] fixing live tail toggle --- src/components/Header/StreamingButton.tsx | 2 +- src/pages/Logs/LogTable.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Header/StreamingButton.tsx b/src/components/Header/StreamingButton.tsx index e7b855a0..1e2da641 100644 --- a/src/components/Header/StreamingButton.tsx +++ b/src/components/Header/StreamingButton.tsx @@ -10,7 +10,7 @@ const StreamingButton: FC = () => { state: { subLiveTailsData }, } = useHeaderContext(); - const [liveTailStatus, setLiveTailStatus] = useMountedState(''); + const [liveTailStatus, setLiveTailStatus] = useMountedState(subLiveTailsData.get().liveTailStatus); const [isClicked, setIsClicked] = useMountedState(false); const handleStreaming = () => { diff --git a/src/pages/Logs/LogTable.tsx b/src/pages/Logs/LogTable.tsx index 516f7bc1..16c47a2e 100644 --- a/src/pages/Logs/LogTable.tsx +++ b/src/pages/Logs/LogTable.tsx @@ -146,7 +146,7 @@ const LogTable: FC = () => { query, }); } - }, [currentStreamName, isQuerySearchActive]); + }, [currentStreamName, isQuerySearchActive, custSearchQuery]); useEffect(() => { resetQuerySearch(); @@ -257,7 +257,7 @@ const LogTable: FC = () => { if (pageOffset === 0 && subLogQuery.get()) { fetchCount(); } - }, [currentStreamName, isQuerySearchActive]); + }, [currentStreamName, isQuerySearchActive, custSearchQuery]); useEffect(() => { const streamErrorListener = subLogStreamError.subscribe(setLogStreamError); From f2846ec64889230bfd23b2a572f520c3e52d2002 Mon Sep 17 00:00:00 2001 From: balaji-jr Date: Thu, 29 Feb 2024 11:19:48 +0530 Subject: [PATCH 11/17] handle 403 and fixed user (other than admin) login --- src/api/axios.ts | 8 ++--- src/hooks/useUser.tsx | 54 ++++++++++++++++------------ src/pages/AccessManagement/Users.tsx | 27 ++++++++------ 3 files changed, 53 insertions(+), 36 deletions(-) diff --git a/src/api/axios.ts b/src/api/axios.ts index f47e04f5..5a07d9a0 100644 --- a/src/api/axios.ts +++ b/src/api/axios.ts @@ -16,15 +16,15 @@ instance.interceptors.request.use( instance.interceptors.response.use( (response) => { - return response; + return response; }, (error) => { const status = error.status || (error.response ? error.response.status : 0); - if (status === 403 || status === 401) { + if (status === 401) { signOutHandler(); } return Promise.reject(error); - } - ); + }, +); export const Axios = () => instance; diff --git a/src/hooks/useUser.tsx b/src/hooks/useUser.tsx index 28ec8e41..296a5b91 100644 --- a/src/hooks/useUser.tsx +++ b/src/hooks/useUser.tsx @@ -14,14 +14,20 @@ export const useUser = () => { isLoading: createUserIsLoading, data: createUserData, reset: createUserReset, - } = useMutation((data: { userName: string; roles: object[] }) => postUser(data.userName, data.roles), { - onError: (data: AxiosError) => { - if (isAxiosError(data) && data.response) { - const error = data.response.data as string; - setCreateUserError(error); - } + } = useMutation( + (data: { userName: string; roles: object[]; onSuccess?: () => void }) => postUser(data.userName, data.roles), + { + onError: (data: AxiosError) => { + if (isAxiosError(data) && data.response) { + const error = data.response.data as string; + setCreateUserError(error); + } + }, + onSuccess: (_data, variables) => { + variables.onSuccess && variables.onSuccess(); + }, }, - }); + ); const { mutate: deleteUserMutation, @@ -54,16 +60,6 @@ export const useUser = () => { }, }); - const { - data: getUserData, - isError: getUserIsError, - isSuccess: getUserIsSuccess, - isLoading: getUserIsLoading, - refetch: getUserRefetch, - } = useQuery(['fetch-user', createUserIsSuccess, deleteUserIsSuccess], () => getUsers(), { - retry: false, - }); - const { data: getUserRolesData, isError: getUserRolesIsError, @@ -81,11 +77,6 @@ export const useUser = () => { createUserIsLoading, createUserData, createUserReset, - getUserRefetch, - getUserData, - getUserIsError, - getUserIsSuccess, - getUserIsLoading, deleteUserMutation, deleteUserIsSuccess, deleteUserIsError, @@ -110,3 +101,22 @@ export const useUser = () => { createUserError, }; }; + +export const useGetUser = () => { + const { + data: getUserData, + isError: getUserIsError, + isSuccess: getUserIsSuccess, + isLoading: getUserIsLoading, + refetch: getUserRefetch, + } = useQuery(['fetch-user'], () => getUsers(), { + retry: false, + }); + return { + getUserRefetch, + getUserData, + getUserIsError, + getUserIsSuccess, + getUserIsLoading, + }; +}; diff --git a/src/pages/AccessManagement/Users.tsx b/src/pages/AccessManagement/Users.tsx index e49a603c..eb082f2d 100644 --- a/src/pages/AccessManagement/Users.tsx +++ b/src/pages/AccessManagement/Users.tsx @@ -2,13 +2,13 @@ import { Box, Button, Group, Modal, ScrollArea, Select, Stack, Table, Text, Text import { useDocumentTitle } from '@mantine/hooks'; import { FC, useEffect, useState } from 'react'; -import { useUser } from '@/hooks/useUser'; +import { useGetUser, useUser } from '@/hooks/useUser'; import { useHeaderContext } from '@/layouts/MainLayout/Context'; import RoleTR from './RoleTR'; import { IconUserPlus } from '@tabler/icons-react'; import { useRole } from '@/hooks/useRole'; -import styles from './styles/AccessManagement.module.css' +import styles from './styles/AccessManagement.module.css'; import { heights } from '@/components/Mantine/sizing'; import { HEADER_HEIGHT } from '@/constants/theme'; import { CodeHighlight } from '@mantine/code-highlight'; @@ -32,9 +32,6 @@ const Users: FC = () => { const [roleSearchValue, setRoleSearchValue] = useState(''); const { - getUserData, - getUserIsSuccess, - getUserIsLoading, createUserMutation, createUserIsError, createUserIsLoading, @@ -51,6 +48,8 @@ const Users: FC = () => { createUserReset, } = useUser(); + const { getUserData, getUserIsSuccess, getUserIsLoading, getUserRefetch } = useGetUser(); + const { getRolesData } = useRole(); const rows = @@ -94,7 +93,7 @@ const Users: FC = () => { if (SelectedRole !== '') { userRole.push(SelectedRole); } - createUserMutation({ userName: createUserInput, roles: userRole }); + createUserMutation({ userName: createUserInput, roles: userRole, onSuccess: getUserRefetch }); }; const createVaildtion = () => { @@ -112,9 +111,11 @@ const Users: FC = () => { }; return ( - + - + Users - } - /> + + setAiQuery(e.target.value)} + placeholder="Enter plain text to generate SQL query using OpenAI" + w="85%" + /> + + ) : ( From 0d67148f266ab4f7478882365d851c9706d5c36f Mon Sep 17 00:00:00 2001 From: balaji-jr Date: Thu, 29 Feb 2024 16:15:37 +0530 Subject: [PATCH 14/17] overall color scheme, heights and widths changes --- src/assets/images/brand/icon.svg | 1 + src/components/Header/PrimaryHeader.tsx | 90 ++++--- src/components/Header/SubHeader.tsx | 52 ---- .../Header/styles/Header.module.css | 33 ++- src/components/Navbar/index.tsx | 20 +- .../Navbar/styles/Navbar.module.css | 33 +-- src/constants/theme.ts | 4 +- src/pages/AccessManagement/Roles.tsx | 52 ++-- src/pages/AccessManagement/Users.tsx | 31 ++- .../styles/AccessManagement.module.css | 17 +- src/pages/Stats/Alerts.tsx | 79 ------ src/pages/Stats/Context.tsx | 53 ---- src/pages/Stats/Status.tsx | 252 ------------------ src/pages/Stats/index.tsx | 18 -- src/pages/Stats/styles/Alerts.module.css | 53 ---- src/pages/Stats/styles/StatsCard.module.css | 39 --- src/pages/Stats/styles/Status.module.css | 50 ---- src/routes/elements.tsx | 45 ---- src/routes/index.tsx | 30 +-- 19 files changed, 159 insertions(+), 793 deletions(-) create mode 100644 src/assets/images/brand/icon.svg delete mode 100644 src/pages/Stats/Alerts.tsx delete mode 100644 src/pages/Stats/Context.tsx delete mode 100644 src/pages/Stats/Status.tsx delete mode 100644 src/pages/Stats/index.tsx delete mode 100644 src/pages/Stats/styles/Alerts.module.css delete mode 100644 src/pages/Stats/styles/StatsCard.module.css delete mode 100644 src/pages/Stats/styles/Status.module.css diff --git a/src/assets/images/brand/icon.svg b/src/assets/images/brand/icon.svg new file mode 100644 index 00000000..36297700 --- /dev/null +++ b/src/assets/images/brand/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/Header/PrimaryHeader.tsx b/src/components/Header/PrimaryHeader.tsx index f0444a1c..7b031a19 100644 --- a/src/components/Header/PrimaryHeader.tsx +++ b/src/components/Header/PrimaryHeader.tsx @@ -1,49 +1,51 @@ -import logoInvert from '@/assets/images/brand/logo-invert.svg'; -import { HOME_ROUTE } from '@/constants/routes'; -import { PRIMARY_HEADER_HEIGHT } from '@/constants/theme'; -import { Box, Button, Image, Stack } from '@mantine/core'; -import { FC } from 'react'; -import styles from './styles/Header.module.css'; -import { IconHelp, IconStackPop } from '@tabler/icons-react'; -import { useHeaderContext } from '@/layouts/MainLayout/Context'; -import HelpModal from './HelpModal'; + import icon from '@/assets/images/brand/icon.svg'; + import { HOME_ROUTE } from '@/constants/routes'; + import { NAVBAR_WIDTH, PRIMARY_HEADER_HEIGHT } from '@/constants/theme'; + import { Button, Image, Stack } from '@mantine/core'; + import { FC } from 'react'; + import styles from './styles/Header.module.css'; + import { useHeaderContext } from '@/layouts/MainLayout/Context'; + import HelpModal from './HelpModal'; -const PrimaryHeader: FC = () => { - const classes = styles; - const { logoContainer, imageSty } = classes; - const { - state: { maximized, helpModalOpen }, - methods: { toggleHelpModal }, - } = useHeaderContext(); + const PrimaryHeader: FC = () => { + const classes = styles; + const { logoContainer, imageSty } = classes; + const { + state: { maximized, helpModalOpen }, + methods: { toggleHelpModal }, + } = useHeaderContext(); - if (maximized) return null; + if (maximized) return null; - return ( - - - - - Parseable Logo - - - - - + return ( + + + + + Parseable Logo + + + + + + + - - ); -}; + ); + }; -export default PrimaryHeader; + export default PrimaryHeader; diff --git a/src/components/Header/SubHeader.tsx b/src/components/Header/SubHeader.tsx index e8dd7e2e..21c9126b 100644 --- a/src/components/Header/SubHeader.tsx +++ b/src/components/Header/SubHeader.tsx @@ -1,15 +1,11 @@ import { Box } from '@mantine/core'; import { type FC } from 'react'; import HeaderBreadcrumbs from './HeaderBreadcrumbs'; -import RefreshNow from './RefreshNow'; -import ReloadUser from './ReloadUser'; -import DocsUser from './UserDocs'; import StreamingButton from './StreamingButton'; import LiveTailFilter from './LiveTailFilter'; import { HEADER_HEIGHT } from '@/constants/theme'; import styles from './styles/LogQuery.module.css'; import headerStyles from './styles/Header.module.css'; -import { useStatsPageContext } from '@/pages/Stats/Context'; type HeaderLayoutProps = { children: React.ReactNode; @@ -27,31 +23,6 @@ const HeaderLayout: FC = (props) => { ); }; -export const StatsHeader: FC = () => { - const classes = styles; - const { container, innerContainer } = classes; - const { - methods: { resetFetchStartTime }, - } = useStatsPageContext(); - return ( - - - - - - - - - - - - - - - - ); -}; - export const LiveTailHeader: FC = () => { const classes = styles; const { container, innerContainer } = classes; @@ -109,29 +80,6 @@ export const ConfigHeader: FC = () => { ); }; -export const UsersManagementHeader: FC = () => { - const classes = styles; - const { container, innerContainer } = classes; - - return ( - - - - - - - - - - - - - - - - ); -}; - export const AllRouteHeader: FC = () => { const classes = styles; const { container, innerContainer } = classes; diff --git a/src/components/Header/styles/Header.module.css b/src/components/Header/styles/Header.module.css index bef9b7e5..79aafa4e 100644 --- a/src/components/Header/styles/Header.module.css +++ b/src/components/Header/styles/Header.module.css @@ -7,16 +7,16 @@ font-weight: 500; line-height: normal; width: 100%; - border-bottom: 1px solid var(--mantine-color-gray-3); } .logoContainer { display: flex; justify-content: center; align-items: center; - padding: 1rem; - padding-right: 0; - height: 24px; + transition: width 0.4s ease-in-out; + background-color: var(--mantine-color-gray-0); + border-bottom: rem(1px) solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4)); + height: 100%; } .navContainer { @@ -35,13 +35,24 @@ .primaryHeaderContainer { background-color: white; - display: flex; align-items: center; - color: #495057; - font-size: 1rem; - font-weight: 500; - line-height: normal; + justify-content: space-between; width: 100%; - border-bottom: 1px solid var(--mantine-color-gray-3); - /* box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.2); */ + flex-direction: row; + background-color: var(--mantine-color-gray-0); +} + +.rightSection { + border-bottom: rem(1px) solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4)); + flex-direction: row; + justify-content: flex-end; + height: 100%; + flex: 1; + align-items: center; +} + +.divider { + border: 1px solid; + height: 70%; + border-color: var(--mantine-color-gray-4); } \ No newline at end of file diff --git a/src/components/Navbar/index.tsx b/src/components/Navbar/index.tsx index d8f6606f..1bd726f8 100644 --- a/src/components/Navbar/index.tsx +++ b/src/components/Navbar/index.tsx @@ -1,5 +1,5 @@ -import { Box, Stack, Text } from '@mantine/core'; -import { IconLogout, IconUser, IconBinaryTree2, IconInfoCircle, IconUserCog, IconHome } from '@tabler/icons-react'; +import { Box, Stack, Tooltip } from '@mantine/core'; +import { IconLogout, IconUser, IconBinaryTree2, IconInfoCircle, IconUserCog, IconHome, IconChevronRight, IconChevronLeft } from '@tabler/icons-react'; import { FC, useCallback, useEffect } from 'react'; import { useLocation, useParams } from 'react-router-dom'; import { useNavigate } from 'react-router-dom'; @@ -13,7 +13,7 @@ import { useUser } from '@/hooks/useUser'; import { useLogStream } from '@/hooks/useLogStream'; import styles from './styles/Navbar.module.css'; import useCurrentRoute from '@/hooks/useCurrentRoute'; -import { NAVBAR_WIDTH, PRIMARY_HEADER_HEIGHT } from '@/constants/theme'; +import { MAXIMIZED_NAVBAR_WIDTH, MINIMIZED_NAVABR_WIDTH, NAVBAR_WIDTH, PRIMARY_HEADER_HEIGHT } from '@/constants/theme'; import UserModal from './UserModal'; import { signOutHandler } from '@/utils'; @@ -41,7 +41,7 @@ const navItems = [ const navActions = [ { icon: IconUser, - label: 'User', + label: 'Profile', key: 'user', }, { @@ -128,7 +128,7 @@ const Navbar: FC = () => {