Skip to content

Commit a1ac925

Browse files
feat: endpoint to fetch distinct values for apps logs filter (#36245)
Co-authored-by: Martin Schoeler <martin.schoeler@rocket.chat>
1 parent 415d7c3 commit a1ac925

File tree

23 files changed

+314
-91
lines changed

23 files changed

+314
-91
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ const AppDetailsPage = ({ id }: AppDetailsPageProps): ReactElement => {
163163
</Page>
164164
{compactMode && contextualBar === 'filter-logs' && (
165165
<FormProvider {...logsFilterFormMethods}>
166-
<AppLogsFilterContextualBar onClose={handleReturnToLogs} />
166+
<AppLogsFilterContextualBar appId={id} onClose={handleReturnToLogs} />
167167
</FormProvider>
168168
)}
169169
</Page>

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

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { Box, Pagination } from '@rocket.chat/fuselage';
2-
import { useDebouncedValue } from '@rocket.chat/fuselage-hooks';
32
import { useMemo, type ReactElement } from 'react';
43
import { useTranslation } from 'react-i18next';
54

@@ -23,15 +22,13 @@ const AppLogs = ({ id }: { id: string }): ReactElement => {
2322

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

26-
const debouncedEvent = useDebouncedValue(event, 500);
27-
2825
const { data, isSuccess, isError, error, isFetching } = useLogs({
2926
appId: id,
3027
current,
3128
itemsPerPage,
3229
...(instance !== 'all' && { instanceId: instance }),
3330
...(severity !== 'all' && { logLevel: severity }),
34-
method: debouncedEvent,
31+
...(event !== 'all' && { method: event }),
3532
...(startTime && startDate && { startDate: new Date(`${startDate}T${startTime}`).toISOString() }),
3633
...(endTime && endDate && { endDate: new Date(`${endDate}T${endTime}`).toISOString() }),
3734
});
@@ -50,14 +47,14 @@ const AppLogs = ({ id }: { id: string }): ReactElement => {
5047
return (
5148
<>
5249
<Box pb={16}>
53-
<AppLogsFilter noResults={isFetching || !isSuccess || data?.logs?.length === 0} isLoading={isFetching} />
50+
<AppLogsFilter appId={id} noResults={isFetching || !isSuccess || data?.logs?.length === 0} isLoading={isFetching} />
5451
</Box>
5552
{isFetching && <AccordionLoading />}
5653
{isError && <GenericError title={parsedError} />}
5754
{!isFetching && isSuccess && data?.logs?.length === 0 && <GenericNoResults />}
5855
{!isFetching && isSuccess && data?.logs?.length > 0 && (
5956
<CustomScrollbars>
60-
<CollapsiblePanel aria-busy={isFetching || event !== debouncedEvent} width='100%' alignSelf='center'>
57+
<CollapsiblePanel aria-busy={isFetching} width='100%' alignSelf='center'>
6158
{data?.logs?.map((log, index) => <AppLogsItem regionId={log._id} key={`${index}-${log._createdAt}`} {...log} />)}
6259
</CollapsiblePanel>
6360
</CustomScrollbars>

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ export default {
1212
args: {},
1313
decorators: [
1414
mockAppRoot()
15-
// @ts-expect-error The endpoint is to be merged in https://github.com/RocketChat/Rocket.Chat/pull/36245
16-
.withEndpoint('GET', '/apps/logs/instanceIds', () => ({
15+
.withEndpoint('GET', '/apps/:id/logs/distinctValues', () => ({
1716
success: true,
1817
instanceIds: ['instance-1', 'instance-2', 'instance-3'],
18+
methods: ['method-1', 'method-2', 'method-3'],
1919
}))
2020
.buildStoryDecorator(),
2121
(fn) => {
@@ -33,4 +33,4 @@ export default {
3333
},
3434
} satisfies Meta<typeof AppLogsFilter>;
3535

36-
export const Default = () => <AppLogsFilter />;
36+
export const Default = () => <AppLogsFilter appId='app-id' />;

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

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { Box, Button, Icon, IconButton, Label, Palette, TextInput } from '@rocket.chat/fuselage';
1+
import { Box, Button, IconButton, Label } from '@rocket.chat/fuselage';
22
import { useRouter, useSetModal } from '@rocket.chat/ui-contexts';
33
import { Controller } from 'react-hook-form';
44
import { useTranslation } from 'react-i18next';
55

66
import CompactFilterOptions from './AppsLogsFilterOptionsCompact';
7+
import { EventFilterSelect } from './EventFilterSelect';
78
import { InstanceFilterSelect } from './InstanceFilterSelect';
89
import { SeverityFilterSelect } from './SeverityFilterSelect';
910
import { TimeFilterSelect } from './TimeFilterSelect';
@@ -12,11 +13,12 @@ import { useAppLogsFilterFormContext } from '../useAppLogsFilterForm';
1213
import { ExportLogsModal } from './ExportLogsModal';
1314

1415
type AppsLogsFilterProps = {
16+
appId: string;
1517
isLoading?: boolean;
1618
noResults?: boolean;
1719
};
1820

19-
export const AppLogsFilter = ({ isLoading = false, noResults = false }: AppsLogsFilterProps) => {
21+
export const AppLogsFilter = ({ appId, isLoading = false, noResults = false }: AppsLogsFilterProps) => {
2022
const { t } = useTranslation();
2123

2224
const { control, getValues } = useAppLogsFilterFormContext();
@@ -44,17 +46,13 @@ export const AppLogsFilter = ({ isLoading = false, noResults = false }: AppsLogs
4446
return (
4547
<Box display='flex' flexDirection='row' width='full' flexWrap='wrap' alignContent='flex-end'>
4648
<Box display='flex' flexDirection='column' mie={10} flexGrow={1}>
47-
<Label htmlFor='eventFilter'>{t('Event')}</Label>
49+
<Label id='eventFilterLabel' htmlFor='eventFilter'>
50+
{t('Event')}
51+
</Label>
4852
<Controller
4953
control={control}
5054
name='event'
51-
render={({ field }) => (
52-
<TextInput
53-
addon={<Icon color={Palette.text['font-secondary-info']} name='magnifier' size={20} />}
54-
id='eventFilter'
55-
{...field}
56-
/>
57-
)}
55+
render={({ field }) => <EventFilterSelect appId={appId} aria-labelledby='eventFilterLabel' id='eventFilter' {...field} />}
5856
/>
5957
</Box>
6058
{!compactMode && (
@@ -73,7 +71,9 @@ export const AppLogsFilter = ({ isLoading = false, noResults = false }: AppsLogs
7371
<Controller
7472
control={control}
7573
name='instance'
76-
render={({ field }) => <InstanceFilterSelect aria-labelledby='instanceFilterLabel' id='instanceFilter' {...field} />}
74+
render={({ field }) => (
75+
<InstanceFilterSelect appId={appId} aria-labelledby='instanceFilterLabel' id='instanceFilter' {...field} />
76+
)}
7777
/>
7878
</Box>
7979
)}

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ test.each(testCases)(`renders AppLogsItem without crashing`, async (_storyname,
1919
test.each(testCases)('AppLogsItem should have no a11y violations', async (_storyname, Story) => {
2020
const { container } = render(<Story />, { wrapper: mockAppRoot().build() });
2121

22-
const results = await axe(container);
22+
/**
23+
** Disable 'nested-interactive' rule because our `Select` component is still not a11y compliant
24+
**/
25+
const results = await axe(container, { rules: { 'nested-interactive': { enabled: false } } });
2326

2427
expect(results).toHaveNoViolations();
2528
});

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ export default {
1212
args: {},
1313
decorators: [
1414
mockAppRoot()
15-
// @ts-expect-error The endpoint is to be merged in https://github.com/RocketChat/Rocket.Chat/pull/36245
16-
.withEndpoint('GET', '/apps/logs/instanceIds', () => ({
15+
.withEndpoint('GET', '/apps/:id/logs/distinctValues', () => ({
1716
success: true,
1817
instanceIds: ['instance-1', 'instance-2', 'instance-3'],
18+
methods: ['method-1', 'method-2', 'method-3'],
1919
}))
2020
.buildStoryDecorator(),
2121
(fn) => {
@@ -33,4 +33,4 @@ export default {
3333
},
3434
} satisfies Meta<typeof AppLogsFilterContextualBar>;
3535

36-
export const Default = () => <AppLogsFilterContextualBar onClose={action('onClose')} />;
36+
export const Default = () => <AppLogsFilterContextualBar appId='app-id' onClose={action('onClose')} />;

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@ import {
1717
import { useAppLogsFilterFormContext } from '../useAppLogsFilterForm';
1818

1919
type AppLogsFilterContextualBarProps = {
20+
appId: string;
2021
onClose: () => void;
2122
};
2223

23-
export const AppLogsFilterContextualBar = ({ onClose = () => undefined }: AppLogsFilterContextualBarProps) => {
24+
export const AppLogsFilterContextualBar = ({ appId, onClose = () => undefined }: AppLogsFilterContextualBarProps) => {
2425
const { t } = useTranslation();
2526

2627
const { control } = useAppLogsFilterFormContext();
@@ -54,7 +55,9 @@ export const AppLogsFilterContextualBar = ({ onClose = () => undefined }: AppLog
5455
<Controller
5556
control={control}
5657
name='instance'
57-
render={({ field }) => <InstanceFilterSelect aria-labelledby='instanceFilterLabel' id='instanceFilter' {...field} />}
58+
render={({ field }) => (
59+
<InstanceFilterSelect appId={appId} aria-labelledby='instanceFilterLabel' id='instanceFilter' {...field} />
60+
)}
5861
/>
5962
</Box>
6063
<Box display='flex' flexDirection='column' mie={10} flexGrow={1}>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import type { SelectOption } from '@rocket.chat/fuselage';
2+
import { InputBoxSkeleton, Select } from '@rocket.chat/fuselage';
3+
import type { ComponentProps } from 'react';
4+
import { useMemo } from 'react';
5+
import { useTranslation } from 'react-i18next';
6+
7+
import { useLogsDistinctValues } from '../../../../hooks/useLogsDistinctValues';
8+
9+
type EventFilterSelectProps = Omit<ComponentProps<typeof Select>, 'options'> & { appId: string };
10+
11+
export const EventFilterSelect = ({ appId, ...props }: EventFilterSelectProps) => {
12+
const { t } = useTranslation();
13+
14+
const { data, isPending } = useLogsDistinctValues(appId);
15+
16+
const options: SelectOption[] = useMemo(() => {
17+
const mappedData: [string, string][] = data?.methods?.map((id: string) => [id, id]) || [];
18+
return [['all', t('All')], ...mappedData];
19+
}, [data, t]);
20+
21+
if (isPending) {
22+
return <InputBoxSkeleton aria-labelledby={props['aria-labelledby']} aria-busy />;
23+
}
24+
25+
return <Select options={options} {...props} />;
26+
};

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

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,19 @@
11
import type { SelectOption } from '@rocket.chat/fuselage';
22
import { InputBoxSkeleton, Select } from '@rocket.chat/fuselage';
3-
import { useEndpoint } from '@rocket.chat/ui-contexts';
4-
import { useQuery } from '@tanstack/react-query';
53
import type { ComponentProps } from 'react';
64
import { useMemo } from 'react';
75
import { useTranslation } from 'react-i18next';
86

9-
type InstanceFilterSelectProps = Omit<ComponentProps<typeof Select>, 'options'>;
7+
import { useLogsDistinctValues } from '../../../../hooks/useLogsDistinctValues';
108

11-
export const InstanceFilterSelect = ({ ...props }: InstanceFilterSelectProps) => {
9+
type InstanceFilterSelectProps = Omit<ComponentProps<typeof Select>, 'options'> & { appId: string };
10+
11+
export const InstanceFilterSelect = ({ appId, ...props }: InstanceFilterSelectProps) => {
1212
const { t } = useTranslation();
13-
// @ts-expect-error The endpoint is to be merged in https://github.com/RocketChat/Rocket.Chat/pull/36245
14-
const getOptions = useEndpoint('GET', '/apps/logs/instanceIds');
1513

16-
const { data, isPending } = useQuery({
17-
queryKey: ['app-logs-filter-instances'],
18-
// @ts-expect-error The endpoint is to be merged in https://github.com/RocketChat/Rocket.Chat/pull/36245
19-
queryFn: async () => getOptions(),
20-
});
14+
const { data, isPending } = useLogsDistinctValues(appId);
2115

2216
const options: SelectOption[] = useMemo(() => {
23-
// @ts-expect-error The endpoint is to be merged in https://github.com/RocketChat/Rocket.Chat/pull/36245
2417
const mappedData: [string, string][] = data?.instanceIds?.map((id: string) => [id, id]) || [];
2518
return [['all', t('All')], ...mappedData];
2619
}, [data, t]);

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

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,19 @@ exports[`renders AppLogsItem without crashing 1`] = `
1515
<label
1616
class="rcx-box rcx-box--full rcx-label"
1717
for="eventFilter"
18+
id="eventFilterLabel"
1819
>
1920
Event
2021
</label>
21-
<label
22-
class="rcx-box rcx-box--full rcx-label rcx-box rcx-box--full rcx-box--animated rcx-input-box__wrapper"
22+
<div
23+
aria-busy="true"
24+
aria-labelledby="eventFilterLabel"
25+
class="rcx-box rcx-box--full rcx-skeleton__input"
2326
>
24-
<input
25-
class="rcx-box rcx-box--full rcx-box--animated rcx-input-box--undecorated rcx-input-box--type-text rcx-input-box"
26-
id="eventFilter"
27-
name="event"
28-
size="1"
29-
type="text"
30-
value=""
31-
/>
3227
<span
33-
class="rcx-box rcx-box--full rcx-input-box__addon"
34-
>
35-
<i
36-
aria-hidden="true"
37-
class="rcx-box rcx-box--full rcx-icon--name-magnifier rcx-icon rcx-css-1bepdyv"
38-
>
39-
40-
</i>
41-
</span>
42-
</label>
28+
class="rcx-skeleton rcx-skeleton--text rcx-css-1qcz93u"
29+
/>
30+
</div>
4331
</div>
4432
<button
4533
class="rcx-box rcx-box--full rcx-button--secondary rcx-button rcx-css-qv9v2f"

0 commit comments

Comments
 (0)