Skip to content

Commit 2c82a07

Browse files
UI: Upgrade to Next 16, React 19 and aggressive linting (#3634)
Upgrades NextJS and React, also sets in place a few scripts for linting. `next lint` has been removed in NextJS 16, so I've replaced that script with eslint directly. CI failing due to next-auth not supporting Next 16, which should be [fixed soon](nextauthjs/next-auth#13302 (comment))
1 parent 3af0833 commit 2c82a07

File tree

27 files changed

+1358
-1003
lines changed

27 files changed

+1358
-1003
lines changed

ui/.eslintignore

Lines changed: 0 additions & 1 deletion
This file was deleted.

ui/.eslintrc.json

Lines changed: 0 additions & 3 deletions
This file was deleted.

ui/app/api/v1/[...slug]/route.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ import { NextRequest } from 'next/server';
33

44
export async function GET(
55
request: NextRequest,
6-
{ params }: { params: { slug: string[] } }
6+
context: { params: Promise<{ slug: string[] }> }
77
) {
8+
const params = await context.params;
89
const flowServiceAddr = GetFlowHttpAddressFromEnv();
910
let url = `${flowServiceAddr}/v1/${params.slug
1011
.map((x) => encodeURIComponent(x))
@@ -19,8 +20,9 @@ export async function GET(
1920

2021
export async function POST(
2122
request: NextRequest,
22-
{ params }: { params: { slug: string[] } }
23+
context: { params: Promise<{ slug: string[] }> }
2324
) {
25+
const params = await context.params;
2426
const flowServiceAddr = GetFlowHttpAddressFromEnv();
2527
return fetch(
2628
`${flowServiceAddr}/v1/${params.slug
@@ -37,8 +39,9 @@ export async function POST(
3739

3840
export async function DELETE(
3941
request: NextRequest,
40-
{ params }: { params: { slug: string[] } }
42+
context: { params: Promise<{ slug: string[] }> }
4143
) {
44+
const params = await context.params;
4245
const flowServiceAddr = GetFlowHttpAddressFromEnv();
4346
let url = `${flowServiceAddr}/v1/${params.slug
4447
.map((x) => encodeURIComponent(x))

ui/app/layout.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ export const metadata: Metadata = {
66
title: 'Peerdb UI',
77
};
88

9-
export default function RootLayout({
9+
export default async function RootLayout({
1010
children,
1111
}: {
1212
children: React.ReactNode;
1313
}) {
1414
// Read theme from cookie during SSR
15-
const cookieStore = cookies();
15+
const cookieStore = await cookies();
1616
const themeCookie = cookieStore.get('peerdb-theme');
1717
const initialTheme = themeCookie?.value === 'dark' ? 'dark' : 'light';
1818
return (

ui/app/mirrors/[mirrorId]/edit/page.tsx

Lines changed: 42 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { RowWithTextField } from '@/lib/Layout';
1818
import { ProgressCircle } from '@/lib/ProgressCircle';
1919
import { TextField } from '@/lib/TextField';
2020
import { useRouter } from 'next/navigation';
21-
import { useCallback, useEffect, useMemo, useState } from 'react';
21+
import React, { useEffect, useMemo, useState } from 'react';
2222
import { ToastContainer } from 'react-toastify';
2323
import TablePicker from '../../create/cdc/tablemapping';
2424
import {
@@ -37,7 +37,7 @@ import {
3737
} from '../styles/edit.styles';
3838

3939
type EditMirrorProps = {
40-
params: { mirrorId: string };
40+
params: Promise<{ mirrorId: string }>;
4141
};
4242

4343
const defaultBatchSize = blankCDCSetting.maxBatchSize;
@@ -51,7 +51,8 @@ const defaultSnapshotMaxParallelWorkers =
5151
const defaultSnapshotNumTablesInParallel =
5252
blankCDCSetting.snapshotNumTablesInParallel;
5353

54-
export default function EditMirror({ params: { mirrorId } }: EditMirrorProps) {
54+
export default function EditMirror({ params }: EditMirrorProps) {
55+
const { mirrorId } = React.use(params);
5556
const theme = useTheme();
5657
const [rows, setRows] = useState<TableMapRow[]>([]);
5758
const [loading, setLoading] = useState(false);
@@ -71,41 +72,47 @@ export default function EditMirror({ params: { mirrorId } }: EditMirrorProps) {
7172
});
7273
const { push } = useRouter();
7374

74-
const fetchStateAndUpdateDeps = useCallback(async () => {
75-
const res = await getMirrorState(mirrorId);
76-
setMirrorState(res);
75+
useEffect(() => {
76+
const fetchStateAndUpdateDeps = async () => {
77+
try {
78+
const res = await getMirrorState(mirrorId);
79+
setMirrorState(res);
7780

78-
setConfig({
79-
batchSize:
80-
(res as MirrorStatusResponse).cdcStatus?.config?.maxBatchSize ||
81-
defaultBatchSize,
82-
idleTimeout:
83-
(res as MirrorStatusResponse).cdcStatus?.config?.idleTimeoutSeconds ||
84-
defaultIdleTimeout,
85-
additionalTables: [],
86-
removedTables: [],
87-
numberOfSyncs: 0,
88-
updatedEnv: {},
89-
snapshotNumRowsPerPartition:
90-
(res as MirrorStatusResponse).cdcStatus?.config
91-
?.snapshotNumRowsPerPartition || defaultSnapshotNumRowsPerPartition,
92-
snapshotNumPartitionsOverride:
93-
(res as MirrorStatusResponse).cdcStatus?.config
94-
?.snapshotNumPartitionsOverride ||
95-
defaultSnapshotNumPartitionsOverride,
96-
snapshotMaxParallelWorkers:
97-
(res as MirrorStatusResponse).cdcStatus?.config
98-
?.snapshotMaxParallelWorkers || defaultSnapshotMaxParallelWorkers,
99-
snapshotNumTablesInParallel:
100-
(res as MirrorStatusResponse).cdcStatus?.config
101-
?.snapshotNumTablesInParallel || defaultSnapshotNumTablesInParallel,
102-
skipInitialSnapshotForTableAdditions: false,
103-
});
104-
}, [mirrorId]);
81+
setConfig({
82+
batchSize:
83+
(res as MirrorStatusResponse).cdcStatus?.config?.maxBatchSize ||
84+
defaultBatchSize,
85+
idleTimeout:
86+
(res as MirrorStatusResponse).cdcStatus?.config
87+
?.idleTimeoutSeconds || defaultIdleTimeout,
88+
additionalTables: [],
89+
removedTables: [],
90+
numberOfSyncs: 0,
91+
updatedEnv: {},
92+
snapshotNumRowsPerPartition:
93+
(res as MirrorStatusResponse).cdcStatus?.config
94+
?.snapshotNumRowsPerPartition ||
95+
defaultSnapshotNumRowsPerPartition,
96+
snapshotNumPartitionsOverride:
97+
(res as MirrorStatusResponse).cdcStatus?.config
98+
?.snapshotNumPartitionsOverride ||
99+
defaultSnapshotNumPartitionsOverride,
100+
snapshotMaxParallelWorkers:
101+
(res as MirrorStatusResponse).cdcStatus?.config
102+
?.snapshotMaxParallelWorkers || defaultSnapshotMaxParallelWorkers,
103+
snapshotNumTablesInParallel:
104+
(res as MirrorStatusResponse).cdcStatus?.config
105+
?.snapshotNumTablesInParallel ||
106+
defaultSnapshotNumTablesInParallel,
107+
skipInitialSnapshotForTableAdditions: false,
108+
});
109+
} catch (error) {
110+
notifyErr('Failed to fetch mirror state');
111+
}
112+
};
105113

106-
useEffect(() => {
107114
fetchStateAndUpdateDeps();
108-
}, [fetchStateAndUpdateDeps]);
115+
}, [mirrorId]);
109116

110117
const alreadySelectedTablesMapping: Map<string, TableMapping[]> =
111118
useMemo(() => {

ui/app/mirrors/[mirrorId]/page.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { Badge } from '@/lib/Badge';
88
import { Header } from '@/lib/Header';
99
import { Label } from '@/lib/Label';
1010
import { LayoutMain } from '@/lib/Layout';
11-
import { useCallback, useEffect, useState } from 'react';
11+
import React, { useCallback, useEffect, useState } from 'react';
1212
import { CDCMirror } from './cdc';
1313
import { getMirrorState } from './handlers';
1414
import NoMirror from './nomirror';
@@ -17,11 +17,12 @@ import QRepStatusButtons from './qrepStatusButtons';
1717
import QRepStatusTable from './qrepStatusTable';
1818
import SyncStatus from './syncStatus';
1919

20-
type EditMirrorProps = {
21-
params: { mirrorId: string };
20+
type ViewMirrorProps = {
21+
params: Promise<{ mirrorId: string }>;
2222
};
2323

24-
export default function ViewMirror({ params: { mirrorId } }: EditMirrorProps) {
24+
export default function ViewMirror({ params }: ViewMirrorProps) {
25+
const { mirrorId } = React.use(params);
2526
const [mirrorState, setMirrorState] = useState<MirrorStatusResponse>();
2627
const [errorMessage, setErrorMessage] = useState('');
2728
const [mounted, setMounted] = useState(false);

ui/app/mirrors/[mirrorId]/qrepGraph.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
Title,
1313
Tooltip,
1414
} from 'chart.js';
15-
import { useEffect, useState } from 'react';
15+
import { useMemo, useState } from 'react';
1616
import { Bar } from 'react-chartjs-2';
1717
import ReactSelect from 'react-select';
1818
import aggregateCountsByInterval from './aggregatedCountsByInterval';
@@ -25,8 +25,7 @@ function QrepGraph({ syncs }: QRepGraphProps) {
2525
const [aggregateType, setAggregateType] = useState<TimeAggregateType>(
2626
TimeAggregateType.TIME_AGGREGATE_TYPE_ONE_HOUR
2727
);
28-
const initialCount: [string, number][] = [];
29-
const [counts, setCounts] = useState(initialCount);
28+
3029
ChartJS.register(
3130
BarElement,
3231
CategoryScale,
@@ -43,14 +42,15 @@ function QrepGraph({ syncs }: QRepGraphProps) {
4342
},
4443
},
4544
};
46-
useEffect(() => {
45+
46+
const counts = useMemo(() => {
4747
const rows = syncs.map((sync) => ({
4848
timestamp: sync.startTime!,
4949
count: Number(sync.rowsInPartition) ?? 0,
5050
}));
5151

52-
const counts = aggregateCountsByInterval(rows, aggregateType);
53-
setCounts(counts.slice(0, 29).reverse());
52+
const aggregatedCounts = aggregateCountsByInterval(rows, aggregateType);
53+
return aggregatedCounts.slice(0, 29).reverse();
5454
}, [aggregateType, syncs]);
5555

5656
const qrepData = {

ui/app/mirrors/create/cdc/cdc.tsx

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -206,19 +206,27 @@ export default function CDCConfigForm({
206206
};
207207

208208
useEffect(() => {
209-
setLoading(true);
210-
const promises = [];
211-
if (sourceType.toString() === DBType[DBType.POSTGRES]) {
212-
promises.push(
213-
fetchPublications(mirrorConfig.sourceName ?? '').then((pubs) =>
214-
setPublications(pubs)
215-
)
216-
);
217-
}
218-
promises.push(getScriptingEnabled());
219-
Promise.all(promises).then(() => setLoading(false));
220-
// ClickHouse has soft-delete as default and hard-delete as opt-in.
221-
// It is the opposite for SF,BQ and PQ target peers.
209+
const fetchData = async () => {
210+
setLoading(true);
211+
212+
try {
213+
const promises = [];
214+
if (sourceType.toString() === DBType[DBType.POSTGRES]) {
215+
promises.push(
216+
fetchPublications(mirrorConfig.sourceName ?? '').then((pubs) =>
217+
setPublications(pubs)
218+
)
219+
);
220+
}
221+
promises.push(getScriptingEnabled());
222+
223+
await Promise.all(promises);
224+
} finally {
225+
setLoading(false);
226+
}
227+
};
228+
229+
// Set soft delete configuration synchronously
222230
if (IsClickHousePeer(destinationType)) {
223231
setter((curr) => ({
224232
...curr,
@@ -230,6 +238,9 @@ export default function CDCConfigForm({
230238
softDeleteColName: curr.softDeleteColName || SOFT_DELETE_COLUMN_NAME,
231239
}));
232240
}
241+
242+
// Fetch data asynchronously
243+
fetchData();
233244
}, [
234245
sourceType,
235246
mirrorConfig.sourceName,

ui/app/mirrors/create/cdc/customColumnType.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,6 @@ export default function CustomColumnType({
4949
const columnsWithDstTypes = selectedColumns.filter(
5050
(col) => destinationTypeMapping[col.name] !== undefined
5151
);
52-
if (columnsWithDstTypes.length === 0) {
53-
setUseCustom(false);
54-
}
5552
return columnsWithDstTypes;
5653
}, [selectedColumns, destinationTypeMapping]);
5754

ui/app/mirrors/create/cdc/schemabox.tsx

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -204,15 +204,18 @@ export default function SchemaBox({
204204
};
205205

206206
const fetchTablesForSchema = useCallback(
207-
(schemaName: string) => {
207+
async (schemaName: string) => {
208208
setTablesLoading(true);
209-
fetchTables(
210-
sourcePeer,
211-
schemaName,
212-
defaultTargetSchema,
213-
peerType,
214-
initialLoadOnly
215-
).then((newRows) => {
209+
210+
try {
211+
const newRows = await fetchTables(
212+
sourcePeer,
213+
schemaName,
214+
defaultTargetSchema,
215+
peerType,
216+
initialLoadOnly
217+
);
218+
216219
if (alreadySelectedTables) {
217220
for (const row of newRows) {
218221
const existingRow = alreadySelectedTables.find(
@@ -232,23 +235,28 @@ export default function SchemaBox({
232235
}
233236
}
234237
}
238+
235239
setRows((oldRows) => {
236240
const filteredRows = oldRows.filter(
237241
(oldRow) => oldRow.schema !== schemaName
238242
);
239243
return [...filteredRows, ...newRows];
240244
});
245+
} catch (error) {
246+
// Handle error if needed
247+
console.error('Error fetching tables:', error);
248+
} finally {
241249
setTablesLoading(false);
242-
});
250+
}
243251
},
244252
[
245-
setRows,
246253
sourcePeer,
247254
defaultTargetSchema,
248255
peerType,
249256
alreadySelectedTables,
250257
addTableColumns,
251258
initialLoadOnly,
259+
setRows,
252260
]
253261
);
254262

0 commit comments

Comments
 (0)