diff --git a/web/vtadmin/src/api/http.ts b/web/vtadmin/src/api/http.ts
index 6f85a82e78d..3f75330d240 100644
--- a/web/vtadmin/src/api/http.ts
+++ b/web/vtadmin/src/api/http.ts
@@ -421,6 +421,20 @@ export const fetchVSchema = async ({ clusterID, keyspace }: FetchVSchemaParams)
return pb.VSchema.create(result);
};
+export interface FetchTransactionParams {
+ clusterID: string;
+ dtid: string;
+}
+
+export const fetchTransaction = async ({ clusterID, dtid }: FetchTransactionParams) => {
+ const { result } = await vtfetch(`/api/transaction/${clusterID}/${dtid}/info`);
+
+ const err = vtctldata.GetTransactionInfoResponse.verify(result);
+ if (err) throw Error(err);
+
+ return vtctldata.GetTransactionInfoResponse.create(result);
+};
+
export interface FetchTransactionsParams {
clusterID: string;
keyspace: string;
diff --git a/web/vtadmin/src/components/App.tsx b/web/vtadmin/src/components/App.tsx
index 505ea7e64eb..ef27a35dc95 100644
--- a/web/vtadmin/src/components/App.tsx
+++ b/web/vtadmin/src/components/App.tsx
@@ -42,6 +42,7 @@ import { Topology } from './routes/topology/Topology';
import { ClusterTopology } from './routes/topology/ClusterTopology';
import { CreateMoveTables } from './routes/createWorkflow/CreateMoveTables';
import { Transactions } from './routes/Transactions';
+import { Transaction } from './routes/transaction/Transaction';
import { CreateReshard } from './routes/createWorkflow/CreateReshard';
import { CreateMaterialize } from './routes/createWorkflow/CreateMaterialize';
@@ -143,6 +144,10 @@ export const App = () => {
+
+
+
+
diff --git a/web/vtadmin/src/components/links/TransactionLink.tsx b/web/vtadmin/src/components/links/TransactionLink.tsx
new file mode 100644
index 00000000000..32d8b4a94bd
--- /dev/null
+++ b/web/vtadmin/src/components/links/TransactionLink.tsx
@@ -0,0 +1,46 @@
+/**
+ * Copyright 2024 The Vitess Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react';
+import { Link } from 'react-router-dom';
+
+interface Props {
+ className?: string;
+ clusterID: string | null | undefined;
+ dtid: string | null | undefined;
+}
+
+export const TransactionLink: React.FunctionComponent = ({
+ children,
+ className,
+ clusterID,
+ dtid,
+ ...props
+}) => {
+ if (!clusterID || !dtid) {
+ return {children};
+ }
+
+ const to = {
+ pathname: `/transaction/${clusterID}/${dtid}`,
+ };
+
+ return (
+
+ {children}
+
+ );
+};
diff --git a/web/vtadmin/src/components/routes/Transactions.tsx b/web/vtadmin/src/components/routes/Transactions.tsx
index 10945403596..5f1745b1de1 100644
--- a/web/vtadmin/src/components/routes/Transactions.tsx
+++ b/web/vtadmin/src/components/routes/Transactions.tsx
@@ -32,9 +32,11 @@ import { orderBy } from 'lodash-es';
import { ReadOnlyGate } from '../ReadOnlyGate';
import TransactionActions from './transactions/TransactionActions';
import { isReadOnlyMode } from '../../util/env';
+import {TransactionLink} from "../links/TransactionLink";
+import * as React from "react";
-const COLUMNS = ['ID', 'State', 'Participants', 'Time Created', 'Actions'];
-const READ_ONLY_COLUMNS = ['ID', 'State', 'Participants', 'Time Created'];
+export const COLUMNS = ['ID', 'State', 'Participants', 'Time Created', 'Actions'];
+export const READ_ONLY_COLUMNS = ['ID', 'State', 'Participants', 'Time Created'];
const ABANDON_AGE_OPTIONS = [
{
@@ -90,7 +92,9 @@ export const Transactions = () => {
return (
-
{row.dtid}
+
+
{row.dtid}
+
{formatTransactionState(row)}
diff --git a/web/vtadmin/src/components/routes/transaction/Transaction.tsx b/web/vtadmin/src/components/routes/transaction/Transaction.tsx
new file mode 100644
index 00000000000..a4f04bd629b
--- /dev/null
+++ b/web/vtadmin/src/components/routes/transaction/Transaction.tsx
@@ -0,0 +1,185 @@
+/**
+ * Copyright 2024 The Vitess Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { useTransaction } from '../../../hooks/api';
+import { DataCell } from '../../dataTable/DataCell';
+import { DataTable } from '../../dataTable/DataTable';
+import { ContentContainer } from '../../layout/ContentContainer';
+import { WorkspaceHeader } from '../../layout/WorkspaceHeader';
+import { WorkspaceTitle } from '../../layout/WorkspaceTitle';
+import { QueryLoadingPlaceholder } from '../../placeholders/QueryLoadingPlaceholder';
+import { useDocumentTitle } from '../../../hooks/useDocumentTitle';
+import { query, vtctldata} from '../../../proto/vtadmin';
+import { formatTransactionState } from '../../../util/transactions';
+import { ShardLink } from '../../links/ShardLink';
+import { formatDateTime, formatRelativeTimeInSeconds } from '../../../util/time';
+import { ReadOnlyGate } from '../../ReadOnlyGate';
+import TransactionActions from '../transactions/TransactionActions';
+import { isReadOnlyMode } from '../../../util/env';
+import {useParams} from "react-router";
+import style from "../keyspace/Keyspace.module.scss";
+import {Link} from "react-router-dom";
+import {NavCrumbs} from "../../layout/NavCrumbs";
+import {READ_ONLY_COLUMNS} from "../Transactions"
+import {COLUMNS} from "../Transactions"
+import * as React from "react";
+import {TransactionLink} from "../../links/TransactionLink";
+
+interface RouteParams {
+ clusterID: string;
+ dtid: string;
+}
+
+export const SHARD_STATE_COLUMNS = ['Shard', 'State', 'Message', 'Time Created', 'Statements'];
+
+export const Transaction = () => {
+ const { clusterID, dtid } = useParams();
+
+ useDocumentTitle(`${dtid} (${clusterID})`);
+
+ const transactionQuery = useTransaction({ clusterID, dtid });
+
+ const { data: transaction } = transactionQuery;
+
+ if (transactionQuery.error) {
+ return (
+
+ 😰
+
An error occurred
+ {(transactionQuery.error as any).response?.error?.message || transactionQuery.error?.message}
+