Skip to content

Commit 24c2e62

Browse files
authored
Merge branch 'main' into main
2 parents 6bfea30 + 8b5494b commit 24c2e62

File tree

33 files changed

+740
-90
lines changed

33 files changed

+740
-90
lines changed

api/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ dependencies {
6565
// CVE Fixes
6666
implementation libs.apache.commons.compress
6767
implementation libs.okhttp3.logging.intercepter
68+
implementation libs.reactor.netty.http
69+
// CVE Fixes End
6870

6971
implementation libs.modelcontextprotocol.spring.webflux
7072
implementation libs.victools.jsonschema.generator

frontend/src/components/ACLPage/List/List.styled.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ export const EnumCell = styled.div`
77
`;
88

99
export const DeleteCell = styled.div`
10-
svg {
11-
cursor: pointer;
12-
}
10+
display: flex;
1311
`;
1412

1513
export const Chip = styled.div<{

frontend/src/components/ACLPage/List/List.tsx

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import Search from 'components/common/Search/Search';
2525
import ResourcePageHeading from 'components/common/ResourcePageHeading/ResourcePageHeading';
2626
import BreakableTextCell from 'components/common/NewTable/BreakableTextCell';
2727
import { useQueryPersister } from 'components/common/NewTable/ColumnFilter';
28+
import { ActionPermissionWrapper } from 'components/common/ActionComponent';
2829

2930
import * as S from './List.styled';
3031

@@ -163,13 +164,23 @@ const ACList: React.FC = () => {
163164
// eslint-disable-next-line react/no-unstable-nested-components
164165
cell: ({ row }) => {
165166
return (
166-
<S.DeleteCell onClick={() => handleDeleteClick(row.original)}>
167-
<DeleteIcon
168-
fill={
169-
rowId === row.id ? theme.acl.table.deleteIcon : 'transparent'
170-
}
171-
/>
172-
</S.DeleteCell>
167+
<ActionPermissionWrapper
168+
onAction={() => handleDeleteClick(row.original)}
169+
permission={{
170+
resource: ResourceType.ACL,
171+
action: Action.EDIT,
172+
}}
173+
>
174+
<S.DeleteCell>
175+
<DeleteIcon
176+
fill={
177+
rowId === row.id
178+
? theme.acl.table.deleteIcon
179+
: 'transparent'
180+
}
181+
/>
182+
</S.DeleteCell>
183+
</ActionPermissionWrapper>
173184
);
174185
},
175186
size: 76,
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import React from 'react';
2+
import { LinkCell } from 'components/common/NewTable';
3+
import useAppParams from 'lib/hooks/useAppParams';
4+
import { clusterConnectConnectorPath, ClusterNameRoute } from 'lib/paths';
5+
import { CellContext } from '@tanstack/react-table';
6+
import { FullConnectorInfo } from 'generated-sources';
7+
8+
type KafkaConnectLinkCellProps = CellContext<FullConnectorInfo, string>;
9+
10+
export const KafkaConnectLinkCell = ({
11+
getValue,
12+
row,
13+
}: KafkaConnectLinkCellProps) => {
14+
const { clusterName } = useAppParams<ClusterNameRoute>();
15+
const { connect, name } = row.original;
16+
const value = getValue();
17+
18+
return (
19+
<LinkCell
20+
value={value}
21+
to={clusterConnectConnectorPath(clusterName, connect, name)}
22+
wordBreak
23+
/>
24+
);
25+
};

frontend/src/components/Connect/List/List.tsx

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
11
import React from 'react';
22
import useAppParams from 'lib/hooks/useAppParams';
3-
import { clusterConnectConnectorPath, ClusterNameRoute } from 'lib/paths';
3+
import { ClusterNameRoute } from 'lib/paths';
44
import Table, { TagCell } from 'components/common/NewTable';
55
import { FullConnectorInfo } from 'generated-sources';
66
import { useConnectors } from 'lib/hooks/api/kafkaConnect';
77
import { ColumnDef } from '@tanstack/react-table';
8-
import { useNavigate, useSearchParams } from 'react-router-dom';
9-
import BreakableTextCell from 'components/common/NewTable/BreakableTextCell';
8+
import { useSearchParams } from 'react-router-dom';
109
import { useQueryPersister } from 'components/common/NewTable/ColumnFilter';
1110
import { useLocalStoragePersister } from 'components/common/NewTable/ColumnResizer/lib';
11+
import BreakableTextCell from 'components/common/NewTable/BreakableTextCell';
1212

1313
import ActionsCell from './ActionsCell';
1414
import TopicsCell from './TopicsCell';
1515
import RunningTasksCell from './RunningTasksCell';
16+
import { KafkaConnectLinkCell } from './KafkaConnectLinkCell';
1617

17-
const kafkaConnectColumns: ColumnDef<FullConnectorInfo>[] = [
18+
const kafkaConnectColumns: ColumnDef<FullConnectorInfo, string>[] = [
1819
{
1920
header: 'Name',
2021
accessorKey: 'name',
21-
cell: BreakableTextCell,
22+
cell: KafkaConnectLinkCell,
2223
enableResizing: true,
2324
},
2425
{
@@ -77,7 +78,6 @@ const kafkaConnectColumns: ColumnDef<FullConnectorInfo>[] = [
7778
];
7879

7980
const List: React.FC = () => {
80-
const navigate = useNavigate();
8181
const { clusterName } = useAppParams<ClusterNameRoute>();
8282
const [searchParams] = useSearchParams();
8383
const { data: connectors } = useConnectors(
@@ -95,9 +95,6 @@ const List: React.FC = () => {
9595
enableSorting
9696
enableColumnResizing
9797
columnSizingPersister={columnSizingPersister}
98-
onRowClick={({ original: { connect, name } }) =>
99-
navigate(clusterConnectConnectorPath(clusterName, connect, name))
100-
}
10198
emptyMessage="No connectors found"
10299
setRowId={(originalRow) => `${originalRow.name}-${originalRow.connect}`}
103100
filterPersister={filterPersister}

frontend/src/components/Connect/List/__tests__/List.spec.tsx

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -64,18 +64,13 @@ describe('Connectors List', () => {
6464

6565
it('opens broker when row clicked', async () => {
6666
renderComponent();
67-
await userEvent.click(
68-
screen.getByRole('row', {
69-
name: 'hdfs-source-connector first SOURCE FileStreamSource a b c RUNNING 2 of 2',
70-
})
71-
);
72-
await waitFor(() =>
73-
expect(mockedUsedNavigate).toBeCalledWith(
74-
clusterConnectConnectorPath(
75-
clusterName,
76-
'first',
77-
'hdfs-source-connector'
78-
)
67+
screen.debug();
68+
expect(screen.getByText('hdfs-source-connector')).toHaveAttribute(
69+
'href',
70+
clusterConnectConnectorPath(
71+
clusterName,
72+
'first',
73+
'hdfs-source-connector'
7974
)
8075
);
8176
});

frontend/src/components/ConsumerGroups/Details/ResetOffsets/Form.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import useAppParams from 'lib/hooks/useAppParams';
2323
import { useResetConsumerGroupOffsetsMutation } from 'lib/hooks/api/consumers';
2424
import { FlexFieldset, StyledForm } from 'components/common/Form/Form.styled';
2525
import ControlledSelect from 'components/common/Select/ControlledSelect';
26+
import { useTimezone } from 'lib/hooks/useTimezones';
2627

2728
import * as S from './ResetOffsets.styled';
2829

@@ -38,6 +39,7 @@ const resetTypeOptions = Object.values(ConsumerGroupOffsetsResetType).map(
3839

3940
const Form: React.FC<FormProps> = ({ defaultValues, partitions, topics }) => {
4041
const navigate = useNavigate();
42+
const { getDateInCurrentTimezone } = useTimezone();
4143
const routerParams = useAppParams<ClusterGroupParam>();
4244
const reset = useResetConsumerGroupOffsetsMutation(routerParams);
4345
const topicOptions = React.useMemo(
@@ -142,8 +144,12 @@ const Form: React.FC<FormProps> = ({ defaultValues, partitions, topics }) => {
142144
render={({ field: { onChange, onBlur, value, ref } }) => (
143145
<S.DatePickerInput
144146
ref={ref}
145-
selected={new Date(value as number)}
146-
onChange={(e: Date | null) => onChange(e?.getTime())}
147+
selected={getDateInCurrentTimezone(
148+
new Date(value as number)
149+
)}
150+
onChange={(selectedDate: Date | null) => {
151+
onChange(selectedDate?.getTime());
152+
}}
147153
onBlur={onBlur}
148154
/>
149155
)}

frontend/src/components/NavBar/NavBar.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import ProductHuntIcon from 'components/common/Icons/ProductHuntIcon';
1212
import { Button } from 'components/common/Button/Button';
1313
import MenuIcon from 'components/common/Icons/MenuIcon';
1414

15+
import { UserTimezone } from './UserTimezone/UserTimezone';
1516
import UserInfo from './UserInfo/UserInfo';
1617
import * as S from './NavBar.styled';
1718

@@ -73,6 +74,8 @@ const NavBar: React.FC<Props> = ({ onBurgerClick }) => {
7374
</S.NavbarBrand>
7475
</S.NavbarBrand>
7576
<S.NavbarSocial>
77+
<UserTimezone />
78+
7679
<Select
7780
options={options}
7881
value={themeMode}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import styled from 'styled-components';
2+
3+
export const SelectedTimezoneContainer = styled.div`
4+
display: flex;
5+
justify-content: center;
6+
align-items: center;
7+
gap: 4px;
8+
`;
9+
10+
export const ContentContainer = styled.div`
11+
display: flex;
12+
width: 320px;
13+
flex-direction: column;
14+
max-height: 640px;
15+
gap: 8px;
16+
`;
17+
18+
export const InputContainer = styled.div`
19+
position: sticky;
20+
top: 0;
21+
background-color: white;
22+
z-index: 1;
23+
`;
24+
25+
export const ItemsContainer = styled.div`
26+
overflow-y: auto;
27+
`;
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { Button } from 'components/common/Button/Button';
2+
import ChevronDownIcon from 'components/common/Icons/ChevronDownIcon';
3+
import { Dropdown, DropdownItem } from 'components/common/Dropdown';
4+
import React, { useMemo, useState } from 'react';
5+
import Input from 'components/common/Input/Input';
6+
import { TIMEZONES, useTimezone } from 'lib/hooks/useTimezones';
7+
8+
import * as S from './UserTimezone.styled';
9+
10+
export const UserTimezone = () => {
11+
const { currentTimezone, availableTimezones, setTimezone } = useTimezone();
12+
13+
const [searchValue, setSearchValue] = useState('');
14+
15+
const filteredTimezones = useMemo(() => {
16+
if (!searchValue.trim()) return availableTimezones;
17+
18+
const searchLower = searchValue.toLowerCase();
19+
return TIMEZONES.filter(
20+
(timezone) =>
21+
timezone.value.toLowerCase().includes(searchLower) ||
22+
timezone.offset.toLowerCase().includes(searchLower)
23+
);
24+
}, [searchValue]);
25+
26+
const handleTimezoneSelect = (timezone: typeof currentTimezone) => {
27+
setTimezone(timezone);
28+
setSearchValue('');
29+
};
30+
31+
const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
32+
setSearchValue(e.target.value);
33+
};
34+
35+
return (
36+
<Dropdown
37+
onClose={() => setSearchValue('')}
38+
align="center"
39+
aria-label="user-timezone-dropdown"
40+
openBtnEl={
41+
<Button buttonType="text" buttonSize="L">
42+
<S.SelectedTimezoneContainer>
43+
<p>{currentTimezone.UTCOffset}</p>
44+
<ChevronDownIcon fill="currentColor" width="16" height="16" />
45+
</S.SelectedTimezoneContainer>
46+
</Button>
47+
}
48+
>
49+
<S.ContentContainer>
50+
<S.InputContainer>
51+
<Input
52+
id="user-timezone-search"
53+
type="text"
54+
placeholder="Search timezone..."
55+
value={searchValue}
56+
onChange={handleSearchChange}
57+
inputSize="M"
58+
search
59+
/>
60+
</S.InputContainer>
61+
62+
<S.ItemsContainer>
63+
{filteredTimezones.map((timezone) => (
64+
<DropdownItem
65+
key={timezone.value}
66+
onClick={() => handleTimezoneSelect(timezone)}
67+
>
68+
{timezone.label}
69+
</DropdownItem>
70+
))}
71+
</S.ItemsContainer>
72+
</S.ContentContainer>
73+
</Dropdown>
74+
);
75+
};

0 commit comments

Comments
 (0)