Re-style cc pair status table

This commit is contained in:
Weves
2023-10-28 19:25:50 -07:00
committed by Chris Weaver
parent 7d6a41243c
commit e744c6b75a
5 changed files with 194 additions and 174 deletions

View File

@@ -1,10 +1,9 @@
import { BasicTable } from "@/components/admin/connectors/BasicTable";
import { PopupSpec, usePopup } from "@/components/admin/connectors/Popup";
import { usePopup } from "@/components/admin/connectors/Popup";
import { useState } from "react";
import { PageSelector } from "@/components/PageSelector";
import { DocumentBoostStatus } from "@/lib/types";
import { updateBoost, updateHiddenStatus } from "../lib";
import { CheckmarkIcon, EditIcon } from "@/components/icons/icons";
import { updateHiddenStatus } from "../lib";
import { numToDisplay } from "./constants";
import { FiEye, FiEyeOff } from "react-icons/fi";
import { getErrorMsg } from "@/lib/fetchUtils";

View File

@@ -0,0 +1,146 @@
"use client";
import {
Table,
TableHead,
TableRow,
TableHeaderCell,
TableBody,
TableCell,
} from "@tremor/react";
import { CCPairStatus, IndexAttemptStatus } from "@/components/Status";
import { useState } from "react";
import { PageSelector } from "@/components/PageSelector";
import { timeAgo } from "@/lib/time";
import { ConnectorIndexingStatus } from "@/lib/types";
import { ConnectorTitle } from "@/components/admin/connectors/ConnectorTitle";
import { getDocsProcessedPerMinute } from "@/lib/indexAttempt";
import Link from "next/link";
const NUM_IN_PAGE = 20;
function CCPairIndexingStatusDisplay({
ccPairsIndexingStatus,
}: {
ccPairsIndexingStatus: ConnectorIndexingStatus<any, any>;
}) {
if (ccPairsIndexingStatus.connector.disabled) {
return (
<CCPairStatus
status="not_started"
disabled={true}
isDeleting={ccPairsIndexingStatus?.deletion_attempt !== null}
/>
);
}
const docsPerMinute = getDocsProcessedPerMinute(
ccPairsIndexingStatus.latest_index_attempt
)?.toFixed(2);
return (
<>
<IndexAttemptStatus
status={ccPairsIndexingStatus.last_status || "not_started"}
errorMsg={ccPairsIndexingStatus?.latest_index_attempt?.error_msg}
size="xs"
/>
{ccPairsIndexingStatus?.latest_index_attempt?.num_docs_indexed &&
ccPairsIndexingStatus?.latest_index_attempt?.status === "in_progress" ? (
<div className="text-xs mt-0.5">
<div>
<i>Current Run:</i>{" "}
{ccPairsIndexingStatus.latest_index_attempt.num_docs_indexed} docs
indexed
</div>
<div>
<i>Speed:</i>{" "}
{docsPerMinute ? (
<>{docsPerMinute} docs / min</>
) : (
"calculating rate..."
)}
</div>
</div>
) : null}
</>
);
}
export function CCPairIndexingStatusTable({
ccPairsIndexingStatuses,
}: {
ccPairsIndexingStatuses: ConnectorIndexingStatus<any, any>[];
}) {
const [page, setPage] = useState(1);
return (
<div className="dark">
<Table>
<TableHead>
<TableRow>
<TableHeaderCell>Connector</TableHeaderCell>
<TableHeaderCell>Status</TableHeaderCell>
<TableHeaderCell>Last Indexed</TableHeaderCell>
<TableHeaderCell>Docs Indexed</TableHeaderCell>
</TableRow>
</TableHead>
<TableBody>
{ccPairsIndexingStatuses
.slice(NUM_IN_PAGE * (page - 1), NUM_IN_PAGE * page)
.map((ccPairsIndexingStatus) => {
return (
<TableRow
key={ccPairsIndexingStatus.cc_pair_id}
className="hover:bg-gradient-to-r hover:from-gray-800 hover:to-indigo-950 cursor-pointer relative"
>
<TableCell className="whitespace-normal">
<ConnectorTitle
connector={ccPairsIndexingStatus.connector}
ccPairId={ccPairsIndexingStatus.cc_pair_id}
ccPairName={ccPairsIndexingStatus.name}
/>
</TableCell>
<TableCell>
<CCPairIndexingStatusDisplay
ccPairsIndexingStatus={ccPairsIndexingStatus}
/>
</TableCell>
<TableCell>
{timeAgo(ccPairsIndexingStatus?.last_success) || "-"}
</TableCell>
<TableCell>{ccPairsIndexingStatus.docs_indexed}</TableCell>
{/* Wrapping in <td> to avoid console warnings */}
<td className="w-0 p-0">
<Link
href={`/admin/connector/${ccPairsIndexingStatus.cc_pair_id}`}
className="absolute w-full h-full left-0"
></Link>
</td>
</TableRow>
);
})}
</TableBody>
</Table>
{ccPairsIndexingStatuses.length > NUM_IN_PAGE && (
<div className="mt-3 flex">
<div className="mx-auto">
<PageSelector
totalPages={Math.ceil(
ccPairsIndexingStatuses.length / NUM_IN_PAGE
)}
currentPage={page}
onPageChange={(newPage) => {
setPage(newPage);
window.scrollTo({
top: 0,
left: 0,
behavior: "smooth",
});
}}
/>
</div>
</div>
)}
</div>
);
}

