diff --git a/tools/mothership/admin/ui/Entries.tsx b/tools/mothership/admin/ui/Entries.tsx
new file mode 100644
index 00000000..a568df38
--- /dev/null
+++ b/tools/mothership/admin/ui/Entries.tsx
@@ -0,0 +1,46 @@
+/**
+ * Copyright 2023 Peridot 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 * as React from 'react';
+
+import Box from '@mui/material/Box';
+
+import { ResourceTable } from 'base/ts/mui/ResourceTable';
+import { srpmArchiverApi } from 'tools/mothership/admin/ui/api';
+import {
+ V1ListEntriesResponse,
+ V1Entry,
+} from 'bazel-bin/tools/mothership/proto/v1/mothershippb_ts_proto_gen';
+import { reqap } from 'base/ts/reqap';
+
+export const Entries = () => {
+ return (
+
+
+ load={(pageSize: number, pageToken?: string) => reqap(srpmArchiverApi.listEntries({
+ pageSize: pageSize,
+ pageToken: pageToken,
+ }))}
+ transform={((response: V1ListEntriesResponse) => response.entries || [])}
+ fields={[
+ { key: 'name', label: 'Entry Name' },
+ { key: 'entryId', label: 'Entry ID' },
+ { key: 'state', label: 'State' },
+ ]}
+ />
+
+ );
+};
diff --git a/tools/mothership/admin/ui/GetEntry.tsx b/tools/mothership/admin/ui/GetEntry.tsx
new file mode 100644
index 00000000..c98d2790
--- /dev/null
+++ b/tools/mothership/admin/ui/GetEntry.tsx
@@ -0,0 +1,110 @@
+/**
+ * Copyright 2023 Peridot 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 { useParams } from 'react-router-dom';
+
+import { ResourceView } from 'base/ts/mui/ResourceView';
+import { reqap } from 'base/ts/reqap';
+
+import {
+ EntryState,
+ V1Entry,
+} from 'bazel-bin/tools/mothership/proto/v1/mothershippb_ts_proto_gen';
+import Box from '@mui/material/Box';
+import Divider from '@mui/material/Divider';
+import { mshipAdminApi, srpmArchiverApi } from 'tools/mothership/admin/ui/api';
+import Button from '@mui/material/Button';
+
+export const GetEntry = () => {
+ const params = useParams();
+ const [resource, setResource] = React.useState(
+ undefined,
+ );
+
+ // Load the resource
+ React.useEffect(() => {
+ (async () => {
+ const [res, err] = await reqap(
+ srpmArchiverApi.getEntry({
+ name1: `entries/${params.name}`,
+ }),
+ );
+
+ if (err) {
+ setResource(null);
+ return;
+ }
+
+ setResource(res);
+ })().then();
+ }, []);
+
+ // Rescue the entry (call API)
+ const rescueEntry = async () => {
+ const [res, err] = await reqap(
+ mshipAdminApi.rescueEntryImport({
+ name: `entries/${params.name}`,
+ }),
+ );
+
+ if (err) {
+ return;
+ }
+
+ window.location.reload();
+ }
+
+ return (
+
+
+ entries/{params.name}
+ {resource && resource.state == EntryState.OnHold && }
+
+
+
+
+
+
+ );
+};