diff --git a/pwa/src/apiService/apiService.ts b/pwa/src/apiService/apiService.ts index b035aab1..3dde8d6b 100644 --- a/pwa/src/apiService/apiService.ts +++ b/pwa/src/apiService/apiService.ts @@ -32,6 +32,7 @@ import Upload from "./resources/upload"; interface PromiseMessage { loading?: string; success?: string; + error?: string; } export type TSendFunction = ( @@ -255,37 +256,35 @@ export default class APIService { switch (action) { case "GET": const response = instance.get(endpoint); - - response.catch((err) => toast.error(err.message)); - + response.catch((err) => toast.error(promiseMessage?.error ?? err.message)); return response; case "POST": return toast.promise(instance.post(endpoint, _payload), { loading: promiseMessage?.loading ?? "Creating item...", success: promiseMessage?.success ?? "Succesfully created item", - error: (err) => err.message, + error: (err) => promiseMessage?.error ?? err.message, }); case "PUT": return toast.promise(instance.put(endpoint, _payload), { loading: promiseMessage?.loading ?? "Updating item...", success: promiseMessage?.success ?? "Succesfully updated item", - error: (err) => err.message, + error: (err) => promiseMessage?.error ?? err.message, }); case "DELETE": return toast.promise(instance.delete(endpoint), { loading: promiseMessage?.loading ?? "Deleting item...", success: promiseMessage?.success ?? "Succesfully deleted item", - error: (err) => err.message, + error: (err) => promiseMessage?.error ?? err.message, }); case "DOWNLOAD": return toast.promise(instance.get(endpoint), { loading: promiseMessage?.loading ?? "Downloading item...", success: promiseMessage?.success ?? "Succesfully downloaded item", - error: (err) => err.message, + error: (err) => promiseMessage?.error ?? err.message, }); } }; diff --git a/pwa/src/apiService/resources/dashboardCards.tsx b/pwa/src/apiService/resources/dashboardCards.tsx index 7e3b866a..b9d4975e 100644 --- a/pwa/src/apiService/resources/dashboardCards.tsx +++ b/pwa/src/apiService/resources/dashboardCards.tsx @@ -25,7 +25,9 @@ export default class DashboardCards { } public getAll = async (): Promise => { - const { data } = await this._send(this._instance, "GET", "/admin/dashboardCards"); + const { data } = await this._send(this._instance, "GET", "/admin/dashboardCards", undefined, { + error: "Could not fetch Dashboardcards.", + }); return data; }; diff --git a/pwa/src/apiService/resources/source.ts b/pwa/src/apiService/resources/source.ts index dfdaadb0..82877ff0 100644 --- a/pwa/src/apiService/resources/source.ts +++ b/pwa/src/apiService/resources/source.ts @@ -16,6 +16,12 @@ export default class Source { return data; }; + public getAllSelectOptions = async (): Promise => { + const { data } = await this._send(this._instance, "GET", "/admin/gateways?limit=200"); + + return data?.map((source: any) => ({ label: source.name, value: source.id })); + }; + public getOne = async (id: string): Promise => { const { data } = await this._send(this._instance, "GET", `/admin/gateways/${id}`); diff --git a/pwa/src/components/horizontalOverflowWrapper/HorizontalOverflowWrapper.module.css b/pwa/src/components/horizontalOverflowWrapper/HorizontalOverflowWrapper.module.css new file mode 100644 index 00000000..8b32a3d9 --- /dev/null +++ b/pwa/src/components/horizontalOverflowWrapper/HorizontalOverflowWrapper.module.css @@ -0,0 +1,52 @@ +:root { + --conduction-horizontal-overflow-wrapper-background-color: unset; + --conduction-horizontal-overflow-wrapper-buttons-top: 12px; + + --conduction-horizontal-overflow-wrapper-margin-inline-start: 8px; + --conduction-horizontal-overflow-wrapper-margin-inline-end: 8px; + --conduction-horizontal-overflow-wrapper-margin-block-start: 8px; + --conduction-horizontal-overflow-wrapper-margin-block-end: 8px; +} + +.container { + position: relative; + background-color: var( + --conduction-horizontal-overflow-wrapper-background-color + ); +} + +.wrapper { + overflow-x: scroll; +} + +.scrollButton { + position: sticky; + top: var(--conduction-horizontal-overflow-wrapper-buttons-top); + + margin-inline-start: var( + --conduction-horizontal-overflow-wrapper-margin-inline-start + ); + margin-inline-end: var( + --conduction-horizontal-overflow-wrapper-margin-inline-end + ); + margin-block-start: var( + --conduction-horizontal-overflow-wrapper-margin-block-start + ); + margin-block-end: var( + --conduction-horizontal-overflow-wrapper-margin-block-end + ); + z-index: 10000; +} + +.scrollButton.right { + left: 100%; +} + +/* Hide scrollbar */ +.wrapper::-webkit-scrollbar { + display: none; +} +.wrapper { + -ms-overflow-style: none; + scrollbar-width: none; +} diff --git a/pwa/src/components/horizontalOverflowWrapper/HorizontalOverflowWrapper.tsx b/pwa/src/components/horizontalOverflowWrapper/HorizontalOverflowWrapper.tsx new file mode 100644 index 00000000..c6385d7d --- /dev/null +++ b/pwa/src/components/horizontalOverflowWrapper/HorizontalOverflowWrapper.tsx @@ -0,0 +1,84 @@ +/** + * IMPORTANT: This is a temporary component that will be removed if the @conduction/components package can be updated. + */ + +import * as React from "react"; +import * as styles from "./HorizontalOverflowWrapper.module.css"; +import clsx from "clsx"; +import { Button } from "@utrecht/component-library-react/dist/css-module"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faChevronLeft, faChevronRight } from "@fortawesome/free-solid-svg-icons"; + +interface HorizontalOverflowWrapperProps { + children: React.ReactNode; + ariaLabels: { + scrollRightButton: string; + scrollLeftButton: string; + }; +} + +export const HorizontalOverflowWrapper: React.FC = ({ children, ariaLabels }) => { + const [canScrollRight, setCanScrollRight] = React.useState(false); + const [canScrollLeft, setCanScrollLeft] = React.useState(false); + + const wrapperRef = React.useRef(null); + + const scrollRight = (): void => { + wrapperRef.current?.scrollTo({ + left: wrapperRef.current.scrollLeft + wrapperRef.current.clientWidth * 0.9, + behavior: "smooth", + }); + }; + + const scrollLeft = (): void => { + wrapperRef.current?.scrollTo({ + left: wrapperRef.current.scrollLeft - wrapperRef.current.clientWidth * 0.9, + behavior: "smooth", + }); + }; + + React.useEffect(() => { + checkScrollDirections(); // initiate available scroll directions + + window.addEventListener("resize", checkScrollDirections); + + return () => window.removeEventListener("resize", checkScrollDirections); + }, []); + + const checkScrollDirections = (): void => { + if (!wrapperRef.current) return; + + setCanScrollRight(wrapperRef.current.scrollLeft + wrapperRef.current.clientWidth < wrapperRef.current.scrollWidth); + setCanScrollLeft(wrapperRef.current.scrollLeft > 0); + }; + + return ( +
+ {canScrollLeft && ( + + )} + + {canScrollRight && ( + + )} + +
+ {children} +
+
+ ); +}; diff --git a/pwa/src/context/logs.ts b/pwa/src/context/logs.ts index 74125129..f3f234b1 100644 --- a/pwa/src/context/logs.ts +++ b/pwa/src/context/logs.ts @@ -13,6 +13,7 @@ export interface ILogFiltersContext { process?: string; endpoint?: string; schema?: string; + source?: string; cronjob?: string; action?: string; user?: string; diff --git a/pwa/src/context/tableColumns.ts b/pwa/src/context/tableColumns.ts index 3d5361b7..e55b307a 100644 --- a/pwa/src/context/tableColumns.ts +++ b/pwa/src/context/tableColumns.ts @@ -21,6 +21,7 @@ const logColumns = { created: true, endpoint: true, schema: true, + source: true, cronjob: true, action: true, user: true, diff --git a/pwa/src/hooks/dashboardCards.ts b/pwa/src/hooks/dashboardCards.ts index 20f79d0e..9b321a5f 100644 --- a/pwa/src/hooks/dashboardCards.ts +++ b/pwa/src/hooks/dashboardCards.ts @@ -13,6 +13,7 @@ export const useDashboardCards = (queryClient: QueryClient) => { onError: (error) => { console.warn(error.message); }, + retry: 0, }); const getOne = (dashboardCardsId: string) => diff --git a/pwa/src/hooks/source.ts b/pwa/src/hooks/source.ts index f809615c..b0dfff2d 100644 --- a/pwa/src/hooks/source.ts +++ b/pwa/src/hooks/source.ts @@ -18,6 +18,13 @@ export const useSource = (queryClient: QueryClient) => { }, }); + const getAllSelectOptions = () => + useQuery("source_select_options", API.Sources.getAllSelectOptions, { + onError: (error) => { + console.warn(error.message); + }, + }); + const getOne = (sourceId: string) => useQuery(["sources", sourceId], () => API?.Sources.getOne(sourceId), { initialData: () => queryClient.getQueryData("sources")?.find((_sources) => _sources.id === sourceId), @@ -78,5 +85,5 @@ export const useSource = (queryClient: QueryClient) => { }, }); - return { getAll, getOne, remove, createOrEdit, getProxy }; + return { getAll, getOne, remove, createOrEdit, getProxy, getAllSelectOptions }; }; diff --git a/pwa/src/services/removeTrailingSlash.ts b/pwa/src/services/removeTrailingSlash.ts new file mode 100644 index 00000000..304ff730 --- /dev/null +++ b/pwa/src/services/removeTrailingSlash.ts @@ -0,0 +1,4 @@ +export const removeTrailingSlash = (str: string) => { + const newStr = str.endsWith("/") ? str.slice(0, -1) : str; + return newStr; +}; diff --git a/pwa/src/templates/actionsTemplate/ActionsTemplate.tsx b/pwa/src/templates/actionsTemplate/ActionsTemplate.tsx index ec84a986..e2503a3e 100644 --- a/pwa/src/templates/actionsTemplate/ActionsTemplate.tsx +++ b/pwa/src/templates/actionsTemplate/ActionsTemplate.tsx @@ -89,7 +89,7 @@ export const ActionsTemplate: React.FC = () => { {action.lastRun ? dateTime(t(i18n.language), action.lastRun) : "-"} - {`${action.lastRunTime}ms` ?? "-"} + {`${action.lastRunTime}s` ?? "-"} {translateDate(i18n.language, action.dateCreated) ?? "-"} diff --git a/pwa/src/templates/gatewayDetailTemplate/GatewayDetailTemplate.tsx b/pwa/src/templates/gatewayDetailTemplate/GatewayDetailTemplate.tsx index 88b79bde..673b4690 100644 --- a/pwa/src/templates/gatewayDetailTemplate/GatewayDetailTemplate.tsx +++ b/pwa/src/templates/gatewayDetailTemplate/GatewayDetailTemplate.tsx @@ -70,7 +70,9 @@ export const GatewayDetailTemplate: React.FC = () => { getPlugins.data.update ? getPlugins.data?.version : `Latest (${getPlugins.data?.version})` } `}

)} -

{`last update time: ${new Date(getPlugins.data?.time).toLocaleString()}`}

+

{`last update time: ${new Date( + getPlugins.data?.versions[getPlugins.data?.version]?.time, + ).toLocaleString()}`}

diff --git a/pwa/src/templates/login/LoginTemplate.tsx b/pwa/src/templates/login/LoginTemplate.tsx index 1d372f6d..7f33e33a 100644 --- a/pwa/src/templates/login/LoginTemplate.tsx +++ b/pwa/src/templates/login/LoginTemplate.tsx @@ -12,6 +12,7 @@ import { useAuthentication } from "../../hooks/useAuthentication"; import { useIsLoadingContext } from "../../context/isLoading"; import APIService from "../../apiService/apiService"; import APIContext from "../../apiService/apiContext"; +import { removeTrailingSlash } from "../../services/removeTrailingSlash"; export const LoginTemplate: React.FC = () => { const { t } = useTranslation(); @@ -54,7 +55,9 @@ export const LoginTemplate: React.FC = () => { React.useEffect(() => { window.sessionStorage.getItem("GATSBY_BASE_URL") && - setDexRedirectURL(`${window.sessionStorage.getItem("GATSBY_BASE_URL")}/login/oidc/dex`); + setDexRedirectURL( + `${removeTrailingSlash(window.sessionStorage.getItem("GATSBY_BASE_URL") ?? "")}/login/oidc/dex`, + ); }); return ( diff --git a/pwa/src/templates/logsDetailTemplate/LogsDetailTemplate.module.css b/pwa/src/templates/logsDetailTemplate/LogsDetailTemplate.module.css index f73f4b63..188a61ec 100644 --- a/pwa/src/templates/logsDetailTemplate/LogsDetailTemplate.module.css +++ b/pwa/src/templates/logsDetailTemplate/LogsDetailTemplate.module.css @@ -48,7 +48,7 @@ } .warningLevel { - --denhaag-alert-info-background-color: #var( + --denhaag-alert-info-background-color: var( --gateway-ui-color-status-background-warning ); --denhaag-alert-info-icon-color: var(--gateway-ui-color-status-warning); diff --git a/pwa/src/templates/logsDetailTemplate/LogsDetailTemplate.tsx b/pwa/src/templates/logsDetailTemplate/LogsDetailTemplate.tsx index 14feb4fb..2d7e6267 100644 --- a/pwa/src/templates/logsDetailTemplate/LogsDetailTemplate.tsx +++ b/pwa/src/templates/logsDetailTemplate/LogsDetailTemplate.tsx @@ -23,6 +23,8 @@ import { Button } from "../../components/button/Button"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useMapping } from "../../hooks/mapping"; import { useObject } from "../../hooks/object"; +import { CodeEditor } from "../../components/codeEditor/CodeEditor"; +import { useSource } from "../../hooks/source"; interface LogsDetailTemplateProps { logId: string; @@ -31,6 +33,10 @@ interface LogsDetailTemplateProps { export const LogsDetailTemplate: React.FC = ({ logId }) => { const { t } = useTranslation(); const { setLogFilters } = useLogFiltersContext(); + const [bodyData, setBodyData] = React.useState(""); + const [exceptionData, setExceptionData] = React.useState(""); + const [routeParametersData, setRouteParametersData] = React.useState(""); + const [sourceCallData, setSourceCallData] = React.useState(""); const queryClient = useQueryClient(); @@ -44,6 +50,7 @@ export const LogsDetailTemplate: React.FC = ({ logId }) const getOrganization = useOrganization(queryClient).getOne(getLog.data?.context?.organization); const getMapping = useMapping(queryClient).getOne(getLog.data?.context?.mapping); const getObject = useObject().getOne(getLog.data?.context?.object); + const getSource = useSource(queryClient).getOne(getLog.data?.context?.source); const handleSetContextFilter = ( context: @@ -51,6 +58,7 @@ export const LogsDetailTemplate: React.FC = ({ logId }) | "process" | "endpoint" | "schema" + | "source" | "cronjob" | "action" | "user" @@ -70,6 +78,13 @@ export const LogsDetailTemplate: React.FC = ({ logId }) navigate("/logs"); }; + React.useEffect(() => { + setBodyData(JSON.stringify(getLog.data?.context?.body, null, 2)); + setExceptionData(JSON.stringify(getLog.data?.context?.exception, null, 2)); + setRouteParametersData(JSON.stringify(getLog.data?.context?.route_parameters, null, 2)); + setSourceCallData(JSON.stringify(getLog.data?.context?.sourceCall, null, 2)); + }, [getLog]); + return ( {t("Logs detail page")} @@ -99,22 +114,120 @@ export const LogsDetailTemplate: React.FC = ({ logId }) - - Host - {getLog.data.context?.host !== "" ? getLog.data.context?.host : "-"} - - - - - - - IP - {getLog.data.context?.ip !== "" ? getLog.data.context?.ip : "-"} - - - - - + {getLog.data.context?.exception && ( + + Exception + + + + + )} + {getLog.data.context?.method && ( + + Method + {getLog.data.context?.method !== "" ? getLog.data.context?.method : "-"} + - + - + + )} + {getLog.data.context?.querystring && ( + + Querystring + + {getLog.data.context?.querystring !== "" ? getLog.data.context?.querystring : "-"} + + - + - + + )} + {getLog.data.context?.pathRaw && ( + + pathRaw + {getLog.data.context?.pathRaw !== "" ? getLog.data.context?.pathRaw : "-"} + - + - + + )} + {getLog.data.context?.request_uri && ( + + Request URI + + {getLog.data.context?.request_uri !== "" ? getLog.data.context?.request_uri : "-"} + + - + - + + )} + {getLog.data.context?.route && ( + + Route + {getLog.data.context?.route !== "" ? getLog.data.context?.route : "-"} + - + - + + )} + {getLog.data.context?.route_parameters && ( + + Route Parameters + + + + + )} + {getLog.data.context?.contentType && ( + + Content Type + + {getLog.data.context?.contentType !== "" ? getLog.data.context?.contentType : "-"} + + - + - + + )} + {getLog.data.context?.body && ( + + Body + + + + + )} + {getLog.data.context?.crude_body && ( + + Crude Body + {getLog.data.context?.crude_body} + + )} + {getLog.data.context?.mongoDBFilter && ( + + MongoDB Filter + {getLog.data.context?.mongoDBFilter} + + )} + {getLog.data.context?.plugin && ( + + Plugin + {getLog.data.context?.plugin !== "" ? getLog.data.context?.plugin : "-"} + - + - + + )} + {getLog.data.context?.sourceCall && ( + + Source Call + + + + + )} Session {getLog.data.context?.session !== "" ? getLog.data.context?.session : "-"} -
diff --git a/pwa/src/templates/templateParts/actionsForm/ActionFormTemplate.module.css b/pwa/src/templates/templateParts/actionsForm/ActionFormTemplate.module.css index 3638d30c..9fe0e619 100644 --- a/pwa/src/templates/templateParts/actionsForm/ActionFormTemplate.module.css +++ b/pwa/src/templates/templateParts/actionsForm/ActionFormTemplate.module.css @@ -6,10 +6,6 @@ padding-block-end: var(--gateway-ui-size-md); } -.gridContainer > *:not(:first-child) { - margin-block-start: var(--gateway-ui-size-sm); -} - .grid { display: grid; grid-gap: var(--gateway-ui-size-lg); diff --git a/pwa/src/templates/templateParts/actionsForm/ActionFormTemplate.tsx b/pwa/src/templates/templateParts/actionsForm/ActionFormTemplate.tsx index ebde5e48..af1d3315 100644 --- a/pwa/src/templates/templateParts/actionsForm/ActionFormTemplate.tsx +++ b/pwa/src/templates/templateParts/actionsForm/ActionFormTemplate.tsx @@ -15,6 +15,9 @@ import { SchemaFormTemplate } from "../schemaForm/SchemaFormTemplate"; import { useIsLoadingContext } from "../../../context/isLoading"; import { enrichValidation } from "../../../services/enrichReactHookFormValidation"; import { CodeEditor } from "../../../components/codeEditor/CodeEditor"; +import { translateDate } from "../../../services/dateFormat"; +import { StatusTag } from "../../../components/statusTag/StatusTag"; +import { formatDateTime } from "../../../services/dateTime"; export const formId: string = "action-form"; @@ -23,7 +26,7 @@ interface ActionFormTemplateProps { } export const ActionFormTemplate: React.FC = ({ action }) => { - const { t } = useTranslation(); + const { t, i18n } = useTranslation(); const { setIsLoading, isLoading } = useIsLoadingContext(); const [listensAndThrows, setListensAndThrows] = React.useState([]); @@ -149,7 +152,7 @@ export const ActionFormTemplate: React.FC = ({ action } -
+
@@ -272,6 +275,50 @@ export const ActionFormTemplate: React.FC = ({ action }
+ {action && ( + <> + +
+ + + {t("Date Created")} +
{translateDate(i18n.language, action.dateCreated) ?? "-"}
+
+
+ + + + {t("Date Modified")} +
{translateDate(i18n.language, action.dateModified) ?? "-"}
+
+
+ + + + {t("Last run")} +
{action.lastRun ? formatDateTime(t(i18n.language), action.lastRun) : "-"}
+
+
+ + + + {t("Last run time")} +
{`${action.lastRunTime}s` ?? "-"}
+
+
+ + + + {t("Status")} + + + +
+ + )}
diff --git a/pwa/src/templates/templateParts/logFilters/LogFiltersTemplate.tsx b/pwa/src/templates/templateParts/logFilters/LogFiltersTemplate.tsx index dcb4cdee..807dcb09 100644 --- a/pwa/src/templates/templateParts/logFilters/LogFiltersTemplate.tsx +++ b/pwa/src/templates/templateParts/logFilters/LogFiltersTemplate.tsx @@ -9,6 +9,7 @@ import { useUser } from "../../../hooks/user"; import { useSchema } from "../../../hooks/schema"; import { useAction } from "../../../hooks/action"; import { useObject } from "../../../hooks/object"; +import { useSource } from "../../../hooks/source"; import { useMapping } from "../../../hooks/mapping"; import { useCronjob } from "../../../hooks/cronjob"; import { useEndpoint } from "../../../hooks/endpoint"; @@ -33,6 +34,7 @@ export const LogFiltersTemplate: React.FC = ({ layoutCl const getEndpoints = useEndpoint(queryClient).getAllSelectOptions(); const getSchemas = useSchema(queryClient).getAllSelectOptions(); + const getSources = useSource(queryClient).getAllSelectOptions(); const getCronjobs = useCronjob(queryClient).getAllSelectOptions(); const getActions = useAction(queryClient).getAllSelectOptions(); const getUsers = useUser(queryClient).getAllSelectOptions(); @@ -72,6 +74,7 @@ export const LogFiltersTemplate: React.FC = ({ layoutCl process: formValues.process, endpoint: formValues.endpoints?.value, schema: formValues.schemas?.value, + source: formValues.sources?.value, cronjob: formValues.cronjobs?.value, action: formValues.actions?.value, user: formValues.users?.value, @@ -114,6 +117,11 @@ export const LogFiltersTemplate: React.FC = ({ layoutCl getSchemas.data?.find((schema) => schema.value === logFilters.context?.schema), ); + setValue( + "sources", + getSources.data?.find((source) => source.value === logFilters.context?.source), + ); + setValue( "cronjobs", getCronjobs.data?.find((cronjob) => cronjob.value === logFilters.context?.cronjob), @@ -229,6 +237,18 @@ export const LogFiltersTemplate: React.FC = ({ layoutCl + + + Sources + + {getSources.isSuccess && ( + + )} + + {getSources.isLoading && } + + + Cronjobs @@ -322,7 +342,6 @@ export const LogFiltersTemplate: React.FC = ({ layoutCl {getMappings.isLoading && } -
diff --git a/pwa/src/templates/templateParts/logsTable/LogsTableTemplate.module.css b/pwa/src/templates/templateParts/logsTable/LogsTableTemplate.module.css index 2d46eeac..bc2070f9 100644 --- a/pwa/src/templates/templateParts/logsTable/LogsTableTemplate.module.css +++ b/pwa/src/templates/templateParts/logsTable/LogsTableTemplate.module.css @@ -17,10 +17,6 @@ justify-content: space-between; } -.tableContainer { - max-width: 100%; - overflow-x: scroll; -} .button > span > svg { margin-inline-end: var(--gateway-ui-size-xs); diff --git a/pwa/src/templates/templateParts/logsTable/LogsTableTemplate.tsx b/pwa/src/templates/templateParts/logsTable/LogsTableTemplate.tsx index 7164f965..7a8b1691 100644 --- a/pwa/src/templates/templateParts/logsTable/LogsTableTemplate.tsx +++ b/pwa/src/templates/templateParts/logsTable/LogsTableTemplate.tsx @@ -16,6 +16,7 @@ import { Link } from "@gemeente-denhaag/components-react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { usePagination } from "../../../hooks/usePagination"; import clsx from "clsx"; +import { HorizontalOverflowWrapper } from "../../../components/horizontalOverflowWrapper/HorizontalOverflowWrapper"; interface LogsTableTemplateProps { logs: any[]; @@ -69,192 +70,210 @@ export const LogsTableTemplate: React.FC = ({ logs, pagi
-
- - - - {logColumns.level && {t("Level")}} - {logColumns.message && {t("Message")}} - {logColumns.created && {t("Created")}} - {logColumns.endpoint && {t("Endpoint")}} - {logColumns.schema && {t("Schema")}} - {logColumns.cronjob && {t("Cronjob")}} - {logColumns.action && {t("Action")}} - {logColumns.user && {t("User")}} - {logColumns.organization && {t("Organization")}} - {logColumns.application && {t("Application")}} - {logColumns.object && {t("Object")}} - {logColumns.mapping && {t("Mapping")}} - - - - - {logs.map((log: any) => ( - - {logColumns.level && ( - - - - )} + +
+
+ + + {logColumns.level && {t("Level")}} + {logColumns.message && {t("Message")}} + {logColumns.created && {t("Created")}} + {logColumns.endpoint && {t("Endpoint")}} + {logColumns.schema && {t("Schema")}} + {logColumns.source && {t("Source")}} + {logColumns.cronjob && {t("Cronjob")}} + {logColumns.action && {t("Action")}} + {logColumns.user && {t("User")}} + {logColumns.organization && {t("Organization")}} + {logColumns.application && {t("Application")}} + {logColumns.object && {t("Object")}} + {logColumns.mapping && {t("Mapping")}} + + + + + {logs.map((log: any) => ( + + {logColumns.level && ( + + + + )} - {logColumns.message && ( - - -
{log.message}
-
-
- )} + {logColumns.message && ( + + +
{log.message}
+
+
+ )} - {logColumns.created && ( - - -
- {formatUnixDateTime(t(i18n.language), log.datetime.$date.$numberLong)} -
-
-
- )} + {logColumns.created && ( + + +
+ {formatUnixDateTime(t(i18n.language), log.datetime.$date.$numberLong)} +
+
+
+ )} - {logColumns.endpoint && ( - -
-
+ navigate(`/logs/${log._id.$oid}`)}> + } iconAlign="start"> + {t("Details")} + + + + ))} + + {!logs.length && ( + + {Object.values(logColumns) + .filter((value) => value) + .map((_, idx) => ( + {idx === 0 && <>No logs found} + ))} + + )} + + + +