Add 'Edit Query' button to Query Results

This allows easier editing of existing query results
This commit is contained in:
Aurora Lahtela 2024-03-14 20:16:56 +02:00
parent e021162729
commit 20b8ab9baa
22 changed files with 78 additions and 26 deletions

View File

@ -336,6 +336,7 @@ public enum HtmlLang implements Lang {
QUERY_SERVERS_TWO("html.query.label.servers.two", "using data of 2 servers"),
QUERY_SERVERS_MANY("html.query.label.servers.many", "using data of {number} servers"),
QUERY_SHOW_FULL_QUERY("html.query.label.showFullQuery", "Show Full Query"),
QUERY_EDIT_QUERY("html.query.label.editQuery", "Edit Query"),
HELP_TEST_RESULT("html.label.help.testResult", "Test result"),
HELP_TEST_IT_OUT("html.label.help.testPrompt", "Test it out:"),

View File

@ -794,6 +794,7 @@ html:
generic:
are: "`是`"
label:
editQuery: "Edit Query"
from: ">从 </label>"
makeAnother: "进行另一个查询"
servers:

View File

@ -794,6 +794,7 @@ html:
generic:
are: "` jsou`"
label:
editQuery: "Edit Query"
from: ">od</label>"
makeAnother: "Vytvořit další dotaz"
servers:

View File

@ -794,6 +794,7 @@ html:
generic:
are: "`are`"
label:
editQuery: "Edit Query"
from: ">from</label>"
makeAnother: "Make another query"
servers:

View File

@ -794,6 +794,7 @@ html:
generic:
are: "`are`"
label:
editQuery: "Edit Query"
from: ">from</label>"
makeAnother: "Make another query"
servers:

View File

@ -794,6 +794,7 @@ html:
generic:
are: "`are`"
label:
editQuery: "Edit Query"
from: ">de</label>"
makeAnother: "Realiza otra consulta"
servers:

View File

@ -794,6 +794,7 @@ html:
generic:
are: "`ovat`"
label:
editQuery: "Edit Query"
from: ">tästä</label>"
makeAnother: "Tee toinen kysely"
servers:

View File

@ -794,6 +794,7 @@ html:
generic:
are: "`sont`"
label:
editQuery: "Edit Query"
from: ">de</label>"
makeAnother: "Faire une autre Requête"
servers:

View File

@ -794,6 +794,7 @@ html:
generic:
are: "`are`"
label:
editQuery: "Edit Query"
from: ">from</label>"
makeAnother: "Make another query"
servers:

View File

@ -794,6 +794,7 @@ html:
generic:
are: "`それは`"
label:
editQuery: "Edit Query"
from: ">から</label>"
makeAnother: "別のクエリを作る"
servers:

View File

@ -794,6 +794,7 @@ html:
generic:
are: "`are`"
label:
editQuery: "Edit Query"
from: ">from</label>"
makeAnother: "Make another query"
servers:

View File

@ -794,6 +794,7 @@ html:
generic:
are: "`zijn`"
label:
editQuery: "Edit Query"
from: ">van</label>"
makeAnother: "Maak nog een query"
servers:

View File

@ -794,6 +794,7 @@ html:
generic:
are: "`are`"
label:
editQuery: "Edit Query"
from: ">from</label>"
makeAnother: "Make another query"
servers:

View File

@ -794,6 +794,7 @@ html:
generic:
are: "``"
label:
editQuery: "Edit Query"
from: ">с</label>"
makeAnother: "Сделать другой запрос"
servers:

View File

@ -794,6 +794,7 @@ html:
generic:
are: "`vardır`"
label:
editQuery: "Edit Query"
from: ">dan</label>"
makeAnother: "Başka bir sorgu yap"
servers:

View File

@ -794,6 +794,7 @@ html:
generic:
are: "``"
label:
editQuery: "Edit Query"
from: ">з</label>"
makeAnother: "Зробити інший запит"
servers:

View File

@ -794,6 +794,7 @@ html:
generic:
are: "`是`"
label:
editQuery: "Edit Query"
from: ">從 </label>"
makeAnother: "進行另一個查詢"
servers:

View File

@ -3,13 +3,14 @@ import {useQueryResultContext} from "../../hooks/queryResultContext";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faFilter} from "@fortawesome/free-solid-svg-icons";
import {useTranslation} from "react-i18next";
import {Link} from "react-router-dom";
const QueryPath = () => {
const QueryPath = ({newQuery}) => {
const {t} = useTranslation();
const {result} = useQueryResultContext();
const hasResults = result && result.data;
const hasResults = Boolean(result?.data);
const path = result?.path;
if (!path || !path.length) return <></>;
if (!path?.length || (newQuery && hasResults)) return <></>;
const getReadableFilterName = kind => {
@ -48,6 +49,7 @@ const QueryPath = () => {
return (
<aside id={"result-path"} className={"alert shadow " + (hasResults ? "alert-success" : "alert-warning")}>
{!newQuery && <Link className={"link float-end"} to={"/query/new"}>{t('html.query.label.editQuery')}</Link>}
{path.map((step, i) => <p key={step.kind + step.size}
style={{marginBottom: 0, marginLeft: i * 0.7 + "rem"}}>
<FontAwesomeIcon

View File

@ -17,6 +17,7 @@ import FilterDropdown from "./FilterDropdown";
import FilterList from "./FilterList";
import {useQueryResultContext} from "../../../hooks/queryResultContext";
import {useNavigate} from "react-router-dom";
import {useNavigation} from "../../../hooks/navigationHook.jsx";
const parseTime = (dateString, timeString) => {
const d = dateString.match(
@ -37,7 +38,8 @@ const parseTime = (dateString, timeString) => {
const QueryOptionsCard = () => {
const {t} = useTranslation();
const navigate = useNavigate()
const {setResult} = useQueryResultContext();
const {result, setResult} = useQueryResultContext();
const {setCurrentTab} = useNavigation();
const [loadingResults, setLoadingResults] = useState(false);
@ -52,6 +54,7 @@ const QueryOptionsCard = () => {
// View & filter data
const {data: options, loadingError} = useDataRequest(fetchFilters, []);
const [graphData, setGraphData] = useState(undefined);
useEffect(() => {
if (options) {
@ -71,24 +74,22 @@ const QueryOptionsCard = () => {
if (invalidFields.length || !options) return;
if (!fromDate && !fromTime && !toDate && !toTime) return;
const newMin = parseTime(
fromDate ? fromDate : options.view.afterDate,
fromTime ? fromTime : options.view.afterTime
);
const newMax = parseTime(
toDate ? toDate : options.view.beforeDate,
toTime ? toTime : options.view.beforeTime
);
setExtremes({
min: newMin,
max: newMax
min: parseTime(
fromDate || options.view.afterDate,
fromTime || options.view.afterTime
),
max: parseTime(
toDate || options.view.beforeDate,
toTime || options.view.beforeTime
)
});
}, [invalidFields, options]);
/* eslint-enable react-hooks/exhaustive-deps */
useEffect(updateExtremes, [invalidFields, updateExtremes]);
useEffect(updateExtremes, [updateExtremes]);
const onSetExtremes = useCallback((event) => {
if (event && event.trigger) {
if (event?.trigger) {
const afterDate = Highcharts.dateFormat('%d/%m/%Y', event.min);
const afterTime = Highcharts.dateFormat('%H:%M', event.min);
const beforeDate = Highcharts.dateFormat('%d/%m/%Y', event.max);
@ -100,6 +101,33 @@ const QueryOptionsCard = () => {
}
}, [setFromTime, setFromDate, setToTime, setToDate]);
useEffect(() => {
if (!options || !result) return;
setSelectedServers(result.view.servers.map(server => options.view.servers.findIndex(s => s.serverUUID === server.serverUUID)).filter(i => i !== -1))
setFromDate(result.view.afterDate);
setFromTime(result.view.afterTime);
setToDate(result.view.beforeDate);
setToTime(result.view.beforeTime);
const existingFilters = result.filters;
existingFilters.forEach(f => f.options = options.filters.find(fo => fo.kind === f.kind).options);
setFilters(existingFilters);
setExtremes({
min: parseTime(
result.view.afterDate || options.view.afterDate,
result.view.afterTime || options.view.afterTime
),
max: parseTime(
result.view.beforeDate || options.view.beforeDate,
result.view.beforeTime || options.view.beforeTime
)
});
setCurrentTab('html.query.label.editQuery')
}, [options, result, setFromDate, setFromTime, setToDate, setToTime, setFilters, setSelectedServers, setExtremes]);
const getServerSelectorMessage = () => {
const selected = selectedServers.length;
const available = options.view.servers.length;
@ -117,10 +145,10 @@ const QueryOptionsCard = () => {
const performQuery = async () => {
const inputDto = {
view: {
afterDate: fromDate ? fromDate : options.view.afterDate,
afterTime: fromTime ? fromTime : options.view.afterTime,
beforeDate: toDate ? toDate : options.view.beforeDate,
beforeTime: toTime ? toTime : options.view.beforeTime,
afterDate: fromDate || options.view.afterDate,
afterTime: fromTime || options.view.afterTime,
beforeDate: toDate || options.view.beforeDate,
beforeTime: toTime || options.view.beforeTime,
servers: selectedServers.map(index => options.view.servers[index])
},
filters

View File

@ -12,10 +12,10 @@ const BetweenDatesFilter = ({index, label, filter, removeFilter, setFilterOption
const options = filter.options;
const [fromDate, setFromDate] = useState(options.after[0]);
const [fromTime, setFromTime] = useState(options.after[1]);
const [toDate, setToDate] = useState(options.before[0]);
const [toTime, setToTime] = useState(options.before[1]);
const [fromDate, setFromDate] = useState(filter.parameters?.afterDate || options.after[0]);
const [fromTime, setFromTime] = useState(filter.parameters?.afterTime || options.after[1]);
const [toDate, setToDate] = useState(filter.parameters?.beforeDate || options.before[0]);
const [toTime, setToTime] = useState(filter.parameters?.beforeTime || options.before[1]);
useEffect(() => {
setFilterOptions({
...filter,

View File

@ -9,7 +9,12 @@ const MultipleChoiceFilter = ({index, label, filter, removeFilter, setFilterOpti
const {t} = useTranslation();
const select = index === 0 ? t('html.query.filter.generic.start') : t('html.query.filter.generic.and');
const [selectedIndexes, setSelectedIndexes] = useState([]);
const parameterSelected = filter.parameters?.selected
? JSON.parse(filter.parameters.selected)
.map(option => filter.options.options.indexOf(option))
: undefined;
const [selectedIndexes, setSelectedIndexes] = useState(parameterSelected || []);
useEffect(() => {
setFilterOptions({
...filter,

View File

@ -12,7 +12,7 @@ const NewQueryView = () => {
{hasPermission('access.query') && <section className={"query-options-view"}>
<Row>
<Col md={12}>
<QueryPath/>
<QueryPath newQuery/>
<QueryOptionsCard/>
</Col>
</Row>