Skip to content

Commit

Permalink
Add new custom settings: state captions, search help
Browse files Browse the repository at this point in the history
  • Loading branch information
gafetinov committed Nov 29, 2023
1 parent c251693 commit a6456eb
Show file tree
Hide file tree
Showing 16 changed files with 246 additions and 176 deletions.
3 changes: 2 additions & 1 deletion cassandra-distributed-task-queue-ui/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@
"@typescript-eslint/explicit-member-accessibility": "error",
"@typescript-eslint/explicit-function-return-type": "off",
"react/no-deprecated": "warn",
"react/prop-types": "off"
"react/prop-types": "off",
"react/display-name": "warn"
},
"ignorePatterns": ["dist/", "react-selenium-testing.js"],
"settings": {
Expand Down
49 changes: 49 additions & 0 deletions cassandra-distributed-task-queue-ui/src/CustomSettingsContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { createContext, PropsWithChildren, useContext } from "react";
import type { JSX } from "react";

import { TaskState } from "./Domain/Api/TaskState";
import { CustomRenderer, ICustomRenderer } from "./Domain/CustomRenderer";

const TaskStateCaptions = {
[TaskState.Unknown]: "Unknown",
[TaskState.New]: "New",
[TaskState.WaitingForRerun]: "Waiting for rerun",
[TaskState.WaitingForRerunAfterError]: "Waiting for rerun after error",
[TaskState.Finished]: "Finished",
[TaskState.InProcess]: "In process",
[TaskState.Fatal]: "Fatal",
[TaskState.Canceled]: "Canceled",
};

export type TaskStateDict = Partial<Record<TaskState, string>>;

export interface ICustomSettings {
customDetailRenderer: ICustomRenderer;
customStateCaptions: TaskStateDict;
customSearchHelp?: JSX.Element;
}

const defaultValue: ICustomSettings = {
customStateCaptions: TaskStateCaptions,
customDetailRenderer: new CustomRenderer(),
};

const CustomSettingsContext = createContext<ICustomSettings>(defaultValue);

export const CustomSettingsProvider = ({
customStateCaptions,
customSearchHelp,
customDetailRenderer,
children,
}: PropsWithChildren<Partial<ICustomSettings>>) => {
const stateCaptions = customStateCaptions || TaskStateCaptions;
const renderer = customDetailRenderer || new CustomRenderer();
return (
<CustomSettingsContext.Provider
value={{ customStateCaptions: stateCaptions, customDetailRenderer: renderer, customSearchHelp }}>
{children}
</CustomSettingsContext.Provider>
);
};

export const useCustomSettings = () => useContext(CustomSettingsContext);
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type { JSX } from "react";
import { Routes, Route } from "react-router-dom";

import { CustomSettingsProvider, TaskStateDict } from "./CustomSettingsContext";
import { IRtqMonitoringApi } from "./Domain/Api/RtqMonitoringApi";
import { ICustomRenderer } from "./Domain/CustomRenderer";
import { TaskChainsTreeContainer } from "./containers/TaskChainsTreeContainer";
Expand All @@ -11,44 +13,52 @@ interface RemoteTaskQueueApplicationProps {
customRenderer: ICustomRenderer;
useErrorHandlingContainer: boolean;
isSuperUser: boolean;
customStateCaptions?: TaskStateDict;
customSearchHelp?: JSX.Element;
}

export const RemoteTaskQueueApplication = ({
isSuperUser,
rtqMonitoringApi,
customRenderer,
useErrorHandlingContainer,
customStateCaptions,
customSearchHelp,
}: RemoteTaskQueueApplicationProps): JSX.Element => (
<Routes>
<Route
path="/"
element={
<TasksPageContainer
isSuperUser={isSuperUser}
rtqMonitoringApi={rtqMonitoringApi}
useErrorHandlingContainer={useErrorHandlingContainer}
/>
}
/>
<Route
path="Tree"
element={
<TaskChainsTreeContainer
rtqMonitoringApi={rtqMonitoringApi}
useErrorHandlingContainer={useErrorHandlingContainer}
/>
}
/>
<Route
path=":id"
element={
<TaskDetailsPageContainer
isSuperUser={isSuperUser}
rtqMonitoringApi={rtqMonitoringApi}
customRenderer={customRenderer}
useErrorHandlingContainer={useErrorHandlingContainer}
/>
}
/>
</Routes>
<CustomSettingsProvider
customStateCaptions={customStateCaptions}
customSearchHelp={customSearchHelp}
customDetailRenderer={customRenderer}>
<Routes>
<Route
path="/"
element={
<TasksPageContainer
isSuperUser={isSuperUser}
rtqMonitoringApi={rtqMonitoringApi}
useErrorHandlingContainer={useErrorHandlingContainer}
/>
}
/>
<Route
path="Tree"
element={
<TaskChainsTreeContainer
rtqMonitoringApi={rtqMonitoringApi}
useErrorHandlingContainer={useErrorHandlingContainer}
/>
}
/>
<Route
path=":id"
element={
<TaskDetailsPageContainer
isSuperUser={isSuperUser}
rtqMonitoringApi={rtqMonitoringApi}
useErrorHandlingContainer={useErrorHandlingContainer}
/>
}
/>
</Routes>
</CustomSettingsProvider>
);
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ThemeContext } from "@skbkontur/react-ui";
import React from "react";

