From 1344074776d6c419366f43b1d966f78eb0b6bfec Mon Sep 17 00:00:00 2001 From: Kartik Gupta Date: Tue, 11 Jul 2023 16:48:25 +0530 Subject: [PATCH 1/7] navigation fixed --- src/components/Navbar/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Navbar/index.tsx b/src/components/Navbar/index.tsx index b62093f7..d12a3529 100644 --- a/src/components/Navbar/index.tsx +++ b/src/components/Navbar/index.tsx @@ -36,6 +36,7 @@ const Navbar: FC = (props) => { const handleChange = (value: string) => { setActiveStream(value); setSearchValue(value); + navigate(`/${value}/logs`); }; useEffect(() => { From 36ba79755ae96128c9fb8ca4b6c7ff58f7fcbfd8 Mon Sep 17 00:00:00 2001 From: Kartik Gupta Date: Sun, 16 Jul 2023 20:07:38 +0530 Subject: [PATCH 2/7] UI Changes --- .../Header/SubHeader.tsx} | 97 +++----- src/components/Header/index.tsx | 165 +++----------- src/components/Header/styles.tsx | 215 +++++++++++++----- src/components/Mantine/theme.tsx | 3 +- src/components/Navbar/index.tsx | 114 +++++++--- src/components/Navbar/styles.tsx | 49 +++- src/constants/theme.ts | 2 +- src/layouts/MainLayout/Context.tsx | 95 ++++++++ src/pages/Logs/Context.tsx | 63 +---- src/pages/Logs/FilterPills.tsx | 4 +- src/pages/Logs/LogStreamList.tsx | 173 -------------- src/pages/Logs/LogTable.tsx | 38 +++- src/pages/Logs/index.tsx | 24 +- src/routes/elements.tsx | 15 +- src/routes/index.tsx | 5 +- 15 files changed, 478 insertions(+), 584 deletions(-) rename src/{pages/Logs/LogQuery.tsx => components/Header/SubHeader.tsx} (78%) create mode 100644 src/layouts/MainLayout/Context.tsx delete mode 100644 src/pages/Logs/LogStreamList.tsx diff --git a/src/pages/Logs/LogQuery.tsx b/src/components/Header/SubHeader.tsx similarity index 78% rename from src/pages/Logs/LogQuery.tsx rename to src/components/Header/SubHeader.tsx index 0ea1c06a..5bcfae62 100644 --- a/src/pages/Logs/LogQuery.tsx +++ b/src/components/Header/SubHeader.tsx @@ -1,35 +1,32 @@ import useMountedState from '@/hooks/useMountedState'; -import { Box, Breadcrumbs, Button, Center, Menu, Text, TextInput, UnstyledButton, px } from '@mantine/core'; +import { Box, Breadcrumbs, Button, Menu, Text, TextInput, UnstyledButton, px } from '@mantine/core'; import { DateTimePicker } from '@mantine/dates'; -import { IconClock, IconRefresh, IconRefreshOff, IconSearch, IconSelector } from '@tabler/icons-react'; +import { IconClock, IconRefresh, IconRefreshOff, IconSearch } from '@tabler/icons-react'; import dayjs from 'dayjs'; import ms from 'ms'; import type { ChangeEvent, FC, KeyboardEvent } from 'react'; import { Fragment, useEffect, useMemo } from 'react'; -import { FIXED_DURATIONS, REFRESH_INTERVALS, SEARCH_TYPES, SearchTypes, useLogsPageContext } from './Context'; +import { FIXED_DURATIONS, REFRESH_INTERVALS, useHeaderContext } from '@/layouts/MainLayout/Context'; import { useLogQueryStyles } from './styles'; -const LogQuery: FC = () => { +const SubHeader: FC = () => { const { classes } = useLogQueryStyles(); - const { container, innerContainer } = classes; + const { container, innerContainer, homeIcon, activeBtn } = classes; const { state: { subLogQuery }, - } = useLogsPageContext(); + } = useHeaderContext(); return ( - - {/* Home > Streams > Stream-name > logs */} - Home - Streams - {subLogQuery.get().streamName} - Logs - - - + + + + Streams + {subLogQuery.get().streamName} + Logs @@ -51,11 +48,19 @@ const LogQuery: FC = () => { const Search: FC = () => { const { state: { subLogSearch }, - } = useLogsPageContext(); + } = useHeaderContext(); - const [searchValue, setSearchValue] = useMountedState(subLogSearch.get().search); + const [searchValue, setSearchValue] = useMountedState(""); const { classes } = useLogQueryStyles(); + useEffect(() => { + const listener = subLogSearch.subscribe((interval) => { + setSearchValue(interval.search); + }); + return () => { + listener(); + }; + }, []); const { searchContainer, searchInput } = classes; const onSearchValueChange = (event: ChangeEvent) => { @@ -90,60 +95,10 @@ const Search: FC = () => { ); }; -export const SearchTypeSelector: FC = () => { - const { - state: { subLogSearchType }, - } = useLogsPageContext(); - const [selectedSearchType, setSelectedSearchType] = useMountedState(subLogSearchType.get()); - - useEffect(() => { - const listener = subLogSearchType.subscribe((state) => { - setSelectedSearchType(state); - }); - - return () => listener(); - }, []); - - const onSelect = (sType: SearchTypes) => { - subLogSearchType.set(sType); - }; - - const { classes, cx } = useLogQueryStyles(); - - const { searchTypeBtn, searchTypeActive } = classes; - - return ( - -
- - - -
- - {SEARCH_TYPES.map((sType) => { - return ( - onSelect(sType)}> - {sType} - - ); - })} - -
- ); -}; - const RefreshInterval: FC = () => { const { state: { subRefreshInterval }, - } = useLogsPageContext(); + } = useHeaderContext(); const [selectedInterval, setSelectedInterval] = useMountedState(subRefreshInterval.get()); @@ -197,7 +152,7 @@ type FixedDurations = (typeof FIXED_DURATIONS)[number]; const TimeRange: FC = () => { const { state: { subLogQuery, subLogSelectedTimeRange }, - } = useLogsPageContext(); + } = useHeaderContext(); const [selectedRange, setSelectedRange] = useMountedState(subLogSelectedTimeRange.get()); @@ -265,7 +220,7 @@ const TimeRange: FC = () => { const CustomTimeRange: FC = () => { const { state: { subLogQuery, subLogSelectedTimeRange }, - } = useLogsPageContext(); + } = useHeaderContext(); const [selectedRange, setSelectedRange] = useMountedState({ startTime: subLogQuery.get().startTime, @@ -344,4 +299,4 @@ const CustomTimeRange: FC = () => { ); }; -export default LogQuery; +export default SubHeader; diff --git a/src/components/Header/index.tsx b/src/components/Header/index.tsx index 41804fe5..e9b41f97 100644 --- a/src/components/Header/index.tsx +++ b/src/components/Header/index.tsx @@ -1,148 +1,51 @@ import logoInvert from '@/assets/images/brand/logo-invert.svg'; -import docImage from '@/assets/images/doc.webp'; -import githubLogo from '@/assets/images/github-logo.webp'; -import slackLogo from '@/assets/images/slack-logo.webp'; -import { HOME_ROUTE, LOGIN_ROUTE } from '@/constants/routes'; +import { HOME_ROUTE } from '@/constants/routes'; import { HEADER_HEIGHT } from '@/constants/theme'; -import type { BoxProps, HeaderProps as MantineHeaderProps, UnstyledButtonProps } from '@mantine/core'; -import { Anchor, Box, Card, Image, Header as MantineHeader, Text, UnstyledButton, Modal, px } from '@mantine/core'; -import { useDisclosure, useLocalStorage } from '@mantine/hooks'; -import { IconHelpCircle, IconLogout, IconUser } from '@tabler/icons-react'; -import { FC, Fragment } from 'react'; -import { Link, useNavigate } from 'react-router-dom'; +import type { HeaderProps as MantineHeaderProps } from '@mantine/core'; +import { Box, Image, Header as MantineHeader, Burger } from '@mantine/core'; +import { FC, useEffect } from 'react'; +import { Link } from 'react-router-dom'; import { useHeaderStyles } from './styles'; +import SubHeader from './SubHeader'; +import {useHeaderContext} from '@/layouts/MainLayout/Context'; +import useMountedState from '@/hooks/useMountedState'; type HeaderProps = Omit; const Header: FC = (props) => { const { classes } = useHeaderStyles(); - const { container, actionsContainer } = classes; + const { container, logoContainer, burgerIcon ,navContainer} = classes; + const { + state: { subNavbarTogle }, + } = useHeaderContext(); + const [isSubNavbarOpen, setIsSubNavbarOpen] = useMountedState(false); + useEffect(() => { + const listener = subNavbarTogle.subscribe(setIsSubNavbarOpen); + return () => { + listener(); + }; + }, []); return ( - - - Parseable Logo - - - - - + + + + Parseable Logo + + subNavbarTogle.set((state) => !state)} + size={"sm"} + /> - - ); -}; - -const helpResources = [ - { - image: slackLogo, - title: 'Slack', - description: 'Connect with us', - href: 'https://launchpass.com/parseable', - }, - { - image: githubLogo, - title: 'GitHub', - description: 'Find resources', - href: 'https://github.com/parseablehq/parseable', - }, - { - image: docImage, - title: 'Documentation', - description: 'Learn more', - href: 'https://www.parseable.io/docs/introduction', - }, -]; - -const Help: FC = (props) => { - const [opened, { close, open }] = useDisclosure(); - - const { classes } = useHeaderStyles(); - const { actionBtn, actionBtnText, helpTitle, helpDescription } = classes; - - return ( - - - - - Help - - - - Need any help? - Here you can find useful resources and information. - - {helpResources.map((data) => ( - - ))} - - - - ); -}; - -type HelpCardProps = { - data: (typeof helpResources)[number]; -}; - -const HelpCard: FC = (props) => { - const { data } = props; - - const { classes } = useHeaderStyles(); - const { helpCard, helpCardTitle, helpCardDescription } = classes; - - return ( - - - - {data.title} - {data.description} - - {data.title} - - - ); -}; - -const User: FC = (props) => { - const [username] = useLocalStorage({ key: 'username', getInitialValueInEffect: false }); - - const { classes } = useHeaderStyles(); - const { userContainer, userText } = classes; - - return ( - - - - {username} - + + - ); -}; - -const SignOut: FC = (props) => { - const nav = useNavigate(); - const [, , removeCredentials] = useLocalStorage({ key: 'credentials' }); - const [, , removeUsername] = useLocalStorage({ key: 'username' }); - - const onSignOut = () => { - removeCredentials(); - removeUsername(); - nav( - { - pathname: LOGIN_ROUTE, - }, - { replace: true }, - ); - }; - - const { classes } = useHeaderStyles(); - const { actionBtn } = classes; - return ( - - - + ); }; + export default Header; diff --git a/src/components/Header/styles.tsx b/src/components/Header/styles.tsx index d240a65c..04783c91 100644 --- a/src/components/Header/styles.tsx +++ b/src/components/Header/styles.tsx @@ -1,106 +1,199 @@ +import { HEADER_HEIGHT, NAVBAR_WIDTH } from '@/constants/theme'; import { createStyles } from '@mantine/core'; export const useHeaderStyles = createStyles((theme) => { - const {colors, spacing, fontSizes, radius, shadows } = theme; - const { fontWeights, widths, heights } = theme.other; + const { colors, spacing, fontSizes } = theme; + const { fontWeights} = theme.other; - const white = colors.white[0]; - const sColor = colors.brandSecondary[0]; - const defaultRadius = radius[theme.defaultRadius as string]; return { container: { - background: white, + background: colors.gray[0], display: 'flex', alignItems: 'center', - justifyContent: 'space-between', - paddingLeft: spacing.lg, + color: theme.colors.gray[7], + fontFamily: theme.fontFamily, + fontSize: fontSizes.md, + fontWeight: fontWeights.normal, + lineHeight: "normal", + height: HEADER_HEIGHT, + width: '100%', + }, + logoContainer: { + display: 'flex', + alignItems: 'self-end', + width: NAVBAR_WIDTH, + justifyContent: "space-between", + padding: spacing.md, + }, + burgerIcon: { + size:"24px", + color: theme.colors.gray[7], + }, + navContainer: { + width: `calc(100% - ${NAVBAR_WIDTH}px)`, + justifyContent: "space-between", }, - actionsContainer: { + }; +}); + + +export const useLogQueryStyles = createStyles((theme) => { + const { spacing, radius, colors, fontSizes } = theme; + const { sizing, widths, fontWeights } = theme.other; + const defaultRadius = radius[theme.defaultRadius as string]; + const pColor = colors.brandPrimary[0]; + const sColor = colors.brandSecondary[0]; + return { + container: { display: 'flex', + justifyContent: 'space-between', alignItems: 'center', + padding: spacing.md, }, - actionBtn: { + innerContainer: { + display: 'flex', + paddingTop: spacing.xxs, + marginRight: spacing.md, + }, + homeIcon: { + size:"24px", + strokeWidth: 1.0, + }, + activeBtn:{ + color: colors.brandPrimary[0], + textDecoration: "underline", + }, + + intervalBtn: { display: 'flex', - alignItems: 'center', justifyContent: 'center', + alignItems: 'center', + + background: pColor, + color: colors.white[0], + minWidth: widths[20], + border: `${sizing.px} ${colors.gray[2]} solid`, + padding: `${spacing.xs} ${spacing.sm}`, + marginRight: spacing.xs, + '&:hover': { + background: sColor, + }, + }, - '&:hover *': { - color: sColor, + timeRangeBTn: { + color: colors.black[0], + border: `${sizing.px} ${colors.gray[2]} solid`, + minWidth: widths[20], + padding: `${spacing.xs} ${spacing.sm}`, + marginRight: spacing.xs, + background: colors.white[0], + fontWeight: fontWeights.medium, + fontSize: fontSizes.xs, + + '&:hover': { + background: colors.gray[1], }, }, - actionBtnText: { - fontSize: fontSizes.sm, + timeRangeContainer: { + display: 'flex', }, - helpTitle: { - fontSize: fontSizes.md, - textAlign: 'center', - color: white, - fontWeight: fontWeights.bold, + fixedRangeContainer: { + display: 'flex', + flexDirection: 'column', + background: colors.gray[0], }, - helpDescription: { + fixedRangeBtn: { + color: colors.black, + padding: spacing.sm, fontSize: fontSizes.sm, - textAlign: 'center', - color: colors.dimmed[0], - marginTop: spacing.xs, - '&::after': { - content: '""', - borderRadius: defaultRadius, - display: 'block', - backgroundColor: sColor, - width: widths[14], - height: heights['0.5'], - marginTop: theme.spacing.sm, - marginLeft: 'auto', - marginRight: 'auto', + '&:hover': { + background: colors.gray[1], }, - }, - helpCard: { - display: 'flex', - alignItems: 'center', - justifyContent: 'space-between', - boxShadow: shadows.sm, - marginTop: spacing.sm, - transition: 'transform .2s ease-in-out', + '&:first-of-type': { + borderTopLeftRadius: defaultRadius, + }, - '&:hover': { - transform: 'scale(1.05)', + '&:last-of-type': { + borderBottomLeftRadius: defaultRadius, }, }, - helpCardTitle: { + fixedRangeBtnSelected: { + background: pColor, fontWeight: fontWeights.semibold, + color: colors.white[0], - '&::after': { - content: '""', - display: 'block', - backgroundColor: white, - width: widths[14], - height: heights['0.5'], - marginTop: spacing.xs, - marginBottom: spacing.xs, + '&:hover': { + background: sColor, }, }, - helpCardDescription: { - color: colors.dimmed[0], - fontSize: fontSizes.sm, + customRangeContainer: { + padding: `${spacing.xs} ${spacing.lg}`, + minWidth: widths[80], + flex: 1, + display: 'flex', + flexDirection: 'column', + justifyContent: 'stretch', }, - userContainer: { + customTimeRangeFooter: { display: 'flex', + marginTop: 'auto', + justifyContent: 'end', alignItems: 'center', - justifyContent: 'center', }, - userText: { - fontSize: fontSizes.sm, + + customTimeRangeApplyBtn: { + background: pColor, + '&:hover': { + background: sColor, + }, + }, + + searchContainer: { + display: 'flex', + }, + + searchTypeBtn: { + border: `${sizing.px} ${colors.gray[1]} solid`, + borderTopRightRadius: 0, + borderBottomRightRadius: 0, + background: pColor, + color: colors.white[0], + borderRight: 'none', + fontWeight: fontWeights.semibold, + paddingRight: spacing.sm, + }, + + searchTypeActive: { + background: pColor, + fontWeight: fontWeights.medium, + color: colors.white[0], + + '&:hover': { + background: sColor, + }, + }, + + searchInput: { + width: '100%', + flex: 1, + + '& input': { + background: colors.white[0], + border: `${sizing.px} ${colors.gray[2]} solid`, + borderRadius: defaultRadius, + fontWeight: fontWeights.medium, + }, }, }; -}); +}); \ No newline at end of file diff --git a/src/components/Mantine/theme.tsx b/src/components/Mantine/theme.tsx index 15d478f9..e962eda4 100644 --- a/src/components/Mantine/theme.tsx +++ b/src/components/Mantine/theme.tsx @@ -15,12 +15,13 @@ const globalStyles = (): CSSObject => { export const theme: MantineThemeOverride = { globalStyles, fontFamily: - 'Inter var,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji', + 'Inter ,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji', colors: { black: ['#000000'], white: ['#FFFFFF'], brandPrimary: ['#545BEB', '#1F288E', '#1A237E', '#10143E'], brandSecondary: ['#FC466B', '#F29C38', '#C27D2D'], + gray: ['#F1F1F1', '#E0E0E0', '#D4D4D4', '#828282', '#4F4F4F', '#777777' , '#211F1F'], error: ['#8F0F27'], dimmed: ['#868e96'], }, diff --git a/src/components/Navbar/index.tsx b/src/components/Navbar/index.tsx index d12a3529..9858d27d 100644 --- a/src/components/Navbar/index.tsx +++ b/src/components/Navbar/index.tsx @@ -1,14 +1,18 @@ import type { NavbarProps as MantineNavbarProps } from '@mantine/core'; -import { Navbar as MantineNavbar, NavLink ,Select} from '@mantine/core'; -import { IconNews , IconCodeCircle2, IconDatabase ,IconCheck ,IconFileAlert, IconReload } from '@tabler/icons-react'; -import { FC ,useEffect, useState} from 'react'; +import { Navbar as MantineNavbar, NavLink, Select } from '@mantine/core'; +import { IconNews, IconCodeCircle2, IconCheck, IconFileAlert, IconReload, IconHelpCircle, IconLogout, IconUser } from '@tabler/icons-react'; +import { FC, useEffect, useState } from 'react'; import { useNavbarStyles } from './styles'; import { useParams } from "react-router-dom"; import { useGetLogStreamList } from '@/hooks/useGetLogStreamList'; import { notifications } from '@mantine/notifications'; import { useNavigate } from 'react-router-dom'; +import { DEFAULT_FIXED_DURATIONS, useHeaderContext } from '@/layouts/MainLayout/Context'; +import useMountedState from '@/hooks/useMountedState'; +import dayjs from 'dayjs'; + const links = [ - { icon: IconNews , label: 'Logs', pathname: "/logs" }, + { icon: IconNews, label: 'Logs', pathname: "/logs" }, { icon: IconCodeCircle2, label: 'Query', pathname: "/query" }, ]; @@ -20,13 +24,35 @@ const Navbar: FC = (props) => { const [activeStream, setActiveStream] = useState(""); const [searchValue, setSearchValue] = useState(""); const { classes } = useNavbarStyles(); - const { container ,linkBtnActive, linkBtn ,selectStreambtn} = classes; + const { container, linkBtnActive, linkBtn, selectStreambtn ,streamsBtn ,lowerContainer} = classes; const { streamName } = useParams(); + const { + state: { subNavbarTogle }, + } = useHeaderContext(); + const [isSubNavbarOpen, setIsSubNavbarOpen] = useMountedState(false); + useEffect(() => { + const listener = subNavbarTogle.subscribe(setIsSubNavbarOpen); + return () => { + listener(); + }; + }, []); + + + const { state: { subLogQuery, subLogSelectedTimeRange, subLogSearch, subRefreshInterval } } = useHeaderContext(); useEffect(() => { - if(streamName) { + if (streamName) { setActiveStream(streamName); setSearchValue(streamName); + if (streamName !== subLogQuery.get().streamName) { + const now = dayjs(); + subLogQuery.set((state) => { + state.streamName = streamName || ''; + state.startTime = now.subtract(DEFAULT_FIXED_DURATIONS.milliseconds, 'milliseconds').toDate(); + state.endTime = now.toDate(); + }); + subLogSelectedTimeRange.set(DEFAULT_FIXED_DURATIONS.name); + } } else if (streams && Boolean(streams.length)) { navigate(`/${streams[0].name}/logs`); @@ -37,10 +63,24 @@ const Navbar: FC = (props) => { setActiveStream(value); setSearchValue(value); navigate(`/${value}/logs`); + if (value !== subLogQuery.get().streamName) { + const now = dayjs(); + subLogQuery.set((state) => { + state.streamName = value || ''; + state.startTime = now.subtract(DEFAULT_FIXED_DURATIONS.milliseconds, 'milliseconds').toDate(); + state.endTime = now.toDate(); + }); + subLogSelectedTimeRange.set(DEFAULT_FIXED_DURATIONS.name); + subLogSearch.set((state) => { + state.search = '', + state.filters = {} + }); + subRefreshInterval.set(null); + } }; useEffect(() => { - if(loading){ + if (loading) { notifications.show({ id: 'load-data', loading: true, @@ -49,36 +89,39 @@ const Navbar: FC = (props) => { message: 'Streams will be loaded.', autoClose: false, withCloseButton: false, - }) - }; - if(streams && Boolean(streams.length)){ + }) + }; + if (streams && Boolean(streams.length)) { notifications.update({ - id: 'load-data', - color: 'green', - title: 'Streams was loaded', - message: 'Successfully Loaded!!', - icon: , - autoClose: 1000, + id: 'load-data', + color: 'green', + title: 'Streams was loaded', + message: 'Successfully Loaded!!', + icon: , + autoClose: 1000, }); - } - if(error){ + } + if (error) { notifications.update({ - id: 'load-data', - color: 'red', - title: 'Error Occured', - message: 'Error Occured while fetching streams', - icon: , - autoClose: 2000, + id: 'load-data', + color: 'red', + title: 'Error Occured', + message: 'Error Occured while fetching streams', + icon: , + autoClose: 2000, }); - } + } + + }, [streams, error, loading]); - }, [streams , error , loading]); - return ( - +