diff --git a/web/src/components/LogTable.tsx b/web/src/components/LogTable.tsx new file mode 100644 index 00000000..70a09ca4 --- /dev/null +++ b/web/src/components/LogTable.tsx @@ -0,0 +1,122 @@ +/* === This file is part of bxt === + * + * SPDX-FileCopyrightText: 2024 Artem Grinev + * SPDX-License-Identifier: AGPL-3.0-or-later + * + */ + +import React from "react"; +import { Table } from "react-daisyui"; +import { + useReactTable, + flexRender, + getCoreRowModel, + getPaginationRowModel, + getSortedRowModel, + SortingState, + createColumnHelper +} from "@tanstack/react-table"; +import SectionLabel from "../components/SectionLabel"; + +interface LogTableProps { + entries: LogEntry[]; + sorting: SortingState; + setSorting: React.Dispatch>; +} + +const LogTable = ({ entries, sorting, setSorting }: LogTableProps) => { + const columnHelper = createColumnHelper(); + + const columns = [ + columnHelper.accessor("type", { + header: "Type" + }), + columnHelper.accessor("package.name", { + header: "Name" + }), + + columnHelper.accessor("package.poolEntries", { + header: "Version", + cell: (context) => { + const value = context.getValue(); + const preferredLocation = + context?.row?.original?.package?.preferredLocation; + + if (value && preferredLocation && value[preferredLocation]) { + return value[preferredLocation].version; + } else { + return "Unknown Version"; + } + } + }), + + columnHelper.accessor("package.section", { + header: "Section", + cell: (context) => + }), + + columnHelper.accessor((entry) => entry.time, { + header: "Time", + enableSorting: true, + sortingFn: "datetime" + }) + ]; + + const table = useReactTable({ + columns, + data: entries, + state: { + sorting + }, + manualPagination: true, + sortDescFirst: true, + enableSorting: true, + onSortingChange: setSorting, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + getSortedRowModel: getSortedRowModel(), + initialState: {} + }); + + return ( + + + {table + .getHeaderGroups() + .flatMap((headerGroup) => + headerGroup.headers.map((header) => ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} + + )) + )} + + + + {table.getRowModel().rows.map((row) => { + return ( + + {row.getVisibleCells().map((cell) => { + return ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} + + ); + })} + + ); + })} + +
+ ); +}; + +export default LogTable; diff --git a/web/src/components/RootDrawerLayout.tsx b/web/src/components/RootDrawerLayout.tsx index e531733c..516b69b1 100644 --- a/web/src/components/RootDrawerLayout.tsx +++ b/web/src/components/RootDrawerLayout.tsx @@ -44,7 +44,7 @@ export default function RootDrawerLayout() { () => [ { route: "/", name: "Packages", icon: faFolderTree }, { route: "/compare", name: "Compare", icon: faCodeCompare }, - { route: "/logs", name: "Logs", icon: faListCheck }, + { route: "/log", name: "Log", icon: faListCheck }, { route: "/admin", name: "Admin", icon: faToolbox } ], [] diff --git a/web/src/hooks/BxtHooks.ts b/web/src/hooks/BxtHooks.ts index e708e9a0..bca7074f 100644 --- a/web/src/hooks/BxtHooks.ts +++ b/web/src/hooks/BxtHooks.ts @@ -28,10 +28,12 @@ export const useSections = (): [Section[], IUpdateSections] => { return [sections, updateSections]; }; -export const usePackageLogs = (): [LogEntry[], () => void] => { - const [entries, setEntries] = useState([]); +export const usePackageLogs = (): [LogEntry[] | null, () => void, boolean] => { + const [entries, setEntries] = useState(null); + const [isLoading, setIsLoading] = useState(false); const updateEntries = useCallback(async () => { + setIsLoading(true); try { const result = await axios.get(`/api/logs/packages`); @@ -40,10 +42,13 @@ export const usePackageLogs = (): [LogEntry[], () => void] => { return value; }); setEntries(entries); - } catch (error) {} + } catch (error) { + setEntries(null); + } + setIsLoading(false); }, [setEntries]); - return [entries, updateEntries]; + return [entries, updateEntries, isLoading]; }; export interface IGetCompareResults { diff --git a/web/src/root.tsx b/web/src/root.tsx index c504d5f1..014aad64 100644 --- a/web/src/root.tsx +++ b/web/src/root.tsx @@ -76,7 +76,7 @@ export const AppRoot = (props: any) => { element:
}, { - path: "logs", + path: "log", element: }, { diff --git a/web/src/routes/log.tsx b/web/src/routes/log.tsx index ee712910..84e1303b 100644 --- a/web/src/routes/log.tsx +++ b/web/src/routes/log.tsx @@ -4,145 +4,60 @@ * SPDX-License-Identifier: AGPL-3.0-or-later * */ -import React, { useEffect, useState } from "react"; -import { Table, Loading } from "react-daisyui"; +import React from "react"; +import { Loading, Card } from "react-daisyui"; import { usePackageLogs } from "../hooks/BxtHooks"; -import { - useReactTable, - createColumnHelper, - getCoreRowModel, - getPaginationRowModel, - getSortedRowModel, - flexRender, - SortingState -} from "@tanstack/react-table"; -import SectionLabel from "../components/SectionLabel"; - -const dateCompare = (a: LogEntry, b: LogEntry) => { - if (a.time < b.time) { - return -1; - } - if (a.time > b.time) { - return 1; - } - return 0; -}; +import { SortingState } from "@tanstack/react-table"; +import LogTable from "../components/LogTable"; +import { faBan } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; export default function Log(props: any) { - const [entries, updateEntries] = usePackageLogs(); - const [isLoading, setIsLoading] = useState(false); + const [entries, updateEntries, isLoading] = usePackageLogs(); const [sorting, setSorting] = React.useState([ { id: "Time", desc: true } ]); - - useEffect(() => { - setIsLoading(false); - }, [entries]); - - useEffect(() => { - updateEntries(); - setIsLoading(true); - }, []); - - const columnHelper = createColumnHelper(); - - const columns = [ - columnHelper.accessor("type", { - header: "Type" - }), - columnHelper.accessor("package.name", { - header: "Name" - }), - - columnHelper.accessor("package.poolEntries", { - header: "Version", - cell: (context) => { - const value = context.getValue(); - const preferredLocation = - context?.row?.original?.package?.preferredLocation; - - if (value && preferredLocation && value[preferredLocation]) { - return value[preferredLocation].version; - } else { - return "Unknown Version"; - } - } - }), - - columnHelper.accessor("package.section", { - header: "Section", - cell: (context) => - }), - - columnHelper.accessor((entry) => entry.time, { - header: "Time", - enableSorting: true, - sortingFn: "datetime" - }) - ]; - - const table = useReactTable({ - columns, - data: entries, - state: { - sorting - }, - manualPagination: true, - sortDescFirst: true, - enableSorting: true, - onSortingChange: setSorting, - getCoreRowModel: getCoreRowModel(), - getPaginationRowModel: getPaginationRowModel(), - getSortedRowModel: getSortedRowModel(), - initialState: {} - }); - - return ( -
- {isLoading ? ( + const renderContent = () => { + if (isLoading) { + return (
- ) : ( - - - {table - .getHeaderGroups() - .flatMap((headerGroup) => - headerGroup.headers.map((header) => ( - - {header.isPlaceholder - ? null - : flexRender( - header.column.columnDef - .header, - header.getContext() - )} - - )) - )} - + ); + } + + if (!entries) { + return ( +
+ + + + + Not found + + No events was found. It may happen because there + were no actions yet or the database is corrupted. + + +
+ ); + } + + return ( + + ); + }; - - {table.getRowModel().rows.map((row) => { - return ( - - {row.getVisibleCells().map((cell) => { - return ( - - {flexRender( - cell.column.columnDef.cell, - cell.getContext() - )} - - ); - })} - - ); - })} - -
- )} + return ( +
+ {renderContent()}
); }