diff --git a/nest_frontend/LocalizationStrings.js b/nest_frontend/LocalizationStrings.js index b254c31..335e0a9 100644 --- a/nest_frontend/LocalizationStrings.js +++ b/nest_frontend/LocalizationStrings.js @@ -71,6 +71,11 @@ export default { alerts: "Allarmi", alertTitle: "I tuoi allarmi", 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.", settings: "Impostazioni", @@ -141,7 +146,8 @@ export default { errorViewNotAllowed: "Errore: Non è permesso effettuare la richiesta.", errorServerNotConfigured: "Errore: Non è stato configurato nessun server.", errorDecodeError: "Errore: Non è stato possibile deserializzare i dati ricevuti dal backend.", - errorSerializationError: "Errore: Non è stato possibile serializzare i dati da inviare al backend." + errorSerializationError: "Errore: Non è stato possibile serializzare i dati da inviare al backend.", + errorPageNotFound: "Errore: Pagina non trovata.", // TODO: Tradurre }, // 🇬🇧 en: { diff --git a/nest_frontend/PageSwitcher.js b/nest_frontend/PageSwitcher.js index 28d5177..41e3cb3 100644 --- a/nest_frontend/PageSwitcher.js +++ b/nest_frontend/PageSwitcher.js @@ -9,15 +9,25 @@ import PageRepositoryEdit from "./routes/PageRepositoryEdit" import PageUsers from "./routes/PageUsers" import PageRepositoryAnalyze from "./routes/PageRepositoryAnalyze" import PageShare from "./routes/PageShare" +import { faQuestionCircle } from "@fortawesome/free-solid-svg-icons" +import makeIcon from "./utils/makeIcon" +import useStrings from "./hooks/useStrings" +import Alert from "./components/base/Alert" +import PageRepositoryAlertsCreate from "./routes/PageRepositoryAlertsCreate" export default function PageSwitcher({ ...props }) { + const strings = useStrings() + return ( - + + + + @@ -38,9 +48,12 @@ export default function PageSwitcher({ ...props }) { - + + + {makeIcon(faQuestionCircle)} {strings.errorPageNotFound} + ) } diff --git a/nest_frontend/components/base/ButtonHeader.js b/nest_frontend/components/base/ButtonHeader.js new file mode 100644 index 0000000..a3e8b50 --- /dev/null +++ b/nest_frontend/components/base/ButtonHeader.js @@ -0,0 +1,19 @@ +import React from "react" +import Style from "./ButtonHeader.module.css" +import classNames from "classnames" +import Button from "./Button" + + +/** + * A {@link Button} without `boxShadow` and with `flexGrow`, to be used in {@link PageWithHeader}. + * + * @param className - Additional class(es) to add to the button. + * @param props - Additional props to pass to the button. + * @returns {JSX.Element} + * @constructor + */ +export default function ButtonHeader({ className, ...props }) { + return ( + + + + ) +} diff --git a/nest_frontend/components/interactive/BoxRepositoryCreate.js b/nest_frontend/components/interactive/BoxRepositoryCreate.js index 40182a3..80c0a85 100644 --- a/nest_frontend/components/interactive/BoxRepositoryCreate.js +++ b/nest_frontend/components/interactive/BoxRepositoryCreate.js @@ -15,24 +15,33 @@ import useStrings from "../../hooks/useStrings" /** * A {@link BoxFull} allowing the user to save the changes made in the current {@link RepositoryEditor}. * + * @param id - The id of the repository. + * @param name - The current name of the repository. + * @param setName - Function to change the name of the repository. + * @param evaluationMode - The current evaluation mode of the repository. + * @param setEvaluationMode - Function to change the current evaluation mode of the repository. * @param running - If a request is running, disabling the buttons. + * @param error - If a request error occoured, the error. + * @param revert - Function to cancel the changes made to the repository. + * @param save - Function to apply the changes made to the repository. * @param props - Additional props to pass to the box. * @returns {JSX.Element} * @constructor */ -export default function BoxRepositoryCreate({ running, ...props }) { - const { +export default function BoxRepositoryCreate( + { id, - evaluationMode, - setEvaluationMode, name, setName, - save, - revert, + evaluationMode, + setEvaluationMode, + running, error, - } = useRepositoryEditor() + revert, + save, + ...props + }) { - const history = useHistory() const strings = useStrings() return ( diff --git a/nest_frontend/components/providers/AlertEditor.js b/nest_frontend/components/providers/AlertEditor.js new file mode 100644 index 0000000..ab365a2 --- /dev/null +++ b/nest_frontend/components/providers/AlertEditor.js @@ -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)|*} + */ + 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 + } + + return ( + +
+ + + + + + +
+
+ ) +} diff --git a/nest_frontend/components/providers/RepositoryEditor.js b/nest_frontend/components/providers/RepositoryEditor.js index 7c2fa94..c8cb367 100644 --- a/nest_frontend/components/providers/RepositoryEditor.js +++ b/nest_frontend/components/providers/RepositoryEditor.js @@ -1,5 +1,5 @@ import React, { useCallback, useContext, useState } from "react" -import ContextRepositoryEditor from "../../contexts/ContextRepositoryEditor" +import ContextConditionEditor from "../../contexts/ContextConditionEditor" import useArrayState from "../../hooks/useArrayState" import Style from "./RepositoryEditor.module.css" import BoxConditionLocation from "../interactive/BoxConditionLocation" @@ -142,14 +142,9 @@ export default function RepositoryEditor({ } return ( -
@@ -158,8 +153,19 @@ export default function RepositoryEditor({ - +
-
+ ) } diff --git a/nest_frontend/components/providers/RepositoryViewer.js b/nest_frontend/components/providers/RepositoryViewer.js index 5e402ea..94c73de 100644 --- a/nest_frontend/components/providers/RepositoryViewer.js +++ b/nest_frontend/components/providers/RepositoryViewer.js @@ -1,6 +1,5 @@ import React, { useContext, useMemo, useState } from "react" import Style from "./RepositoryViewer.module.css" -import classNames from "classnames" import ContextLanguage from "../../contexts/ContextLanguage" import useBackendResource from "../../hooks/useBackendResource" import useBackendViewset from "../../hooks/useBackendViewset" @@ -9,7 +8,7 @@ import countTweetWords from "../../utils/countTweetWords" import BoxHeader from "../base/BoxHeader" import Loading from "../base/Loading" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" -import { faFolder, faFolderOpen, faTrash } from "@fortawesome/free-solid-svg-icons" +import { faFolder, faFolderOpen, faPlus, faTrash } from "@fortawesome/free-solid-svg-icons" import BoxRepositoryTweets from "../interactive/BoxRepositoryTweets" import PickerVisualization from "../interactive/PickerVisualization" import BoxVisualizationWordcloud from "../interactive/BoxVisualizationWordcloud" @@ -29,6 +28,9 @@ import BoxFilterDatetime from "../interactive/BoxFilterDatetime" import BoxFilterHasPlace from "../interactive/BoxFilterHasPlace" import BoxFilterHasImage from "../interactive/BoxFilterHasImage" import BoxFilterIsNotRetweet from "../interactive/BoxFilterIsNotRetweet" +import PageWithHeader from "../base/layout/PageWithHeader" +import ButtonHeader from "../base/ButtonHeader" +import AlertError from "../interactive/AlertError" export default function RepositoryViewer({ id, className, ...props }) { @@ -49,7 +51,11 @@ export default function RepositoryViewer({ id, className, ...props }) { const mapViewHook = useMapAreaState() // Repository - const repositoryBr = useBackendResource( + const { + resource: repository, + error: repositoryError, + firstLoad: repositoryFirstLoad, + } = useBackendResource( `/api/v1/repositories/${id}`, { retrieve: true, @@ -58,11 +64,13 @@ export default function RepositoryViewer({ id, className, ...props }) { action: false, }, ) - const repository = repositoryBr.error ? null : repositoryBr.resource - // Tweets - const rawTweetsBv = useBackendViewset( + const { + resources: tweets, + error: tweetsError, + firstLoad: tweetsFirstLoad, + } = useBackendViewset( `/api/v1/repositories/${id}/tweets/`, "snowflake", { @@ -75,63 +83,73 @@ export default function RepositoryViewer({ id, className, ...props }) { action: false, }, ) - const rawTweets = rawTweetsBv.resources && rawTweetsBv.error ? [] : rawTweetsBv.resources - // Filtering - let tweets = rawTweets - for(const filter of filters) { - tweets = tweets.filter(tweet => filter.exec(tweet)) - } - + const filteredTweets = useMemo( + () => { + let tempTweets = tweets + for(const filter of filters) { + tempTweets = tempTweets.filter(tweet => filter.exec(tweet)) + } + return tempTweets + }, + [tweets, filters] + ) // Words const words = useMemo( - () => objectToWordcloudFormat(countTweetWords(tweets)), - [tweets], + () => objectToWordcloudFormat(countTweetWords(filteredTweets)), + [filteredTweets], ) - - let contents - if(!repositoryBr.firstLoad || !rawTweetsBv.firstLoad) { - contents = <> - - - - - } - else if(repository === null) { - contents = <> - - {strings.repoDeleted} - - - } - else { - contents = <> - - {repository.name} + // Page header + const header = useMemo( + () => ( + + {repository?.name} + ), + [repository?.is_active, repository?.name] + ) - - - {visualizationTab === "wordcloud" ? : null} - {visualizationTab === "chart" ? : null} - {visualizationTab === "map" ? : null} - {visualizationTab === "stats" ? : null} + // Page contents + const contents = useMemo( + () => { + if(!repositoryFirstLoad || !tweetsFirstLoad) { + return + } + else if(repositoryError) { + return + } + else if(tweetsError) { + return + } + else { + return ( +
+ + + {visualizationTab === "wordcloud" ? : null} + {visualizationTab === "chart" ? : null} + {visualizationTab === "map" ? : null} + {visualizationTab === "stats" ? : null} - - - {filterTab === "contains" ? : null} - {filterTab === "hashtag" ? : null} - {filterTab === "user" ? : null} - {filterTab === "retweet" ? : null} - {filterTab === "image" ? : null} - {filterTab === "time" ? : null} - {filterTab === "place" ? : null} - {filterTab === "location" ? : null} - - } + + + {filterTab === "contains" ? : null} + {filterTab === "hashtag" ? : null} + {filterTab === "user" ? : null} + {filterTab === "retweet" ? : null} + {filterTab === "image" ? : null} + {filterTab === "time" ? : null} + {filterTab === "place" ? : null} + {filterTab === "location" ? : null} +
+ ) + } + }, + [repositoryFirstLoad, tweetsFirstLoad, visualizationTab, filterTab] + ) return ( - -
+ {contents} -
- +
) } diff --git a/nest_frontend/components/providers/RepositoryViewer.module.css b/nest_frontend/components/providers/RepositoryViewer.module.css index c678bc5..403db53 100644 --- a/nest_frontend/components/providers/RepositoryViewer.module.css +++ b/nest_frontend/components/providers/RepositoryViewer.module.css @@ -2,7 +2,6 @@ display: grid; grid-template-areas: - "h h" "a b" "a c" "d e" @@ -10,16 +9,12 @@ grid-gap: 10px; grid-template-columns: 1fr 1fr; - grid-template-rows: auto auto 1fr auto auto; + grid-template-rows: auto 1fr auto auto; width: 100%; height: 100%; } -.Header { - grid-area: h; -} - .Tweets { grid-area: a; } diff --git a/nest_frontend/contexts/ContextRepositoryEditor.js b/nest_frontend/contexts/ContextConditionEditor.js similarity index 57% rename from nest_frontend/contexts/ContextRepositoryEditor.js rename to nest_frontend/contexts/ContextConditionEditor.js index 3364c6b..a68a4c0 100644 --- a/nest_frontend/contexts/ContextRepositoryEditor.js +++ b/nest_frontend/contexts/ContextConditionEditor.js @@ -2,7 +2,7 @@ import { createContext } from "react" /** - * React Context representing containing all variables of a {@link RepositoryEditor}. + * React Context representing a list of {@link Condition}s as provided by {@link useArrayState}. * * It is `null` outside a RepositoryEditor. */ diff --git a/nest_frontend/hooks/useRepositoryEditor.js b/nest_frontend/hooks/useRepositoryEditor.js index 143f665..dedfce2 100644 --- a/nest_frontend/hooks/useRepositoryEditor.js +++ b/nest_frontend/hooks/useRepositoryEditor.js @@ -1,5 +1,5 @@ import { useContext } from "react" -import ContextRepositoryEditor from "../contexts/ContextRepositoryEditor" +import ContextRepositoryEditor from "../contexts/ContextConditionEditor" /** diff --git a/nest_frontend/routes/PageLogin.js b/nest_frontend/routes/PageLogin.js index fcea5fe..f8786b9 100644 --- a/nest_frontend/routes/PageLogin.js +++ b/nest_frontend/routes/PageLogin.js @@ -1,6 +1,4 @@ import React, { useContext } from "react" -import Style from "./PageLogin.module.css" -import classNames from "classnames" import BoxSetServer from "../components/interactive/BoxSetServer" import BoxLogin from "../components/interactive/BoxLogin" import ContextUser from "../contexts/ContextUser" @@ -9,9 +7,11 @@ import BoxHeader from "../components/base/BoxHeader" import useStrings from "../hooks/useStrings" import BoxFull from "../components/base/BoxFull" import SelectLanguage from "../components/interactive/SelectLanguage" +import PageWithHeader from "../components/base/layout/PageWithHeader" +import BodyFlex from "../components/base/layout/BodyFlex" -export default function PageLogin({ className, ...props }) { +export default function PageLogin() { const {user} = useContext(ContextUser) const strings = useStrings() @@ -20,15 +20,20 @@ export default function PageLogin({ className, ...props }) { } return ( -
- - {strings.welcomeToNest} - - - - - - -
+ + {strings.welcomeToNest} +
+ } + > + + + + + + + + ) } diff --git a/nest_frontend/routes/PageRepositoriesList.js b/nest_frontend/routes/PageRepositoriesList.js index 24c7ed8..840fde2 100644 --- a/nest_frontend/routes/PageRepositoriesList.js +++ b/nest_frontend/routes/PageRepositoriesList.js @@ -1,6 +1,4 @@ import React, { useCallback, useContext } from "react" -import Style from "./PageRepositoriesList.module.css" -import classNames from "classnames" import useBackendViewset from "../hooks/useBackendViewset" import BoxRepositories from "../components/interactive/BoxRepositories" import { useHistory } from "react-router" @@ -8,10 +6,12 @@ import ContextLanguage from "../contexts/ContextLanguage" import BoxHeader from "../components/base/BoxHeader" import { faHome, faPlus } from "@fortawesome/free-solid-svg-icons" import {FontAwesomeIcon} from "@fortawesome/react-fontawesome" -import Button from "../components/base/Button" +import PageWithHeader from "../components/base/layout/PageWithHeader" +import ButtonHeader from "../components/base/ButtonHeader" +import BodyHorizontalHalves from "../components/base/layout/BodyHorizontalHalves" -export default function PageRepositoriesList({ children, className, ...props }) { +export default function PageRepositoriesList() { const bv = useBackendViewset("/api/v1/repositories/", "id") const history = useHistory() const { strings } = useContext(ContextLanguage) @@ -27,40 +27,47 @@ export default function PageRepositoriesList({ children, className, ...props }) ) return ( -
- - {strings.dashboard} - -
- -
- r.is_active)} - view={pk => history.push(`/repositories/${pk}`)} - share={pk => history.push(`/repositories/${pk}/share`)} - alerts={pk => history.push(`/repositories/${pk}/alerts`)} - archive={archive} - edit={pk => history.push(`/repositories/${pk}/edit`)} + + } + > + r.is_active)} + view={pk => history.push(`/repositories/${pk}`)} + share={pk => history.push(`/repositories/${pk}/share`)} + alerts={pk => history.push(`/repositories/${pk}/alerts/`)} + archive={archive} + edit={pk => history.push(`/repositories/${pk}/edit`)} + /> + } + lower={ + !r.is_active)} + view={pk => history.push(`/repositories/${pk}`)} + destroy={bv.destroyResource} + /> + } /> - !r.is_active)} - view={pk => history.push(`/repositories/${pk}`)} - destroy={bv.destroyResource} - /> -
+ ) } diff --git a/nest_frontend/routes/PageRepositoriesList.module.css b/nest_frontend/routes/PageRepositoriesList.module.css deleted file mode 100644 index 7feeae5..0000000 --- a/nest_frontend/routes/PageRepositoriesList.module.css +++ /dev/null @@ -1,39 +0,0 @@ -.PageRepositories { - display: grid; - - grid-template-areas: - "h x" - "a a" - "b b"; - grid-template-columns: 4fr 1fr; - grid-template-rows: auto 1fr 1fr; - grid-gap: 10px; - - width: 100%; - height: 100%; -} - -.Header { - grid-area: h; -} - -.Buttons { - grid-area: x; - - display: flex; - flex-direction: row; - align-items: stretch; -} - -.Buttons > * { - box-shadow: none; - flex-grow: 1; -} - -.ActiveRepositories { - grid-area: a; -} - -.ArchivedRepositories { - grid-area: b; -} diff --git a/nest_frontend/routes/PageRepositoryAlerts.js b/nest_frontend/routes/PageRepositoryAlerts.js index dc8eaa5..02a2a1f 100644 --- a/nest_frontend/routes/PageRepositoryAlerts.js +++ b/nest_frontend/routes/PageRepositoryAlerts.js @@ -1,27 +1,39 @@ import React, { useContext } from "react" -import Style from "./PageRepositoryAlerts.module.css" -import classNames from "classnames" import BoxFull from "../components/base/BoxFull" import ContextLanguage from "../contexts/ContextLanguage" import BoxHeader from "../components/base/BoxHeader" -import { useParams } from "react-router" +import { useHistory, useParams } from "react-router" +import { faBell, faPlus } from "@fortawesome/free-solid-svg-icons" +import PageWithHeader from "../components/base/layout/PageWithHeader" +import ButtonHeader from "../components/base/ButtonHeader" +import makeIcon from "../utils/makeIcon" -export default function PageRepositoryAlerts({ children, className, ...props }) { +export default function PageRepositoryAlerts() { const { strings } = useContext(ContextLanguage) const { id } = useParams() + const history = useHistory() return ( -
- - {strings.alerts} - - + + {makeIcon(faBell)} {strings.alerts} + + } + buttons={ + history.push(`/repositories/${id}/alerts/create`)} + > + {strings.alertCreate} + + } + > + {strings.notImplemented} - - {strings.notImplemented} - -
+ ) } diff --git a/nest_frontend/routes/PageRepositoryAlerts.module.css b/nest_frontend/routes/PageRepositoryAlerts.module.css deleted file mode 100644 index f1dd8d9..0000000 --- a/nest_frontend/routes/PageRepositoryAlerts.module.css +++ /dev/null @@ -1,26 +0,0 @@ -.PageAlerts { - display: grid; - - grid-template-areas: - "a" - "b" - "c"; - grid-template-rows: auto 1fr 1fr; - - grid-gap: 10px; - - width: 100%; - height: 100%; -} - -.Header { - grid-area: a; -} - -.YourAlerts { - grid-area: b; -} - -.CreateAlert { - grid-area: c; -} \ No newline at end of file diff --git a/nest_frontend/routes/PageRepositoryAlertsCreate.js b/nest_frontend/routes/PageRepositoryAlertsCreate.js new file mode 100644 index 0000000..6ecb44e --- /dev/null +++ b/nest_frontend/routes/PageRepositoryAlertsCreate.js @@ -0,0 +1,25 @@ +import React, { useContext } from "react" +import ContextLanguage from "../contexts/ContextLanguage" +import BoxHeader from "../components/base/BoxHeader" +import { useParams } from "react-router" +import { faPlus } from "@fortawesome/free-solid-svg-icons" +import PageWithHeader from "../components/base/layout/PageWithHeader" +import makeIcon from "../utils/makeIcon" +import AlertEditor from "../components/providers/AlertEditor" + + +export default function PageRepositoryAlertsCreate() { + const { strings } = useContext(ContextLanguage) + + return ( + + {makeIcon(faPlus)} {strings.alertCreate} + + } + > + + + ) +} diff --git a/nest_frontend/routes/PageRepositoryAnalyze.js b/nest_frontend/routes/PageRepositoryAnalyze.js index f71d530..cfa67ed 100644 --- a/nest_frontend/routes/PageRepositoryAnalyze.js +++ b/nest_frontend/routes/PageRepositoryAnalyze.js @@ -3,7 +3,7 @@ import { useParams } from "react-router" import RepositoryViewer from "../components/providers/RepositoryViewer" -export default function PageRepositoryAnalyze({ className, ...props }) { +export default function PageRepositoryAnalyze({...props }) { const { id } = useParams() return ( diff --git a/nest_frontend/routes/PageRepositoryCreate.js b/nest_frontend/routes/PageRepositoryCreate.js index b846d06..f8892f1 100644 --- a/nest_frontend/routes/PageRepositoryCreate.js +++ b/nest_frontend/routes/PageRepositoryCreate.js @@ -1,20 +1,24 @@ import React, { useContext } from "react" -import Style from "./PageRepositoryCreate.module.css" -import classNames from "classnames" import BoxHeader from "../components/base/BoxHeader" import RepositoryEditor from "../components/providers/RepositoryEditor" import ContextLanguage from "../contexts/ContextLanguage" +import PageWithHeader from "../components/base/layout/PageWithHeader" +import makeIcon from "../utils/makeIcon" +import { faPlus } from "@fortawesome/free-solid-svg-icons" -export default function PageRepositoryCreate({ children, className, ...props }) { +export default function PageRepositoryCreate() { const { strings } = useContext(ContextLanguage) return ( -
- - {strings.dashboardTitle} - - -
+ + {makeIcon(faPlus)} {strings.dashboardTitle} + + } + > + + ) } diff --git a/nest_frontend/routes/PageRepositoryCreate.module.css b/nest_frontend/routes/PageRepositoryCreate.module.css deleted file mode 100644 index f062f37..0000000 --- a/nest_frontend/routes/PageRepositoryCreate.module.css +++ /dev/null @@ -1,21 +0,0 @@ -.PageHome { - display: grid; - - grid-template-areas: - "a" - "b"; - grid-template-rows: auto 1fr; - - grid-gap: 10px; - - width: 100%; - height: 100%; -} - -.Header { - grid-area: a; -} - -.RepositoryEditor { - grid-area: b; -} diff --git a/nest_frontend/routes/PageRepositoryEdit.js b/nest_frontend/routes/PageRepositoryEdit.js index 6dfed22..8619f68 100644 --- a/nest_frontend/routes/PageRepositoryEdit.js +++ b/nest_frontend/routes/PageRepositoryEdit.js @@ -1,6 +1,4 @@ import React, { useContext } from "react" -import Style from "./PageRepositoryCreate.module.css" -import classNames from "classnames" import BoxHeader from "../components/base/BoxHeader" import RepositoryEditor from "../components/providers/RepositoryEditor" import useBackendImmediately from "../hooks/useBackendImmediately" @@ -8,9 +6,12 @@ import ContextUser from "../contexts/ContextUser" import renderContents from "../utils/renderContents" import { useParams } from "react-router" import ContextLanguage from "../contexts/ContextLanguage" +import PageWithHeader from "../components/base/layout/PageWithHeader" +import makeIcon from "../utils/makeIcon" +import { faPencilAlt } from "@fortawesome/free-solid-svg-icons" -export default function PageRepositoryEdit({ className, ...props }) { +export default function PageRepositoryEdit() { const { strings } = useContext(ContextLanguage) const { id } = useParams() @@ -18,18 +19,18 @@ export default function PageRepositoryEdit({ className, ...props }) { const repositoryRequest = useBackendImmediately(fetchDataAuth, "GET", `/api/v1/repositories/${id}`) const contents = renderContents( repositoryRequest, - data => { - console.debug("Data: ", data) - return - }, + data => ) return ( -
- - {strings.repoEdit} - + + {makeIcon(faPencilAlt)} {strings.repoEdit} + + } + > {contents} -
+ ) } diff --git a/nest_frontend/routes/PageSettings.js b/nest_frontend/routes/PageSettings.js index a1f132c..6ee0a4f 100644 --- a/nest_frontend/routes/PageSettings.js +++ b/nest_frontend/routes/PageSettings.js @@ -1,25 +1,36 @@ import React, { useContext } from "react" -import Style from "./PageSettings.module.css" -import classNames from "classnames" import BoxFull from "../components/base/BoxFull" import SelectTheme from "../components/interactive/SelectTheme" import BoxLoggedIn from "../components/interactive/BoxLoggedIn" import SelectLanguage from "../components/interactive/SelectLanguage" import ContextLanguage from "../contexts/ContextLanguage" +import PageWithHeader from "../components/base/layout/PageWithHeader" +import BoxHeader from "../components/base/BoxHeader" +import { faCog } from "@fortawesome/free-solid-svg-icons" +import makeIcon from "../utils/makeIcon" +import BodyFlex from "../components/base/layout/BodyFlex" -export default function PageSettings({ children, className, ...props }) { +export default function PageSettings() { const { strings } = useContext(ContextLanguage) return ( -
- - - - - - - -
+ + {makeIcon(faCog)} {strings.settings} + + } + > + + + + + + + + + + ) } diff --git a/nest_frontend/routes/PageSettings.module.css b/nest_frontend/routes/PageSettings.module.css deleted file mode 100644 index 1f6bff8..0000000 --- a/nest_frontend/routes/PageSettings.module.css +++ /dev/null @@ -1,9 +0,0 @@ -.PageSettings { - display: flex; - flex-direction: column; - - grid-gap: 10px; - - width: 100%; - height: 100%; -} diff --git a/nest_frontend/routes/PageShare.js b/nest_frontend/routes/PageShare.js index 2067461..d075052 100644 --- a/nest_frontend/routes/PageShare.js +++ b/nest_frontend/routes/PageShare.js @@ -1,17 +1,16 @@ import React, { useCallback, useContext } from "react" -import classNames from "classnames" import BoxHeader from "../components/base/BoxHeader" -import ContextLanguage from "../contexts/ContextLanguage" -import Style from "./PageShare.module.css" import BoxUserList from "../components/interactive/BoxUserList" import useBackendViewset from "../hooks/useBackendViewset" import { useParams } from "react-router" import ContextUser from "../contexts/ContextUser" -import Alert from "../components/base/Alert" import useStrings from "../hooks/useStrings" +import PageWithHeader from "../components/base/layout/PageWithHeader" +import BodyHorizontalHalves from "../components/base/layout/BodyHorizontalHalves" +import AlertError from "../components/interactive/AlertError" -export default function PageShare({ className, ...props }) { +export default function PageShare() { const strings = useStrings() const { user: loggedUser } = useContext(ContextUser) const { id } = useParams() @@ -71,30 +70,35 @@ export default function PageShare({ className, ...props }) { ) return ( -
- - {strings.repoShare} - - user["email"] !== loggedUser["email"] && !authorizations.map(a => a.email).includes(user.email))} - shareWithUser={shareWith} - header={strings.availableUsers} - running={usersBvRunning && authBvRunning} + + {strings.repoShare} + + } + > + user["email"] !== loggedUser["email"] && !authorizations.map(a => a.email).includes(user.email))} + shareWithUser={shareWith} + header={strings.availableUsers} + running={usersBvRunning || authBvRunning} + /> + } + lower={<> + user["email"] === loggedUser["email"] || authorizations.map(a => a.email).includes(user.email))} + unshareWithUser={unshareWith} + header={strings.sharingWith} + running={usersBvRunning || authBvRunning} + /> + } + error={<> + {authBvError ? : null} + {usersBvError ? : null} + } /> - user["email"] === loggedUser["email"] || authorizations.map(a => a.email).includes(user.email))} - unshareWithUser={unshareWith} - header={strings.sharingWith} - running={usersBvRunning && authBvRunning} - /> - {authBvError ? - {strings[authBvError?.data?.code ?? "errorUnknownError"]} - : null} - {usersBvError ? - {strings[usersBvError?.data?.code ?? "errorUnknownError"]} - : null} -
+ ) } diff --git a/nest_frontend/routes/PageShare.module.css b/nest_frontend/routes/PageShare.module.css deleted file mode 100644 index 474f9e1..0000000 --- a/nest_frontend/routes/PageShare.module.css +++ /dev/null @@ -1,32 +0,0 @@ -.PageShare { - display: grid; - - grid-template-areas: - "a" - "b" - "c" - "d"; - grid-template-rows: auto 1fr 1fr auto; - grid-template-columns: 1fr; - grid-gap: 10px; - - width: 100%; - height: 100%; -} - - -.Header { - grid-area: a; -} - -.UserList { - grid-area: b; -} - -.SharingWith { - grid-area: c; -} - -.Error { - grid-area: d; -} \ No newline at end of file diff --git a/nest_frontend/routes/PageUsers.js b/nest_frontend/routes/PageUsers.js index e216cef..c897734 100644 --- a/nest_frontend/routes/PageUsers.js +++ b/nest_frontend/routes/PageUsers.js @@ -1,29 +1,45 @@ import React, { useContext } from "react" -import Style from "./PageUsers.module.css" -import classNames from "classnames" import BoxHeader from "../components/base/BoxHeader" import BoxUserCreate from "../components/interactive/BoxUserCreate" import useBackendViewset from "../hooks/useBackendViewset" import BoxUserList from "../components/interactive/BoxUserList" import ContextLanguage from "../contexts/ContextLanguage" -import Alert from "../components/base/Alert" +import PageWithHeader from "../components/base/layout/PageWithHeader" +import { faUserCog } from "@fortawesome/free-solid-svg-icons" +import makeIcon from "../utils/makeIcon" +import AlertError from "../components/interactive/AlertError" +import BodyHorizontalUpperGrow from "../components/base/layout/BodyHorizontalUpperGrow" -export default function PageUsers({ children, className, ...props }) { +export default function PageUsers() { const { strings } = useContext(ContextLanguage) - const bv = useBackendViewset("/api/v1/users/", "email") + const {createResource, running, resources, destroyResource, error} = useBackendViewset("/api/v1/users/", "email") return ( -
- - {strings.manageUsers} - - - - {bv.error ? - {strings[bv.error?.data?.code ?? "errorUnknownError"]} - : null} -
+ + {makeIcon(faUserCog)} {strings.manageUsers} + + } + > + + } + lower={ + + } + error={error ? : null} + /> + ) } diff --git a/nest_frontend/routes/PageUsers.module.css b/nest_frontend/routes/PageUsers.module.css deleted file mode 100644 index 6731e46..0000000 --- a/nest_frontend/routes/PageUsers.module.css +++ /dev/null @@ -1,32 +0,0 @@ -.PageUsers { - display: grid; - - grid-template-areas: - "a" - "b" - "c" - "e"; - grid-template-rows: auto 1fr auto auto; - grid-template-columns: 1fr; - - grid-gap: 10px; - - width: 100%; - height: 100%; -} - -.Header { - grid-area: a; -} - -.UserList { - grid-area: b; -} - -.CreateUser { - grid-area: c; -} - -.Error { - grid-area: e; -} \ No newline at end of file diff --git a/nest_frontend/utils/countTweetWords.js b/nest_frontend/utils/countTweetWords.js index 172f501..551237c 100644 --- a/nest_frontend/utils/countTweetWords.js +++ b/nest_frontend/utils/countTweetWords.js @@ -4,7 +4,7 @@ import sw from "stopword" const stopwords = [...sw.it, ...sw.en, "rt"] -export default function countTweetWords(tweets = {}) { +export default function countTweetWords(tweets = []) { let words = {} for(const tweet of tweets) { if(!tweet.content) { diff --git a/nest_frontend/utils/renderContents.js b/nest_frontend/utils/renderContents.js index f1ea761..9f8eee5 100644 --- a/nest_frontend/utils/renderContents.js +++ b/nest_frontend/utils/renderContents.js @@ -4,6 +4,9 @@ import Alert from "../components/base/Alert" import Starting from "../components/base/Starting" +/** + * @deprecated + */ export default function renderContents(requestHookResults, renderFunction) { const { data, error, loading } = requestHookResults