View File

@@ -2,49 +2,13 @@
import useSWR from "swr";
import { BasicTable } from "@/components/admin/connectors/BasicTable";
import { LoadingAnimation } from "@/components/Loading";
import { timeAgo } from "@/lib/time";
import {
NotebookIcon,
QuestionIcon,
XSquareIcon,
} from "@/components/icons/icons";
import { NotebookIcon } from "@/components/icons/icons";
import { fetcher } from "@/lib/fetcher";
import { getSourceMetadata } from "@/components/source";
import { CheckCircle } from "@phosphor-icons/react";
import { HealthCheckBanner } from "@/components/health/healthcheck";
import { ConnectorIndexingStatus } from "@/lib/types";
import { useState } from "react";
import { getDocsProcessedPerMinute } from "@/lib/indexAttempt";
import { ConnectorTitle } from "@/components/admin/connectors/ConnectorTitle";
const ErrorDisplay = ({ message }: { message: string }) => {
const [isHovered, setIsHovered] = useState(false);
return (
<div
onMouseEnter={() => {
setIsHovered(true);
}}
onMouseLeave={() => setIsHovered(false)}
className="relative"
>
{isHovered && (
<div className="absolute pt-8 top-0 left-0 z-10">
<div className="bg-gray-700 px-3 pb-3 pt-2 rounded shadow-lg text-xs">
<div className="text-sm text-red-600 mb-1 flex">Error Message:</div>
{message}
</div>
</div>
)}
<div className="text-red-600 flex cursor-default">
<QuestionIcon className="my-auto mr-1" size={18} />
Error
</div>
</div>
);
};
import { CCPairIndexingStatusTable } from "./CCPairIndexingStatusTable";
import { Divider } from "@tremor/react";
function Main() {
const {
@@ -54,7 +18,7 @@ function Main() {
} = useSWR<ConnectorIndexingStatus<any, any>[]>(
"/api/manage/admin/connector/indexing-status",
fetcher,
{ refreshInterval: 30000 } // 30 seconds
{ refreshInterval: 10000 } // 10 seconds
);
if (indexAttemptIsLoading) {
@@ -77,132 +41,20 @@ function Main() {
});
return (
<BasicTable
columns={[
{ header: "Connector", key: "connector" },
{ header: "Status", key: "status" },
{ header: "Last Indexed", key: "indexed_at" },
{ header: "Docs Indexed", key: "docs_indexed" },
// { header: "Re-Index", key: "reindex" },
]}
data={indexAttemptData.map((connectorIndexingStatus) => {
const sourceMetadata = getSourceMetadata(
connectorIndexingStatus.connector.source
);
let statusDisplay = (
<div className="text-gray-400">Initializing...</div>
);
if (connectorIndexingStatus.connector.disabled) {
statusDisplay = (
<div className="text-red-600 flex">
<XSquareIcon className="my-auto mr-1" size={18} />
Disabled
</div>
);
} else if (connectorIndexingStatus.last_status === "success") {
statusDisplay = (
<div className="text-green-600 flex">
<CheckCircle className="my-auto mr-1" size="18" />
Enabled
</div>
);
} else if (connectorIndexingStatus.last_status === "failed") {
statusDisplay = (
<ErrorDisplay message={connectorIndexingStatus.error_msg} />
);
} else if (connectorIndexingStatus.last_status === "not_started") {
statusDisplay = <div className="text-gray-400">Scheduled</div>;
} else if (connectorIndexingStatus.last_status === "in_progress") {
const docsPerMinute = getDocsProcessedPerMinute(
connectorIndexingStatus.latest_index_attempt
)?.toFixed(2);
statusDisplay = (
<div className="text-gray-400">
In Progress{" "}
{connectorIndexingStatus?.latest_index_attempt
?.num_docs_indexed ? (
<div className="text-xs mt-0.5">
<div>
<i>Current Run:</i>{" "}
{
connectorIndexingStatus.latest_index_attempt
.num_docs_indexed
}{" "}
docs indexed
</div>
<div>
<i>Speed:</i>{" "}
{docsPerMinute ? (
<>~{docsPerMinute} docs / min</>
) : (
"calculating rate..."
)}
</div>
</div>
) : null}
</div>
);
}
return {
indexed_at: timeAgo(connectorIndexingStatus?.last_success) || "-",
docs_indexed: connectorIndexingStatus?.docs_indexed
? `${connectorIndexingStatus?.docs_indexed} documents`
: "-",
connector: (
<ConnectorTitle
ccPairName={connectorIndexingStatus.name}
ccPairId={connectorIndexingStatus.cc_pair_id}
connector={connectorIndexingStatus.connector}
isPublic={connectorIndexingStatus.public_doc}
owner={connectorIndexingStatus.owner}
/>
),
status: statusDisplay,
// TODO: add the below back in after this is supported in the backend
// reindex: (
// <button
// className={
// "group relative " +
// "py-1 px-2 border border-transparent text-sm " +
// "font-medium rounded-md text-white bg-red-800 " +
// "hover:bg-red-900 focus:outline-none focus:ring-2 " +
// "focus:ring-offset-2 focus:ring-red-500 mx-auto"
// }
// onClick={async () => {
// const { message, isSuccess } = await submitIndexRequest(
// connectorIndexingStatus.connector.source,
// connectorIndexingStatus.connector
// .connector_specific_config
// );
// setPopup({
// message,
// type: isSuccess ? "success" : "error",
// });
// setTimeout(() => {
// setPopup(null);
// }, 4000);
// mutate("/api/manage/admin/connector/index-attempt");
// }}
// >
// Index
// </button>
// ),
};
})}
/>
<CCPairIndexingStatusTable ccPairsIndexingStatuses={indexAttemptData} />
);
}
export default function Status() {
return (
<div className="mx-auto container">
<div className="mx-auto container dark">
<div className="mb-4">
<HealthCheckBanner />
</div>
<div className="border-solid border-gray-600 border-b pb-2 mb-4 flex">
<NotebookIcon size={32} />
<h1 className="text-3xl font-bold pl-2">Indexing Status</h1>
</div>
<h1 className="text-3xl font-bold flex gap-x-2 mb-2">
<NotebookIcon size={32} /> Indexing Status
</h1>
<Divider />
<Main />
</div>
);

View File

@@ -21,28 +21,28 @@ export const HoverPopup = ({
popupDirectionClass = "top-0 left-0 transform translate-x-[-110%]";
break;
case "bottom":
popupDirectionClass = "top-0 left-0 mt-8";
popupDirectionClass = "top-0 left-0 mt-6 pt-2";
break;
}
return (
<div
className="relative flex"
className="relative flex z-30"
onMouseEnter={() => {
setHovered(true);
console.log("HIII");
}}
onMouseLeave={() => setHovered(false)}
>
{hovered && (
<div
className={
`absolute bg-gray-700 px-3 py-2 rounded shadow-lg z-30 ` +
(classNameModifications || "") +
` ${popupDirectionClass}`
}
>
{popupContent}
<div className={`absolute ${popupDirectionClass}`}>
<div
className={
`bg-gray-800 px-3 py-2 rounded shadow-lg z-30 ` +
(classNameModifications || "")
}
>
{popupContent}
</div>
</div>
)}
{mainContent}

View File

@@ -8,34 +8,57 @@ import {
FiClock,
FiPauseCircle,
} from "react-icons/fi";
import { HoverPopup } from "./HoverPopup";
export function IndexAttemptStatus({
status,
errorMsg,
size = "md",
}: {
status: ValidStatuses;
errorMsg?: string | null;
size?: "xs" | "sm" | "md" | "lg";
}) {
let badge;
if (status === "failed") {
badge = (
const icon = (
<Badge size={size} color="red" icon={FiAlertTriangle}>
Failed
</Badge>
);
if (errorMsg) {
badge = (
<HoverPopup
mainContent={<div className="cursor-pointer">{icon}</div>}
popupContent={
<div className="flex flex-wrap whitespace-normal w-64">
{errorMsg}
</div>
}
/>
);
} else {
badge = icon;
}
} else if (status === "success") {
badge = (
<Badge size={size} color="green" icon={FiCheckCircle}>
Succeeded
</Badge>
);
} else if (status === "in_progress" || status === "not_started") {
} else if (status === "in_progress") {
badge = (
<Badge size={size} color="fuchsia" icon={FiClock}>
In Progress
</Badge>
);
} else if (status === "not_started") {
badge = (
<Badge size={size} color="fuchsia" icon={FiClock}>
Scheduled
</Badge>
);
} else {
badge = (
<Badge size={size} color="yellow" icon={FiClock}>