Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/silent-zoos-notice.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@rocket.chat/meteor": minor
"@rocket.chat/i18n": minor
---

Adds 2 new buttons to the App Logs Filters, "Expand all" and "Refresh"
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { ILogItem } from '@rocket.chat/core-typings';
import { Box, Pagination } from '@rocket.chat/fuselage';
import { useMemo, type ReactElement } from 'react';
import { useEffect, useMemo, useReducer, type ReactElement } from 'react';
import { useTranslation } from 'react-i18next';

import AppLogsItem from './AppLogsItem';
Expand All @@ -13,6 +14,25 @@ import { usePagination } from '../../../../../components/GenericTable/hooks/useP
import AccordionLoading from '../../../components/AccordionLoading';
import { useLogs } from '../../../hooks/useLogs';

function expandedReducer(
expandedStates: { id: string; expanded: boolean }[],
action: { type: 'update'; id: string; expanded: boolean } | { type: 'expand-all' } | { type: 'reset'; logs: ILogItem[] },
) {
switch (action.type) {
case 'update':
return expandedStates.map((state) => (state.id === action.id ? { ...state, expanded: action.expanded } : state));

case 'expand-all':
return expandedStates.map((state) => ({ ...state, expanded: true }));

case 'reset':
return action.logs.map((log) => ({ id: log._id, expanded: false }));

default:
return expandedStates;
}
}