import { useCustomSettings } from "../../CustomSettingsContext";
import { RtqMonitoringTaskMeta } from "../../Domain/Api/RtqMonitoringTaskMeta";
import { Ticks } from "../../Domain/DataTypes/Time";
import { ticksToMilliseconds } from "../../Domain/Utils/ConvertTimeUtil";
Expand Down Expand Up @@ -34,6 +35,7 @@ export const TaskDetailsMetaTable = ({
childTaskIds,
}: TaskDetailsMetaTableProps): JSX.Element => {
const theme = React.useContext(ThemeContext);
const { customStateCaptions } = useCustomSettings();

const renderDate = (date?: Nullable<Ticks>): JSX.Element => (
<span>
Expand All @@ -57,7 +59,7 @@ export const TaskDetailsMetaTable = ({
</tr>,
<tr key="State">
<td>State</td>
<td data-tid="State">{state}</td>
<td data-tid="State">{customStateCaptions[state]}</td>
</tr>,
<tr key="Name">
<td>Name</td>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { Button, Link, Modal, ThemeContext } from "@skbkontur/react-ui";
import React from "react";
import { Location } from "react-router-dom";

import { useCustomSettings } from "../../CustomSettingsContext";
import { RtqMonitoringTaskModel } from "../../Domain/Api/RtqMonitoringTaskModel";
import { ICustomRenderer } from "../../Domain/CustomRenderer";
import { cancelableStates, rerunableStates } from "../../Domain/TaskStateExtensions";
import { searchRequestMapping } from "../../containers/TasksPageContainer";
import { Accordion } from "../Accordion/Accordion";
Expand All @@ -21,7 +21,6 @@ import { jsStyles } from "./TaskDetailsPage.styles";
export interface TaskDetailsPageProps {
parentLocation: string;
taskDetails: Nullable<RtqMonitoringTaskModel>;
customRenderer: ICustomRenderer;
getTaskLocation: (id: string) => string | Partial<Location>;
allowRerunOrCancel: boolean;
onRerun: (id: string) => void;
Expand All @@ -31,7 +30,6 @@ export interface TaskDetailsPageProps {
export function TaskDetailsPage({
parentLocation,
taskDetails,
customRenderer,
getTaskLocation,
allowRerunOrCancel,
onRerun,
Expand All @@ -40,6 +38,7 @@ export function TaskDetailsPage({
const [openedModal, setOpenedModal] = React.useState(false);
const [modalType, setModalType] = React.useState<"Cancel" | "Rerun">("Cancel");
const theme = React.useContext(ThemeContext);
const { customDetailRenderer } = useCustomSettings();

const rerun = () => {
setOpenedModal(true);
Expand All @@ -59,7 +58,7 @@ export function TaskDetailsPage({
}
const isCancelable = cancelableStates.includes(taskDetails.taskMeta.state);
const isRerunable = rerunableStates.includes(taskDetails.taskMeta.state);
const relatedTasksRequest = customRenderer.getRelatedTasksLocation(taskDetails);
const relatedTasksRequest = customDetailRenderer.getRelatedTasksLocation(taskDetails);
if (!isCancelable && !isRerunable && relatedTasksRequest == null) {
return null;
}
Expand Down Expand Up @@ -183,7 +182,7 @@ export function TaskDetailsPage({
<Fit className={jsStyles.taskDataContainer()}>
<Accordion
renderCaption={null}
renderValue={customRenderer.renderDetails}
renderValue={customDetailRenderer.renderDetails}
value={taskDetails.taskData}
title="TaskData"
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { ColumnStack, Fill, Fit, RowStack } from "@skbkontur/react-stack-layout";
import { Button, Input, Link, Modal, ThemeContext } from "@skbkontur/react-ui";
import { Button, Input, Link } from "@skbkontur/react-ui";
import React from "react";
import type { JSX } from "react";

import { RtqMonitoringSearchRequest } from "../../Domain/Api/RtqMonitoringSearchRequest";
import { DateTimeRangePicker } from "../DateTimeRangePicker/DateTimeRangePicker";
import { TaskStatesSelect } from "../TaskStatesSelect/TaskStatesSelect";
import { TaskTypesSelect } from "../TaskTypesSelect/TaskTypesSelect";

import { jsStyles } from "./TaskQueueFilter.styles";
import { TaskQueueFilterHelpModal } from "./TaskQueueFilterHelpModal";

export interface TaskQueueFilterProps {
value: RtqMonitoringSearchRequest;
Expand All @@ -23,7 +25,6 @@ export function TaskQueueFilter({
onSearchButtonClick,
}: TaskQueueFilterProps): JSX.Element {
const [openedModal, setOpenedModal] = React.useState(false);
const theme = React.useContext(ThemeContext);

const openModal = () => {
setOpenedModal(true);
Expand All @@ -33,75 +34,6 @@ export function TaskQueueFilter({
setOpenedModal(false);
};

const renderModal = (): JSX.Element => {
return (
<Modal data-tid="Modal" onClose={closeModal} width={900}>
<Modal.Header>
<span className={jsStyles.modalText(theme)}>Справка</span>
</Modal.Header>
<Modal.Body>
<div className={jsStyles.modalText(theme)}>
При поиске задач можно пользоваться следующими инструментами поиска:
<ol className={jsStyles.modalList()}>
<li>
Ввод значения без дополнительных указаний. В этом случае найдутся все задачи, в
текстовых полях которых встречается это значение.
</li>
<li>
Операторы AND, OR, NOT, скобки между ними. Например:{" "}
<code>(value1 OR value2) AND NOT value3</code>.
</li>
<li>
Ввод значения с указанием конкретного поля, в котором его нужно искать. Поля есть двух
типов: Meta и Data.SomeTaskName, в полях Meta лежит общая информация по задаче, в полях
Data.SomeTaskName - информация по типу задачи.
<br />
Например: <code>Meta.Id:value</code>,{" "}
<code>Data.FtpMessageDeliveryFinished.DocumentCirculationId:value</code>,
<code>Data.\*.DocumentCirculationId:(value1 OR value2)</code>. Конкретный список полей в
Meta можно узнать в RemoteTaskQueue.Monitoring.Storage.RtqElastcisearchSchema
</li>
<li>
Знаки * и ?. Например, <code>Meta.Name:val*</code> или <code>Meta.Name:val?e</code>.
Звездочка не может быть в начале искомого значения (<code>Meta.Name:*lue</code> не
работает).
<br />
Знаки можно использовать не только в значениях, но и при указании полей. По запросу{" "}
<code>Data.DocumentCirculationId.\*Id:value</code> во всех поля даты, которые
заканчиваются на id найдётся value.
</li>
<li>
Задание интервалов для дат. Квадратные скобки при задании интервала означают, что концы
включены, фигурные - что исключены. Звездочка показывает, что интервал в эту сторону
бесконечен.
<br />
Например: <code>date:[2012-01-01 TO 2012-12-31]</code>,
<code>date:&#123;* TO 2012-01-01&#125;.</code>
</li>
<li>
Задание интервалов для чисел аналогично заданию интервалов для дат. Например:{" "}
<code>count:[1 TO 5&#125;</code>.
</li>
<li>
Сравнение чисел. Например: <code>age:&gt;=10</code>,<code>age:&lt;=10</code>,{" "}
<code>age:(&gt;=10 AND &lt; 20)</code>.
</li>
<li>
Проверка полей на заполненность. Например: <code>NOT _exists_:Meta.ParentTaskId</code>-
поле пусто, <code>_exists_:Meta.ParentTaskId</code> - поле заполнено.
</li>
</ol>
</div>
</Modal.Body>
<Modal.Footer>
<div className={jsStyles.modalFooter()}>
<Button onClick={closeModal}>Закрыть</Button>
</div>
</Modal.Footer>
</Modal>
);
};

const { enqueueTimestampRange, queryString, states, names } = value;
const defaultEnqueueDateTimeRange = {
lowerBound: null,
Expand All @@ -128,7 +60,7 @@ export function TaskQueueFilter({
<Link onClick={openModal} data-tid="OpenModalButton">
Что можно ввести в строку поиска
</Link>
{openedModal && renderModal()}
{openedModal && <TaskQueueFilterHelpModal onClose={closeModal} />}
</Fit>
</ColumnStack>
</Fill>
Expand Down
Loading

0 comments on commit a6456eb

Please sign in to comment.