Skip to content

Commit

Permalink
ISSUE-178 - added instant exports to explore tab
Browse files Browse the repository at this point in the history
  • Loading branch information
balaji-jr committed Jan 19, 2024
1 parent 46e8a4a commit 112ec3d
Show file tree
Hide file tree
Showing 10 changed files with 209 additions and 101 deletions.
17 changes: 15 additions & 2 deletions src/components/Header/Dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,26 @@ import { Select } from '@mantine/core';

type DropdownProps = {
data: string[];
placeholder?: string;
searchable?: boolean;
value?: string | null;
onChange: (value: string) => void;
};

const Dropdown: FC<DropdownProps> = (props) => {
const { data, onChange } = props;
const { data, onChange, placeholder = "Export", searchable = false, value = null } = props;

return <Select placeholder="Column" data={data} w={140} searchable onChange={onChange} allowDeselect={false} />;
return (
<Select
placeholder={placeholder}
data={data}
w={140}
searchable={searchable}
onChange={onChange}
allowDeselect={false}
value={value}
/>
);
};

export default Dropdown;
8 changes: 7 additions & 1 deletion src/components/Header/LiveTailFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,13 @@ const LiveTailFilter: FC = () => {
<Box className={liveTailFilterContainer}>
{schemaData.length > 0 && (
<>
<Dropdown data={schemaData.map((item) => item.name)} onChange={handleDropdownValue} />
<Dropdown
data={schemaData.map((item) => item.name)}
searchable
onChange={handleDropdownValue}
placeholder="Column"
value={searchField}
/>
<TextInput
className={searchInput}
value={searchValue}
Expand Down
32 changes: 0 additions & 32 deletions src/components/Header/SecondaryHeader.tsx

This file was deleted.

153 changes: 93 additions & 60 deletions src/components/Header/SubHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Box } from '@mantine/core';
import { Box, Header as MantineHeader } from '@mantine/core';
import type { FC } from 'react';
import HeaderBreadcrumbs from './HeaderBreadcrumbs';
import RefreshInterval from './RefreshInterval';
Expand All @@ -10,25 +10,46 @@ import ReloadUser from './ReloadUser';
import DocsUser from './UserDocs';
import StreamingButton from './StreamingButton';
import LiveTailFilter from './LiveTailFilter';
import Dropdown from './Dropdown';
import { useHeaderStyles } from './styles';
import { HEADER_HEIGHT } from '@/constants/theme';
import { useExportData } from '@/hooks/useExportData';

type HeaderLayoutProps = {
children: React.ReactNode;
};

const HeaderLayout: FC<HeaderLayoutProps> = (props) => {
const { classes } = useHeaderStyles();
const { container, navContainer } = classes;

return (
<MantineHeader {...props} className={container} height={HEADER_HEIGHT} p={0} withBorder zIndex={100}>
<Box className={navContainer}>{props.children}</Box>
</MantineHeader>
);
};

export const StatsHeader: FC = () => {
const { classes } = useLogQueryStyles();
const { container, innerContainer } = classes;

return (
<Box className={container}>
<Box>
<Box className={innerContainer}>
<HeaderBreadcrumbs crumbs={['Streams', 'streamName', 'Stats']} />
<HeaderLayout>
<Box className={container}>
<Box>
<Box className={innerContainer}>
<HeaderBreadcrumbs crumbs={['Streams', 'streamName', 'Stats']} />
</Box>
</Box>
</Box>

<Box>
<Box className={innerContainer}>
<RefreshNow />
<Box>
<Box className={innerContainer}>
<RefreshNow />
</Box>
</Box>
</Box>
</Box>
</HeaderLayout>
);
};

Expand All @@ -37,20 +58,22 @@ export const QueryHeader: FC = () => {
const { container, innerContainer } = classes;

return (
<Box className={container}>
<Box>
<Box className={innerContainer}>
<HeaderBreadcrumbs crumbs={['Streams', 'streamName', 'Query']} />
<HeaderLayout>
<Box className={container}>
<Box>
<Box className={innerContainer}>
<HeaderBreadcrumbs crumbs={['Streams', 'streamName', 'Query']} />
</Box>
</Box>
</Box>

<Box>
<Box className={innerContainer}>
<TimeRange />
<RefreshInterval />
<Box>
<Box className={innerContainer}>
<TimeRange />
<RefreshInterval />
</Box>
</Box>
</Box>
</Box>
</HeaderLayout>
);
};

Expand All @@ -59,48 +82,54 @@ export const LiveTailHeader: FC = () => {
const { container, innerContainer } = classes;

return (
<Box className={container}>
<Box>
<Box className={innerContainer}>
<HeaderBreadcrumbs crumbs={['Streams', 'streamName', 'Live tail']} />
<HeaderLayout>
<Box className={container}>
<Box>
<Box className={innerContainer}>
<HeaderBreadcrumbs crumbs={['Streams', 'streamName', 'Live tail']} />
</Box>
</Box>
</Box>

<Box>
<Box className={innerContainer}>
<LiveTailFilter />
<StreamingButton />
{/* <TimeRange /> */}
{/* <RefreshInterval /> */}
<Box>
<Box className={innerContainer}>
<LiveTailFilter />
<StreamingButton />
{/* <TimeRange /> */}
{/* <RefreshInterval /> */}
</Box>
</Box>
</Box>
</Box>
</HeaderLayout>
);
};

export const LogsHeader: FC = () => {
const { classes } = useLogQueryStyles();
const { container, innerContainer } = classes;
const { exportLogsHandler } = useExportData();

return (
<Box className={container}>
<Box>
<Box className={innerContainer}>
<HeaderBreadcrumbs crumbs={['Streams', 'streamName', 'Logs']} />
<HeaderLayout>
<Box className={container}>
<Box>
<Box className={innerContainer}>
<HeaderBreadcrumbs crumbs={['Streams', 'streamName', 'Logs']} />
</Box>
</Box>
</Box>

<Box>
<Box className={innerContainer}>
<Search />
<RefreshNow />
{/* <LimitLog /> */}
<Box>
<Box className={innerContainer}>
<Search />
<RefreshNow />
{/* <LimitLog /> */}

<TimeRange />
<RefreshInterval />
<TimeRange />
<RefreshInterval />
<Dropdown data={['JSON', 'CSV']} onChange={exportLogsHandler} />
</Box>
</Box>
</Box>
</Box>
</HeaderLayout>
);
};

Expand All @@ -124,19 +153,21 @@ export const UsersManagementHeader: FC = () => {
const { container, innerContainer } = classes;

return (
<Box className={container}>
<Box>
<Box className={innerContainer}>
<HeaderBreadcrumbs crumbs={['User Management']} />
<HeaderLayout>
<Box className={container}>
<Box>
<Box className={innerContainer}>
<HeaderBreadcrumbs crumbs={['User Management']} />
</Box>
</Box>
</Box>
<Box>
<Box className={innerContainer}>
<ReloadUser />
<DocsUser />
<Box>
<Box className={innerContainer}>
<ReloadUser />
<DocsUser />
</Box>
</Box>
</Box>
</Box>
</HeaderLayout>
);
};

Expand All @@ -145,12 +176,14 @@ export const AllRouteHeader: FC = () => {
const { container, innerContainer } = classes;

return (
<Box className={container}>
<Box>
<Box className={innerContainer}>
<HeaderBreadcrumbs crumbs={[]} />
<HeaderLayout>
<Box className={container}>
<Box>
<Box className={innerContainer}>
<HeaderBreadcrumbs crumbs={[]} />
</Box>
</Box>
</Box>
</Box>
</HeaderLayout>
);
};
2 changes: 0 additions & 2 deletions src/components/Header/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import PrimaryHeader from "./PrimaryHeader";
import SecondaryHeader from "./SecondaryHeader";

export {
PrimaryHeader,
SecondaryHeader
}
59 changes: 59 additions & 0 deletions src/hooks/useExportData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// import { useEffect, useState } from 'react';
import type { Log } from '@/@types/parseable/api/query';
import { useHeaderContext } from '@/layouts/MainLayout/Context';
import { useLogsPageContext } from '@/pages/Logs/Context';

type Data = Log[] | null;

const downloadDataAsJson = (data: Data, filename: string) => {
if (data === null || data.length === 0) return;

const jsonString = JSON.stringify(data, null, 2);
const blob = new Blob([jsonString], { type: 'application/json' });
const downloadLink = document.createElement('a');
downloadLink.href = URL.createObjectURL(blob);
downloadLink.download = `${filename}.json`;
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
};

const downloadDataAsCSV = (data: Data, filename: string) => {
if (data === null || data.length === 0) return;

const csvString = data
.map((row) =>
Object.values(row)
.map((value) => (value !== null ? value : ''))
.join(','),
)
.join('\n');
const blob = new Blob([csvString], { type: 'text/csv;charset=utf-8;' });
const downloadLink = document.createElement('a');
downloadLink.href = URL.createObjectURL(blob);
downloadLink.download = `${filename}.csv`;
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
};

export const useExportData = () => {
const {
state: { subLogQueryData },
} = useLogsPageContext();
const {
state: { subLogQuery },
} = useHeaderContext();
const exportLogsHandler = (type: string) => {
const { rawData, filteredData: _filteredData } = subLogQueryData.get(); // filteredData - records filtered with in-page search
const query = subLogQuery.get();
const filename = `${query.streamName}-logs`;
type === 'JSON'
? downloadDataAsJson(rawData, filename)
: type === 'CSV'
? downloadDataAsCSV(rawData, filename)
: null;
};

return { exportLogsHandler };
};
Loading

0 comments on commit 112ec3d

Please sign in to comment.