const AppLogs = ({ id }: { id: string }): ReactElement => {
const { t } = useTranslation();

Expand All @@ -22,7 +42,13 @@ const AppLogs = ({ id }: { id: string }): ReactElement => {

const { current, itemsPerPage, setItemsPerPage: onSetItemsPerPage, setCurrent: onSetCurrent, ...paginationProps } = usePagination();

const { data, isSuccess, isError, error, isFetching } = useLogs({
const [expandedStates, dispatch] = useReducer(expandedReducer, []);

const handleExpand = ({ id, expanded }: { id: string; expanded: boolean }) => dispatch({ id, expanded, type: 'update' });

const handleExpandAll = () => dispatch({ type: 'expand-all' });

const { data, isSuccess, isError, error, refetch, isFetching } = useLogs({
appId: id,
current,
itemsPerPage,
Expand All @@ -33,6 +59,12 @@ const AppLogs = ({ id }: { id: string }): ReactElement => {
...(endTime && endDate && { endDate: new Date(`${endDate}T${endTime}`).toISOString() }),
});

useEffect(() => {
if (isSuccess) {
dispatch({ type: 'reset', logs: data.logs });
}
}, [data, isSuccess]);

const parsedError = useMemo(() => {
if (error) {
// TODO: Check why tanstack expects a default Error but we return {error: string}
Expand All @@ -47,15 +79,29 @@ const AppLogs = ({ id }: { id: string }): ReactElement => {
return (
<>
<Box pb={16}>
<AppLogsFilter appId={id} noResults={isFetching || !isSuccess || data?.logs?.length === 0} isLoading={isFetching} />
<AppLogsFilter
appId={id}
noResults={isFetching || !isSuccess || data?.logs?.length === 0}
isLoading={isFetching}
expandAll={() => handleExpandAll()}
refetchLogs={() => refetch()}
/>
</Box>
{isFetching && <AccordionLoading />}
{isError && <GenericError title={parsedError} />}
{!isFetching && isSuccess && data?.logs?.length === 0 && <GenericNoResults />}
{!isFetching && isSuccess && data?.logs?.length > 0 && (
<CustomScrollbars>
<CollapsiblePanel aria-busy={isFetching} width='100%' alignSelf='center'>
{data?.logs?.map((log, index) => <AppLogsItem regionId={log._id} key={`${index}-${log._createdAt}`} {...log} />)}
<CollapsiblePanel width='100%' alignSelf='center'>
{data?.logs?.map((log, index) => (
<AppLogsItem
regionId={log._id}
expanded={expandedStates.find((state) => state.id === log._id)?.expanded || false}
onExpand={handleExpand}
key={`${index}-${log._createdAt}`}
{...log}
/>
))}
</CollapsiblePanel>
</CustomScrollbars>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { ILogItem } from '@rocket.chat/core-typings';
import { Box, Divider } from '@rocket.chat/fuselage';
import { useRef, useState } from 'react';
import { useRef } from 'react';
import { useTranslation } from 'react-i18next';

import AppLogsItemEntry from './AppLogsItemEntry';
Expand All @@ -11,11 +11,12 @@ import { useFormatDateAndTime } from '../../../../../hooks/useFormatDateAndTime'

export type AppLogsItemProps = {
regionId: string;
expanded: boolean;
onExpand: ({ id }: { id: string; expanded: boolean }) => void;
} & ILogItem;

const AppLogsItem = ({ regionId, ...props }: AppLogsItemProps) => {
const AppLogsItem = ({ regionId, expanded, onExpand, ...props }: AppLogsItemProps) => {
const { t } = useTranslation();
const [expanded, setExpanded] = useState(false);
const title = (
<>
{props.entries.map(({ severity, timestamp, caller, args }, index) => {
Expand All @@ -33,7 +34,7 @@ const AppLogsItem = ({ regionId, ...props }: AppLogsItemProps) => {
);

const handleClick = () => {
setExpanded(!expanded);
onExpand({ id: regionId, expanded: !expanded });
};

const anchorRef = useRef<HTMLDivElement>(null);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Box } from '@rocket.chat/fuselage';
import { mockAppRoot } from '@rocket.chat/mock-providers';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import { FormProvider } from 'react-hook-form';

Expand Down Expand Up @@ -33,4 +34,6 @@ export default {
},
} satisfies Meta<typeof AppLogsFilter>;

export const Default = () => <AppLogsFilter appId='app-id' />;
export const Default = () => (
<AppLogsFilter appId='app-id' expandAll={action('expandAll')} refetchLogs={action('refetchLogs')} isLoading={false} />
);
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useRouter, useSetModal } from '@rocket.chat/ui-contexts';
import { Controller } from 'react-hook-form';
import { useTranslation } from 'react-i18next';

import CompactFilterOptions from './AppsLogsFilterOptionsCompact';
import CompactFilterOptions from './CompactFilterOptions';
import { EventFilterSelect } from './EventFilterSelect';
import { InstanceFilterSelect } from './InstanceFilterSelect';
import { SeverityFilterSelect } from './SeverityFilterSelect';
Expand All @@ -14,11 +14,13 @@ import { ExportLogsModal } from './ExportLogsModal';

type AppsLogsFilterProps = {
appId: string;
isLoading?: boolean;
expandAll: () => void;
refetchLogs: () => void;
isLoading: boolean;
noResults?: boolean;
};

export const AppLogsFilter = ({ appId, isLoading = false, noResults = false }: AppsLogsFilterProps) => {
export const AppLogsFilter = ({ appId, expandAll, refetchLogs, isLoading, noResults = false }: AppsLogsFilterProps) => {
const { t } = useTranslation();

const { control, getValues } = useAppLogsFilterFormContext();
Expand All @@ -37,6 +39,11 @@ export const AppLogsFilter = ({ appId, isLoading = false, noResults = false }: A
);
};

const openAllLogs = () => expandAll();

const refreshLogs = () => {
refetchLogs();
};
const openExportModal = () => {
setModal(<ExportLogsModal onClose={() => setModal(null)} filterValues={getValues()} />);
};
Expand Down Expand Up @@ -83,6 +90,22 @@ export const AppLogsFilter = ({ appId, isLoading = false, noResults = false }: A
<Controller control={control} name='severity' render={({ field }) => <SeverityFilterSelect id='severityFilter' {...field} />} />
</Box>
)}
{!compactMode && (
<Button alignSelf='flex-end' icon='arrow-expand' secondary mie={10} onClick={() => openAllLogs()}>
{t('Expand_all')}
</Button>
)}
{!compactMode && (
<IconButton
title={isLoading ? t('Loading') : t('Refresh_logs')}
alignSelf='flex-end'
disabled={isLoading}
icon='refresh'
secondary
mie={10}
onClick={() => refreshLogs()}
/>
)}
{!compactMode && (
<IconButton
title={noResults ? t('No_data_to_export') : t('Export')}
Expand All @@ -101,7 +124,9 @@ export const AppLogsFilter = ({ appId, isLoading = false, noResults = false }: A
{t('Filters')}
</Button>
)}
{compactMode && <CompactFilterOptions isLoading={isLoading} handleExportLogs={openExportModal} />}
{compactMode && (
<CompactFilterOptions isLoading={isLoading} onExportLogs={openExportModal} onExpandAll={openAllLogs} onRefreshLogs={refreshLogs} />
)}
</Box>
);
};

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Box, Icon, Menu } from '@rocket.chat/fuselage';
import { useTranslation } from 'react-i18next';

type CompactFilterOptionsProps = {
onExpandAll: () => void;
onRefreshLogs: () => void;
onExportLogs: () => void;
isLoading: boolean;
};

const CompactFilterOptions = ({ onExportLogs, onExpandAll, onRefreshLogs, isLoading, ...props }: CompactFilterOptionsProps) => {
const { t } = useTranslation();

const menuOptions = {
exportLogs: {
label: (
<Box>
<Icon name='circle-arrow-down' size='x16' marginInlineEnd={4} />
{t('Export')}
</Box>
),
action: onExportLogs,
},
expandAll: {
label: (
<Box>
<Icon name='arrow-expand' size='x16' marginInlineEnd={4} />
{t('Expand_all')}
</Box>
),
action: onExpandAll,
},
refreshLogs: {
label: (
<Box>
<Icon name='refresh' size='x16' marginInlineEnd={4} />
{t('Refresh_logs')}
</Box>
),
action: onRefreshLogs,
disabled: isLoading,
},
};
return <Menu title={t('Options')} small={false} alignSelf='flex-end' options={menuOptions} {...props} />;
};

export default CompactFilterOptions;
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,34 @@ exports[`renders AppLogsItem without crashing 1`] = `
</i>
</button>
</div>
<button
class="rcx-box rcx-box--full rcx-button--secondary rcx-button rcx-css-qv9v2f"
type="button"
>
<span
class="rcx-button--content"
>
<i
aria-hidden="true"
class="rcx-box rcx-box--full rcx-icon--name-arrow-expand rcx-icon rcx-css-1hdf9ok"
>
</i>
Expand_all
</span>
</button>
<button
class="rcx-box rcx-box--full rcx-button--large-square rcx-button--icon-secondary rcx-button--square rcx-button--icon rcx-button rcx-css-qv9v2f"
title="Refresh_logs"
type="button"
>
<i
aria-hidden="true"
class="rcx-box rcx-box--full rcx-icon--name-refresh rcx-icon rcx-css-1x2l7ts"
>
</i>
</button>
<button
aria-disabled="false"
aria-label="Export"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing

exports[`renders AppLogsItem without crashing 1`] = `
<body>
Expand All @@ -12,7 +12,6 @@ exports[`renders AppLogsItem without crashing 1`] = `
style="white-space: nowrap; text-overflow: ellipsis; overflow: hidden;"
>
<button
aria-expanded="false"
class="rcx-box rcx-box--full rcx-box--focusable rcx-css-wj27h8 rcx-css-ylkbm1"
role="button"
>
Expand Down
2 changes: 2 additions & 0 deletions packages/i18n/src/locales/en.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -1998,6 +1998,7 @@
"Execute_Synchronization_Now": "Execute Synchronization Now",
"Exit_Full_Screen": "Exit Full Screen",
"Expand": "Expand",
"Expand_all": "Expand all",
"Expand_view": "Expand view",
"Experimental_Feature_Alert": "This is an experimental feature! Please be aware that it may change, break, or even be removed in the future without any notice.",
"Expiration": "Expiration",
Expand Down Expand Up @@ -4152,6 +4153,7 @@
"Redirect_URL_does_not_match": "Redirect URL does not match",
"Refresh": "Refresh",
"Refresh_keys": "Refresh keys",
"Refresh_logs": "Refresh logs",
"Refresh_oauth_services": "Refresh OAuth Services",
"Refresh_your_page_after_install_to_enable_screen_sharing": "Refresh your page after install to enable screen sharing",
"Refreshing": "Refreshing",
Expand Down
Loading