Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 55 additions & 1 deletion assets/less/cds-rdm/administration/harvester-reports.less
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,58 @@
color: #999;
margin-top: 0.2em;
}
}
}

.harvester-report-actions {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-items: center;
justify-content: flex-end;
gap: 0.5em;
width: 100%;
box-sizing: border-box;
padding-right: 0.25rem;

> .ui.button {
flex: 0 0 auto;
margin: 0 !important;
}
}

.ui.grid .column.harvester-report-actions-col {
padding-right: 1.25rem;
}

.harvester-run-report-meta {
margin-top: 0.15rem !important;
}

.harvester-run-log-grid {
margin-top: 1rem;
}

.harvester-run-success-message {
white-space: normal;
text-align: left;
}

// Job run report: wrap long lines; scroll region height (matches admin log-table intent)
.harvester-run-log-report {
width: 100%;

.harvester-run-log-table.log-table {
max-height: calc(100vh - 11rem);
overflow-x: hidden;
}

.harvester-run-log-segment {
overflow-x: hidden;
}

.log-line .log-message {
white-space: pre-wrap;
overflow-wrap: anywhere;
word-break: break-word;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,39 @@
// under the terms of the GPL-2.0 License; see LICENSE file for more details.

import React from "react";
import { withState } from "react-searchkit";
import { Button, Icon } from "semantic-ui-react";
import { i18next } from "@translations/invenio_administration/i18next";
import { extractRunIdFromQuery } from "./utils";

const DownloadButtonComponent = ({ currentQueryState }) => {
const domContainer = document.getElementById("invenio-search-config");
const runs = JSON.parse(domContainer?.dataset.harvesterRuns || "[]");

const runId = extractRunIdFromQuery(
currentQueryState.queryString || "",
runs
);

const handleDownload = () => {
if (!runId) return;
const params = new URLSearchParams({ run_id: runId });
window.location.href = `/harvester-reports/download?${params.toString()}`;
};

return (
export const DownloadButton = ({ runId }) => (
<div className="harvester-report-actions">
<Button
icon
labelPosition="left"
onClick={handleDownload}
onClick={() => {
if (!runId) return;
const q = new URLSearchParams({ run_id: runId });
window.location.href = `/harvester-reports/download?${q}`;
}}
disabled={!runId}
className="harvester-download-button"
className="harvester-download-log-button"
size="small"
>
<Icon name="download" />
{i18next.t("Download")}
{i18next.t("Download error logs")}
</Button>
);
};

export const DownloadButton = withState(DownloadButtonComponent);
<Button
icon
labelPosition="left"
onClick={() => {
if (!runId) return;
window.location.assign(`/administration/harvester-reports/${runId}/report`);
}}
disabled={!runId}
className="harvester-view-logs-button"
size="small"
>
<Icon name="file alternate outline" />
{i18next.t("View error logs")}
</Button>
</div>
);
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,15 @@ const SearchBarComponent = ({ updateQueryState, currentQueryState }) => {

const { sortOptions, sortOrderDisabled } = useContext(SearchConfigurationContext);

// Derive selected run from the timestamp in the current query — null if user typed a custom range
const runIdFromQuery = extractRunIdFromQuery(currentQueryState.queryString, runs);
const selectedRun = runs.find((r) => r.id === runIdFromQuery) || null;
const [activeRunId, setActiveRunId] = React.useState(() => defaultRun?.id ?? null);

const [inputValue, setInputValue] = React.useState(currentQueryState.queryString || "");
const runIdFromQuery = extractRunIdFromQuery(currentQueryState.queryString, runs);
const effectiveRunId = runIdFromQuery || activeRunId;
const selectedRun = runs.find((r) => r.id === effectiveRunId) || null;

// Auto-select default run on mount only if there is no existing query
React.useEffect(() => {
if (!currentQueryState.queryString && defaultRun) {
executeSearch(defaultRun, "");
}
}, []);
const [inputValue, setInputValue] = React.useState(
currentQueryState.queryString || ""
);

const executeSearch = (run, userInput) => {
const timestampFilter = buildTimestampFilter(run);
Expand All @@ -57,9 +54,25 @@ const SearchBarComponent = ({ updateQueryState, currentQueryState }) => {
});
};

React.useEffect(() => {
const q = currentQueryState.queryString || "";
const fromQuery = extractRunIdFromQuery(q, runs);
if (fromQuery) {
setActiveRunId(fromQuery);
return;
}
if (!q && defaultRun) {
setActiveRunId(defaultRun.id);
executeSearch(defaultRun, "");
}
}, []);

const onRunChange = (e, { value }) => {
setActiveRunId(value || null);
const run = runs.find((r) => r.id === value);
executeSearch(run, "");
if (run) {
executeSearch(run, "");
}
};

const onBtnSearchClick = () => {
Expand All @@ -68,6 +81,10 @@ const SearchBarComponent = ({ updateQueryState, currentQueryState }) => {
queryString: inputValue,
hiddenParams,
});
const id = extractRunIdFromQuery(inputValue, runs);
if (id) {
setActiveRunId(id);
}
};

const formatDate = (dateStr) => {
Expand Down Expand Up @@ -114,7 +131,7 @@ const SearchBarComponent = ({ updateQueryState, currentQueryState }) => {
selection
placeholder={i18next.t("Select a harvest run...")}
options={runOptions}
value={selectedRun?.id || ""}
value={activeRunId || ""}
onChange={onRunChange}
/>

Expand Down Expand Up @@ -162,7 +179,7 @@ const SearchBarComponent = ({ updateQueryState, currentQueryState }) => {
</Grid.Column>
</Grid.Row>
<Grid.Row>
<Grid.Column width={11}>
<Grid.Column width={9}>
<Header as="h4">{i18next.t("Search Logs")}</Header>
<Input
action={{
Expand All @@ -184,15 +201,19 @@ const SearchBarComponent = ({ updateQueryState, currentQueryState }) => {
}}
/>
</Grid.Column>
<Grid.Column width={3} verticalAlign="bottom">
<Grid.Column width={2} verticalAlign="bottom">
<Sort
sortOrderDisabled={sortOrderDisabled}
values={sortOptions}
ariaLabel={i18next.t("Sort")}
/>
</Grid.Column>
<Grid.Column width={2} verticalAlign="bottom">
<DownloadButton />
<Grid.Column
className="harvester-report-actions-col"
width={5}
verticalAlign="bottom"
>
<DownloadButton runId={effectiveRunId} />
</Grid.Column>
</Grid.Row>
</Grid>
Expand Down
Loading
Loading