1
Fork 0
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:
Steffo 2021-05-25 15:38:10 +02:00
parent 8de3de0bf0
commit 2e7fdc9dcf
Signed by: steffo
GPG key ID: 6965406171929D01
6 changed files with 264 additions and 24 deletions

View file

@ -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",

View file

@ -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}>

View 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>
&nbsp;
<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>
)
}

View 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>
)
}

View file

@ -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`)}
/> />

View file

@ -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>
) )
} }