mirror of
https://github.com/pds-nest/nest.git
synced 2024-11-28 23:44:19 +00:00
🔧 Add window size to the alert creation
This commit is contained in:
parent
8de3de0bf0
commit
2e7fdc9dcf
6 changed files with 264 additions and 24 deletions
|
@ -71,6 +71,11 @@ export default {
|
||||||
alerts: "Allarmi",
|
alerts: "Allarmi",
|
||||||
alertTitle: "I tuoi allarmi",
|
alertTitle: "I tuoi allarmi",
|
||||||
alertCreate: "Crea un allarme",
|
alertCreate: "Crea un allarme",
|
||||||
|
alertName: "Nome allarme", // TODO: tradurre
|
||||||
|
createAlert: "Crea allarme", // TODO: tradurre
|
||||||
|
alertLimit: "Limite", // TODO: tradurre e migliorare?
|
||||||
|
alertWindow: "Finestra (in ore)", // TODO: tradurre
|
||||||
|
|
||||||
notImplemented: "🚧 Non implementato.",
|
notImplemented: "🚧 Non implementato.",
|
||||||
|
|
||||||
settings: "Impostazioni",
|
settings: "Impostazioni",
|
||||||
|
|
|
@ -27,7 +27,7 @@ export default function PageSwitcher({ ...props }) {
|
||||||
<Route path={"/repositories/:id/alerts/create"} exact={true}>
|
<Route path={"/repositories/:id/alerts/create"} exact={true}>
|
||||||
<PageRepositoryAlertsCreate/>
|
<PageRepositoryAlertsCreate/>
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={"/repositories/:id/alerts"} exact={true}>
|
<Route path={"/repositories/:id/alerts/"} exact={true}>
|
||||||
<PageRepositoryAlerts/>
|
<PageRepositoryAlerts/>
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={"/repositories/:id/share"} exact={true}>
|
<Route path={"/repositories/:id/share"} exact={true}>
|
||||||
|
|
109
nest_frontend/components/interactive/BoxAlertCreate.js
Normal file
109
nest_frontend/components/interactive/BoxAlertCreate.js
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
import React from "react"
|
||||||
|
import BoxFull from "../base/BoxFull"
|
||||||
|
import FormLabelled from "../base/FormLabelled"
|
||||||
|
import FormLabel from "../base/formparts/FormLabel"
|
||||||
|
import InputWithIcon from "../base/InputWithIcon"
|
||||||
|
import {
|
||||||
|
faBackward, faBell,
|
||||||
|
faFolder,
|
||||||
|
faPencilAlt,
|
||||||
|
faPlus, faStopwatch,
|
||||||
|
faThermometerThreeQuarters,
|
||||||
|
} from "@fortawesome/free-solid-svg-icons"
|
||||||
|
import Radio from "../base/Radio"
|
||||||
|
import Button from "../base/Button"
|
||||||
|
import useRepositoryEditor from "../../hooks/useRepositoryEditor"
|
||||||
|
import FormAlert from "../base/formparts/FormAlert"
|
||||||
|
import { useHistory } from "react-router"
|
||||||
|
import useStrings from "../../hooks/useStrings"
|
||||||
|
|
||||||
|
|
||||||
|
export default function BoxAlertCreate(
|
||||||
|
{
|
||||||
|
name,
|
||||||
|
setName,
|
||||||
|
evaluationMode,
|
||||||
|
setEvaluationMode,
|
||||||
|
limit,
|
||||||
|
setLimit,
|
||||||
|
windowSize,
|
||||||
|
setWindowSize,
|
||||||
|
running,
|
||||||
|
error,
|
||||||
|
save,
|
||||||
|
...props
|
||||||
|
}) {
|
||||||
|
|
||||||
|
const strings = useStrings()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BoxFull header={strings.createRepo} {...props}>
|
||||||
|
<FormLabelled
|
||||||
|
onSubmit={e => {
|
||||||
|
e.preventDefault()
|
||||||
|
save()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FormLabel htmlFor={"alert-name"} text={strings.alertName}>
|
||||||
|
<InputWithIcon
|
||||||
|
id={"alert-name"}
|
||||||
|
icon={faBell}
|
||||||
|
value={name}
|
||||||
|
onChange={e => setName(e.target.value)}
|
||||||
|
/>
|
||||||
|
</FormLabel>
|
||||||
|
<FormLabel htmlFor={"filter-mode"} text={strings.request}>
|
||||||
|
<label>
|
||||||
|
<Radio
|
||||||
|
name={"filter-mode"}
|
||||||
|
onChange={() => setEvaluationMode(0)}
|
||||||
|
checked={evaluationMode === 0}
|
||||||
|
/>
|
||||||
|
{strings.filterOR}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<Radio
|
||||||
|
name={"filter-mode"}
|
||||||
|
onChange={() => setEvaluationMode(1)}
|
||||||
|
checked={evaluationMode === 1}
|
||||||
|
/>
|
||||||
|
{strings.filterAND}
|
||||||
|
</label>
|
||||||
|
</FormLabel>
|
||||||
|
<FormLabel htmlFor={"alert-limit"} text={strings.alertLimit}>
|
||||||
|
<InputWithIcon
|
||||||
|
id={"alert-limit"}
|
||||||
|
type={"number"}
|
||||||
|
icon={faThermometerThreeQuarters}
|
||||||
|
value={limit}
|
||||||
|
onChange={e => setLimit(e.target.limit)}
|
||||||
|
/>
|
||||||
|
</FormLabel>
|
||||||
|
<FormLabel htmlFor={"alert-window"} text={strings.alertWindow}>
|
||||||
|
<InputWithIcon
|
||||||
|
id={"alert-window"}
|
||||||
|
type={"number"}
|
||||||
|
icon={faStopwatch}
|
||||||
|
value={windowSize}
|
||||||
|
onChange={e => setWindowSize(e.target.limit)}
|
||||||
|
/>
|
||||||
|
</FormLabel>
|
||||||
|
{error ?
|
||||||
|
<FormAlert color={"Red"}>
|
||||||
|
{strings[error.data.code]}
|
||||||
|
</FormAlert>
|
||||||
|
: null}
|
||||||
|
<Button
|
||||||
|
style={{ "gridColumn": "1 / 3" }}
|
||||||
|
icon={faPlus}
|
||||||
|
color={"Green"}
|
||||||
|
onClick={save}
|
||||||
|
disabled={running}
|
||||||
|
>
|
||||||
|
{strings.createAlert}
|
||||||
|
</Button>
|
||||||
|
</FormLabelled>
|
||||||
|
</BoxFull>
|
||||||
|
)
|
||||||
|
}
|
145
nest_frontend/components/providers/AlertEditor.js
Normal file
145
nest_frontend/components/providers/AlertEditor.js
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
import React, { useCallback, useState } from "react"
|
||||||
|
import ContextConditionEditor from "../../contexts/ContextConditionEditor"
|
||||||
|
import useArrayState from "../../hooks/useArrayState"
|
||||||
|
import Style from "./RepositoryEditor.module.css"
|
||||||
|
import BoxConditionLocation from "../interactive/BoxConditionLocation"
|
||||||
|
import BoxConditionHashtag from "../interactive/BoxConditionHashtag"
|
||||||
|
import BoxConditionUser from "../interactive/BoxConditionUser"
|
||||||
|
import BoxConditionDatetime from "../interactive/BoxConditionDatetime"
|
||||||
|
import BoxConditions from "../interactive/BoxConditions"
|
||||||
|
import classNames from "classnames"
|
||||||
|
import { Condition } from "../../objects/Condition"
|
||||||
|
import useBackendViewset from "../../hooks/useBackendViewset"
|
||||||
|
import { Redirect, useParams } from "react-router"
|
||||||
|
import BoxAlertCreate from "../interactive/BoxAlertCreate"
|
||||||
|
|
||||||
|
|
||||||
|
export default function AlertEditor({className}) {
|
||||||
|
/** The connected repository id. */
|
||||||
|
const {id: repoId} = useParams()
|
||||||
|
|
||||||
|
/** The alert name. */
|
||||||
|
const [_name, setName] = useState("")
|
||||||
|
|
||||||
|
/** The alert limit. */
|
||||||
|
const [limit, setLimit] = useState(10)
|
||||||
|
|
||||||
|
/** The window size. */
|
||||||
|
const [windowSize, setWindowSize] = useState(24)
|
||||||
|
|
||||||
|
/** The conditions of the data gathering. */
|
||||||
|
const {
|
||||||
|
value: rawConditions,
|
||||||
|
setValue: setRawConditions,
|
||||||
|
appendValue: appendRawCondition,
|
||||||
|
removeValue: removeRawCondition,
|
||||||
|
spliceValue: spliceRawCondition,
|
||||||
|
} = useArrayState([])
|
||||||
|
const _conditions = rawConditions.map(cond => Condition.fromRaw(cond))
|
||||||
|
|
||||||
|
/** The operator the conditions should be evaluated with. */
|
||||||
|
const [_evaluationMode, setEvaluationMode] = useState(0)
|
||||||
|
|
||||||
|
/** The backend viewset to use to create / edit the repository. */
|
||||||
|
const {running, error, createResource} = useBackendViewset(
|
||||||
|
`/api/v1/repositories/${repoId}/alerts/`,
|
||||||
|
"name",
|
||||||
|
{
|
||||||
|
list: false,
|
||||||
|
create: true,
|
||||||
|
retrieve: false,
|
||||||
|
edit: false,
|
||||||
|
destroy: false,
|
||||||
|
command: false,
|
||||||
|
action: false,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
/** If `true`, switches to the repository page on the next render. */
|
||||||
|
const [switchPage, setSwitchPage] = useState(false)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the current changes, creating or editing it as needed.
|
||||||
|
*
|
||||||
|
* @type {(function(): Promise<void>)|*}
|
||||||
|
*/
|
||||||
|
const save = useCallback(
|
||||||
|
async () => {
|
||||||
|
const body = {
|
||||||
|
"repository_id": repoId,
|
||||||
|
"name": _name,
|
||||||
|
"window_size": windowSize,
|
||||||
|
"limit": limit,
|
||||||
|
"evaluation_mode": _evaluationMode,
|
||||||
|
"conditions": _conditions,
|
||||||
|
}
|
||||||
|
|
||||||
|
console.info("Creating new alert with body: ", body)
|
||||||
|
await createResource(body)
|
||||||
|
setSwitchPage(true)
|
||||||
|
},
|
||||||
|
[repoId, createResource, _conditions, _evaluationMode, _name, limit],
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to add a new condition, logging a message to the console if something goes wrong.
|
||||||
|
*
|
||||||
|
* @type {(function(): void)|*}
|
||||||
|
*/
|
||||||
|
const addCondition = useCallback(
|
||||||
|
(newCond) => {
|
||||||
|
|
||||||
|
// Check for duplicates
|
||||||
|
let duplicate = null
|
||||||
|
for(const oldCond of _conditions) {
|
||||||
|
if(newCond.type === oldCond.type && newCond.content === oldCond.content) {
|
||||||
|
duplicate = oldCond
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(duplicate) {
|
||||||
|
console.debug("Cannot add ", newCond, ": ", duplicate, " already exists.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
console.debug("Adding ", newCond, " to the repository conditions")
|
||||||
|
appendRawCondition(newCond)
|
||||||
|
},
|
||||||
|
[_conditions, appendRawCondition],
|
||||||
|
)
|
||||||
|
|
||||||
|
// Hack to switch page on success
|
||||||
|
if(!error && switchPage) {
|
||||||
|
return <Redirect to={`/repositories/${repoId}/alerts/`}/>
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ContextConditionEditor.Provider
|
||||||
|
value={{
|
||||||
|
conditions: _conditions, addCondition, appendRawCondition, removeRawCondition, spliceRawCondition,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className={classNames(Style.RepositoryEditor, className)}>
|
||||||
|
<BoxConditionLocation className={Style.SearchByZone}/>
|
||||||
|
<BoxConditionHashtag className={Style.SearchByHashtags}/>
|
||||||
|
<BoxConditionUser className={Style.SearchByUser}/>
|
||||||
|
<BoxConditionDatetime className={Style.SearchByTimePeriod}/>
|
||||||
|
<BoxConditions className={Style.Conditions}/>
|
||||||
|
<BoxAlertCreate
|
||||||
|
className={Style.CreateDialog}
|
||||||
|
name={_name}
|
||||||
|
setName={setName}
|
||||||
|
evaluationMode={_evaluationMode}
|
||||||
|
setEvaluationMode={setEvaluationMode}
|
||||||
|
limit={limit}
|
||||||
|
setLimit={setLimit}
|
||||||
|
windowSize={windowSize}
|
||||||
|
setWindowSize={setWindowSize}
|
||||||
|
running={running}
|
||||||
|
error={error}
|
||||||
|
save={save}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</ContextConditionEditor.Provider>
|
||||||
|
)
|
||||||
|
}
|
|
@ -52,7 +52,7 @@ export default function PageRepositoriesList() {
|
||||||
repositories={bv.resources.filter(r => r.is_active)}
|
repositories={bv.resources.filter(r => r.is_active)}
|
||||||
view={pk => history.push(`/repositories/${pk}`)}
|
view={pk => history.push(`/repositories/${pk}`)}
|
||||||
share={pk => history.push(`/repositories/${pk}/share`)}
|
share={pk => history.push(`/repositories/${pk}/share`)}
|
||||||
alerts={pk => history.push(`/repositories/${pk}/alerts`)}
|
alerts={pk => history.push(`/repositories/${pk}/alerts/`)}
|
||||||
archive={archive}
|
archive={archive}
|
||||||
edit={pk => history.push(`/repositories/${pk}/edit`)}
|
edit={pk => history.push(`/repositories/${pk}/edit`)}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,32 +1,15 @@
|
||||||
import React, { useContext } from "react"
|
import React, { useContext } from "react"
|
||||||
import BoxFull from "../components/base/BoxFull"
|
|
||||||
import ContextLanguage from "../contexts/ContextLanguage"
|
import ContextLanguage from "../contexts/ContextLanguage"
|
||||||
import BoxHeader from "../components/base/BoxHeader"
|
import BoxHeader from "../components/base/BoxHeader"
|
||||||
import { useHistory, useParams } from "react-router"
|
import { useParams } from "react-router"
|
||||||
import { faPlus } from "@fortawesome/free-solid-svg-icons"
|
import { faPlus } from "@fortawesome/free-solid-svg-icons"
|
||||||
import PageWithHeader from "../components/base/layout/PageWithHeader"
|
import PageWithHeader from "../components/base/layout/PageWithHeader"
|
||||||
import makeIcon from "../utils/makeIcon"
|
import makeIcon from "../utils/makeIcon"
|
||||||
import useBackendViewset from "../hooks/useBackendViewset"
|
import AlertEditor from "../components/providers/AlertEditor"
|
||||||
|
|
||||||
|
|
||||||
export default function PageRepositoryAlertsCreate() {
|
export default function PageRepositoryAlertsCreate() {
|
||||||
const { strings } = useContext(ContextLanguage)
|
const { strings } = useContext(ContextLanguage)
|
||||||
const { id } = useParams()
|
|
||||||
const history = useHistory()
|
|
||||||
|
|
||||||
const {createResource} = useBackendViewset(
|
|
||||||
`/api/v1/repositories/${id}/alerts/`,
|
|
||||||
"name",
|
|
||||||
{
|
|
||||||
list: false,
|
|
||||||
create: true,
|
|
||||||
retrieve: false,
|
|
||||||
edit: false,
|
|
||||||
destroy: false,
|
|
||||||
command: false,
|
|
||||||
action: false,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageWithHeader
|
<PageWithHeader
|
||||||
|
@ -36,9 +19,7 @@ export default function PageRepositoryAlertsCreate() {
|
||||||
</BoxHeader>
|
</BoxHeader>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<BoxFull header={strings.alertTitle}>
|
<AlertEditor/>
|
||||||
{strings.notImplemented}
|
|
||||||
</BoxFull>
|
|
||||||
</PageWithHeader>
|
</PageWithHeader>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue