Skip to content

Commit 208fe33

Browse files
MartinSchoelerd-guberttassoevan
authored
feat: New reload and expand all buttons in App Logs view (#36413)
Co-authored-by: Douglas Gubert <douglas.gubert@gmail.com> Co-authored-by: Tasso <tasso.evangelista@rocket.chat>
1 parent 34c5f88 commit 208fe33

File tree

10 files changed

+173
-42
lines changed

10 files changed

+173
-42
lines changed

.changeset/silent-zoos-notice.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@rocket.chat/meteor": minor
3+
"@rocket.chat/i18n": minor
4+
---
5+
6+
Adds 2 new buttons to the App Logs Filters, "Expand all" and "Refresh"

apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppLogs/AppLogs.tsx

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
import type { ILogItem } from '@rocket.chat/core-typings';
12
import { Box, Pagination } from '@rocket.chat/fuselage';
2-
import { useMemo, type ReactElement } from 'react';
3+
import { useEffect, useMemo, useReducer, type ReactElement } from 'react';
34
import { useTranslation } from 'react-i18next';
45

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

17+
function expandedReducer(
18+
expandedStates: { id: string; expanded: boolean }[],
19+
action: { type: 'update'; id: string; expanded: boolean } | { type: 'expand-all' } | { type: 'reset'; logs: ILogItem[] },
20+
) {
21+
switch (action.type) {
22+
case 'update':
23+
return expandedStates.map((state) => (state.id === action.id ? { ...state, expanded: action.expanded } : state));
24+
25+
case 'expand-all':
26+
return expandedStates.map((state) => ({ ...state, expanded: true }));
27+
28+
case 'reset':
29+
return action.logs.map((log) => ({ id: log._id, expanded: false }));
30+
31+
default:
32+
return expandedStates;
33+
}
34+
}
35+
1636
const AppLogs = ({ id }: { id: string }): ReactElement => {
1737
const { t } = useTranslation();
1838

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

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

25-
const { data, isSuccess, isError, error, isFetching } = useLogs({
45+
const [expandedStates, dispatch] = useReducer(expandedReducer, []);
46+
47+
const handleExpand = ({ id, expanded }: { id: string; expanded: boolean }) => dispatch({ id, expanded, type: 'update' });
48+
49+
const handleExpandAll = () => dispatch({ type: 'expand-all' });
50+
51+
const { data, isSuccess, isError, error, refetch, isFetching } = useLogs({
2652
appId: id,
2753
current,
2854
itemsPerPage,
@@ -33,6 +59,12 @@ const AppLogs = ({ id }: { id: string }): ReactElement => {
3359
...(endTime && endDate && { endDate: new Date(`${endDate}T${endTime}`).toISOString() }),
3460
});
3561

62+
useEffect(() => {
63+
if (isSuccess) {
64+
dispatch({ type: 'reset', logs: data.logs });
65+
}
66+
}, [data, isSuccess]);
67+
3668
const parsedError = useMemo(() => {
3769
if (error) {
3870
// TODO: Check why tanstack expects a default Error but we return {error: string}
@@ -47,15 +79,29 @@ const AppLogs = ({ id }: { id: string }): ReactElement => {
4779
return (
4880
<>
4981
<Box pb={16}>
50-
<AppLogsFilter appId={id} noResults={isFetching || !isSuccess || data?.logs?.length === 0} isLoading={isFetching} />
82+
<AppLogsFilter
83+
appId={id}
84+
noResults={isFetching || !isSuccess || data?.logs?.length === 0}
85+
isLoading={isFetching}
86+
expandAll={() => handleExpandAll()}
87+
refetchLogs={() => refetch()}
88+
/>
5189
</Box>
5290
{isFetching && <AccordionLoading />}
5391
{isError && <GenericError title={parsedError} />}
5492
{!isFetching && isSuccess && data?.logs?.length === 0 && <GenericNoResults />}
5593
{!isFetching && isSuccess && data?.logs?.length > 0 && (
5694
<CustomScrollbars>
57-
<CollapsiblePanel aria-busy={isFetching} width='100%' alignSelf='center'>
58-
{data?.logs?.map((log, index) => <AppLogsItem regionId={log._id} key={`${index}-${log._createdAt}`} {...log} />)}
95+
<CollapsiblePanel width='100%' alignSelf='center'>
96+
{data?.logs?.map((log, index) => (
97+
<AppLogsItem
98+
regionId={log._id}
99+
expanded={expandedStates.find((state) => state.id === log._id)?.expanded || false}
100+
onExpand={handleExpand}
101+
key={`${index}-${log._createdAt}`}
102+
{...log}
103+
/>
104+
))}
59105
</CollapsiblePanel>
60106
</CustomScrollbars>
61107
)}

apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppLogs/AppLogsItem.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { ILogItem } from '@rocket.chat/core-typings';
22
import { Box, Divider } from '@rocket.chat/fuselage';
3-
import { useRef, useState } from 'react';
3+
import { useRef } from 'react';
44
import { useTranslation } from 'react-i18next';
55

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

1212
export type AppLogsItemProps = {
1313
regionId: string;
14+
expanded: boolean;
15+
onExpand: ({ id }: { id: string; expanded: boolean }) => void;
1416
} & ILogItem;
1517

16-
const AppLogsItem = ({ regionId, ...props }: AppLogsItemProps) => {
18+
const AppLogsItem = ({ regionId, expanded, onExpand, ...props }: AppLogsItemProps) => {
1719
const { t } = useTranslation();
18-
const [expanded, setExpanded] = useState(false);
1920
const title = (
2021
<>
2122
{props.entries.map(({ severity, timestamp, caller, args }, index) => {
@@ -33,7 +34,7 @@ const AppLogsItem = ({ regionId, ...props }: AppLogsItemProps) => {
3334
);
3435

3536
const handleClick = () => {
36-
setExpanded(!expanded);
37+
onExpand({ id: regionId, expanded: !expanded });
3738
};
3839

3940
const anchorRef = useRef<HTMLDivElement>(null);

apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppLogs/Filters/AppLogsFilter.stories.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Box } from '@rocket.chat/fuselage';
22
import { mockAppRoot } from '@rocket.chat/mock-providers';
3+
import { action } from '@storybook/addon-actions';
34
import type { Meta } from '@storybook/react';
45
import { FormProvider } from 'react-hook-form';
56

@@ -33,4 +34,6 @@ export default {
3334
},
3435
} satisfies Meta<typeof AppLogsFilter>;
3536

36-
export const Default = () => <AppLogsFilter appId='app-id' />;
37+
export const Default = () => (
38+
<AppLogsFilter appId='app-id' expandAll={action('expandAll')} refetchLogs={action('refetchLogs')} isLoading={false} />
39+
);

apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppLogs/Filters/AppLogsFilter.tsx

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { useRouter, useSetModal } from '@rocket.chat/ui-contexts';
33
import { Controller } from 'react-hook-form';
44
import { useTranslation } from 'react-i18next';
55

6-
import CompactFilterOptions from './AppsLogsFilterOptionsCompact';
6+
import CompactFilterOptions from './CompactFilterOptions';
77
import { EventFilterSelect } from './EventFilterSelect';
88
import { InstanceFilterSelect } from './InstanceFilterSelect';
99
import { SeverityFilterSelect } from './SeverityFilterSelect';
@@ -14,11 +14,13 @@ import { ExportLogsModal } from './ExportLogsModal';
1414

1515
type AppsLogsFilterProps = {
1616
appId: string;
17-
isLoading?: boolean;
17+
expandAll: () => void;
18+
refetchLogs: () => void;
19+
isLoading: boolean;
1820
noResults?: boolean;
1921
};
2022

21-
export const AppLogsFilter = ({ appId, isLoading = false, noResults = false }: AppsLogsFilterProps) => {
23+
export const AppLogsFilter = ({ appId, expandAll, refetchLogs, isLoading, noResults = false }: AppsLogsFilterProps) => {
2224
const { t } = useTranslation();
2325

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

42+
const openAllLogs = () => expandAll();
43+
44+
const refreshLogs = () => {
45+
refetchLogs();
46+
};
4047
const openExportModal = () => {
4148
setModal(<ExportLogsModal onClose={() => setModal(null)} filterValues={getValues()} />);
4249
};
@@ -83,6 +90,22 @@ export const AppLogsFilter = ({ appId, isLoading = false, noResults = false }: A
8390
<Controller control={control} name='severity' render={({ field }) => <SeverityFilterSelect id='severityFilter' {...field} />} />
8491
</Box>
8592
)}
93+
{!compactMode && (
94+
<Button alignSelf='flex-end' icon='arrow-expand' secondary mie={10} onClick={() => openAllLogs()}>
95+
{t('Expand_all')}
96+
</Button>
97+
)}
98+
{!compactMode && (
99+
<IconButton
100+
title={isLoading ? t('Loading') : t('Refresh_logs')}
101+
alignSelf='flex-end'
102+
disabled={isLoading}
103+
icon='refresh'
104+
secondary
105+
mie={10}
106+
onClick={() => refreshLogs()}
107+
/>
108+
)}
86109
{!compactMode && (
87110
<IconButton
88111
title={noResults ? t('No_data_to_export') : t('Export')}
@@ -101,7 +124,9 @@ export const AppLogsFilter = ({ appId, isLoading = false, noResults = false }: A
101124
{t('Filters')}
102125
</Button>
103126
)}
104-
{compactMode && <CompactFilterOptions isLoading={isLoading} handleExportLogs={openExportModal} />}
127+
{compactMode && (
128+
<CompactFilterOptions isLoading={isLoading} onExportLogs={openExportModal} onExpandAll={openAllLogs} onRefreshLogs={refreshLogs} />
129+
)}
105130
</Box>
106131
);
107132
};

apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppLogs/Filters/AppsLogsFilterOptionsCompact.tsx

Lines changed: 0 additions & 26 deletions
This file was deleted.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { Box, Icon, Menu } from '@rocket.chat/fuselage';
2+
import { useTranslation } from 'react-i18next';
3+
4+
type CompactFilterOptionsProps = {
5+
onExpandAll: () => void;
6+
onRefreshLogs: () => void;
7+
onExportLogs: () => void;
8+
isLoading: boolean;
9+
};
10+
11+
const CompactFilterOptions = ({ onExportLogs, onExpandAll, onRefreshLogs, isLoading, ...props }: CompactFilterOptionsProps) => {
12+
const { t } = useTranslation();
13+
14+
const menuOptions = {
15+
exportLogs: {
16+
label: (
17+
<Box>
18+
<Icon name='circle-arrow-down' size='x16' marginInlineEnd={4} />
19+
{t('Export')}
20+
</Box>
21+
),
22+
action: onExportLogs,
23+
},
24+
expandAll: {
25+
label: (
26+
<Box>
27+
<Icon name='arrow-expand' size='x16' marginInlineEnd={4} />
28+
{t('Expand_all')}
29+
</Box>
30+
),
31+
action: onExpandAll,
32+
},
33+
refreshLogs: {
34+
label: (
35+
<Box>
36+
<Icon name='refresh' size='x16' marginInlineEnd={4} />
37+
{t('Refresh_logs')}
38+
</Box>
39+
),
40+
action: onRefreshLogs,
41+
disabled: isLoading,
42+
},
43+
};
44+
return <Menu title={t('Options')} small={false} alignSelf='flex-end' options={menuOptions} {...props} />;
45+
};
46+
47+
export default CompactFilterOptions;

apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppLogs/Filters/__snapshots__/AppLogsFilterExpanded.spec.tsx.snap

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,34 @@ exports[`renders AppLogsItem without crashing 1`] = `
204204
</i>
205205
</button>
206206
</div>
207+
<button
208+
class="rcx-box rcx-box--full rcx-button--secondary rcx-button rcx-css-qv9v2f"
209+
type="button"
210+
>
211+
<span
212+
class="rcx-button--content"
213+
>
214+
<i
215+
aria-hidden="true"
216+
class="rcx-box rcx-box--full rcx-icon--name-arrow-expand rcx-icon rcx-css-1hdf9ok"
217+
>
218+
219+
</i>
220+
Expand_all
221+
</span>
222+
</button>
223+
<button
224+
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"
225+
title="Refresh_logs"
226+
type="button"
227+
>
228+
<i
229+
aria-hidden="true"
230+
class="rcx-box rcx-box--full rcx-icon--name-refresh rcx-icon rcx-css-1x2l7ts"
231+
>
232+
233+
</i>
234+
</button>
207235
<button
208236
aria-disabled="false"
209237
aria-label="Export"

apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppLogs/__snapshots__/AppLogsItem.spec.tsx.snap

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Jest Snapshot v1, https://goo.gl/fbAQLP
1+
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
22

33
exports[`renders AppLogsItem without crashing 1`] = `
44
<body>
@@ -12,7 +12,6 @@ exports[`renders AppLogsItem without crashing 1`] = `
1212
style="white-space: nowrap; text-overflow: ellipsis; overflow: hidden;"
1313
>
1414
<button
15-
aria-expanded="false"
1615
class="rcx-box rcx-box--full rcx-box--focusable rcx-css-wj27h8 rcx-css-ylkbm1"
1716
role="button"
1817
>

packages/i18n/src/locales/en.i18n.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1998,6 +1998,7 @@
19981998
"Execute_Synchronization_Now": "Execute Synchronization Now",
19991999
"Exit_Full_Screen": "Exit Full Screen",
20002000
"Expand": "Expand",
2001+
"Expand_all": "Expand all",
20012002
"Expand_view": "Expand view",
20022003
"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.",
20032004
"Expiration": "Expiration",
@@ -4152,6 +4153,7 @@
41524153
"Redirect_URL_does_not_match": "Redirect URL does not match",
41534154
"Refresh": "Refresh",
41544155
"Refresh_keys": "Refresh keys",
4156+
"Refresh_logs": "Refresh logs",
41554157
"Refresh_oauth_services": "Refresh OAuth Services",
41564158
"Refresh_your_page_after_install_to_enable_screen_sharing": "Refresh your page after install to enable screen sharing",
41574159
"Refreshing": "Refreshing",

0 commit comments

Comments
 (0)