mirror of
https://github.com/pds-nest/nest.git
synced 2024-11-22 04:54:18 +00:00
🔧 Refactor more page stuff
This commit is contained in:
parent
db167a7d8a
commit
6c0248cda6
29 changed files with 485 additions and 341 deletions
|
@ -141,7 +141,8 @@ export default {
|
||||||
errorViewNotAllowed: "Errore: Non è permesso effettuare la richiesta.",
|
errorViewNotAllowed: "Errore: Non è permesso effettuare la richiesta.",
|
||||||
errorServerNotConfigured: "Errore: Non è stato configurato nessun server.",
|
errorServerNotConfigured: "Errore: Non è stato configurato nessun server.",
|
||||||
errorDecodeError: "Errore: Non è stato possibile deserializzare i dati ricevuti dal backend.",
|
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: {
|
en: {
|
||||||
|
|
|
@ -9,9 +9,15 @@ import PageRepositoryEdit from "./routes/PageRepositoryEdit"
|
||||||
import PageUsers from "./routes/PageUsers"
|
import PageUsers from "./routes/PageUsers"
|
||||||
import PageRepositoryAnalyze from "./routes/PageRepositoryAnalyze"
|
import PageRepositoryAnalyze from "./routes/PageRepositoryAnalyze"
|
||||||
import PageShare from "./routes/PageShare"
|
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"
|
||||||
|
|
||||||
|
|
||||||
export default function PageSwitcher({ ...props }) {
|
export default function PageSwitcher({ ...props }) {
|
||||||
|
const strings = useStrings()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Switch {...props}>
|
<Switch {...props}>
|
||||||
<Route path={"/repositories/create"} exact={true}>
|
<Route path={"/repositories/create"} exact={true}>
|
||||||
|
@ -38,9 +44,12 @@ export default function PageSwitcher({ ...props }) {
|
||||||
<Route path={"/settings"} exact={true}>
|
<Route path={"/settings"} exact={true}>
|
||||||
<PageSettings/>
|
<PageSettings/>
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={"/"}>
|
<Route path={"/"} exact={true}>
|
||||||
<PageLogin/>
|
<PageLogin/>
|
||||||
</Route>
|
</Route>
|
||||||
|
<Route>
|
||||||
|
<Alert color={"Red"}>{makeIcon(faQuestionCircle)} {strings.errorPageNotFound}</Alert>
|
||||||
|
</Route>
|
||||||
</Switch>
|
</Switch>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
19
nest_frontend/components/base/ButtonHeader.js
Normal file
19
nest_frontend/components/base/ButtonHeader.js
Normal file
|
@ -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 (
|
||||||
|
<Button className={classNames(Style.ButtonHeader, className)} {...props}/>
|
||||||
|
)
|
||||||
|
}
|
4
nest_frontend/components/base/ButtonHeader.module.css
Normal file
4
nest_frontend/components/base/ButtonHeader.module.css
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
.ButtonHeader {
|
||||||
|
box-shadow: none;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
12
nest_frontend/components/base/layout/BodyFlex.js
Normal file
12
nest_frontend/components/base/layout/BodyFlex.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import React from "react"
|
||||||
|
import Style from "./BodyFlex.module.css"
|
||||||
|
import classNames from "classnames"
|
||||||
|
|
||||||
|
|
||||||
|
export default function BodyFlex({ children, className, ...props }) {
|
||||||
|
return (
|
||||||
|
<div className={classNames(Style.BodyFlex, className)} {...props}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,9 +1,6 @@
|
||||||
.PageLogin {
|
.BodyFlex {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
}
|
17
nest_frontend/components/base/layout/BodyHorizontalHalves.js
Normal file
17
nest_frontend/components/base/layout/BodyHorizontalHalves.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import React from "react"
|
||||||
|
import Style from "./BodyHorizontalHalves.module.css"
|
||||||
|
import classNames from "classnames"
|
||||||
|
|
||||||
|
|
||||||
|
export default function BodyHorizontalHalves({ upper, lower, className, ...props }) {
|
||||||
|
return (
|
||||||
|
<div className={classNames(Style.BodyHorizontalHalves, className)} {...props}>
|
||||||
|
<div className={Style.Upper}>
|
||||||
|
{upper}
|
||||||
|
</div>
|
||||||
|
<div className={Style.Lower}>
|
||||||
|
{lower}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
.BodyHorizontalHalves {
|
||||||
|
display: grid;
|
||||||
|
grid-template-areas: "upper" "lower";
|
||||||
|
grid-template-rows: 1fr 1fr;
|
||||||
|
grid-gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Upper {
|
||||||
|
grid-area: upper;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Upper > * {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Lower {
|
||||||
|
grid-area: lower;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Lower > * {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
import React from "react"
|
||||||
|
import Style from "./BodyHorizontalUpperGrow.module.css"
|
||||||
|
import classNames from "classnames"
|
||||||
|
|
||||||
|
|
||||||
|
export default function BodyHorizontalUpperGrow({ upper, lower, error, className, ...props }) {
|
||||||
|
return (
|
||||||
|
<div className={classNames(Style.BodyHorizontalUpperGrow, className)} {...props}>
|
||||||
|
<div className={Style.Upper}>
|
||||||
|
{upper}
|
||||||
|
</div>
|
||||||
|
<div className={Style.Lower}>
|
||||||
|
{lower}
|
||||||
|
</div>
|
||||||
|
<div className={Style.Error}>
|
||||||
|
{error}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
.BodyHorizontalUpperGrow {
|
||||||
|
display: grid;
|
||||||
|
grid-template-areas: "upper" "lower" "error";
|
||||||
|
grid-template-rows: 1fr auto auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Upper {
|
||||||
|
grid-area: upper;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Upper > * {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Lower {
|
||||||
|
grid-area: lower;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Lower > * {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Error {
|
||||||
|
grid-area: error;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
22
nest_frontend/components/base/layout/PageWithHeader.js
Normal file
22
nest_frontend/components/base/layout/PageWithHeader.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import React from "react"
|
||||||
|
import Style from "./PageWithHeader.module.css"
|
||||||
|
import classNames from "classnames"
|
||||||
|
|
||||||
|
|
||||||
|
export default function PageWithHeader({ header, buttons, children, className, ...props }) {
|
||||||
|
return (
|
||||||
|
<div className={classNames(Style.PageWithHeader, className)} {...props}>
|
||||||
|
<div className={Style.Header}>
|
||||||
|
{header}
|
||||||
|
</div>
|
||||||
|
{buttons ?
|
||||||
|
<div className={Style.Buttons}>
|
||||||
|
{buttons}
|
||||||
|
</div>
|
||||||
|
: null}
|
||||||
|
<div className={Style.Body}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
.PageWithHeader {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
grid-template-areas:
|
||||||
|
"header button"
|
||||||
|
"body body";
|
||||||
|
grid-template-columns: 1fr auto;
|
||||||
|
grid-template-rows: auto 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Header {
|
||||||
|
grid-area: header;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Buttons {
|
||||||
|
grid-area: button;
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Buttons > * {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Body {
|
||||||
|
grid-area: body;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Body > * {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
import React, { useContext, useMemo, useState } from "react"
|
import React, { useContext, useMemo, useState } from "react"
|
||||||
import Style from "./RepositoryViewer.module.css"
|
import Style from "./RepositoryViewer.module.css"
|
||||||
import classNames from "classnames"
|
|
||||||
import ContextLanguage from "../../contexts/ContextLanguage"
|
import ContextLanguage from "../../contexts/ContextLanguage"
|
||||||
import useBackendResource from "../../hooks/useBackendResource"
|
import useBackendResource from "../../hooks/useBackendResource"
|
||||||
import useBackendViewset from "../../hooks/useBackendViewset"
|
import useBackendViewset from "../../hooks/useBackendViewset"
|
||||||
|
@ -9,7 +8,7 @@ import countTweetWords from "../../utils/countTweetWords"
|
||||||
import BoxHeader from "../base/BoxHeader"
|
import BoxHeader from "../base/BoxHeader"
|
||||||
import Loading from "../base/Loading"
|
import Loading from "../base/Loading"
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
|
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 BoxRepositoryTweets from "../interactive/BoxRepositoryTweets"
|
||||||
import PickerVisualization from "../interactive/PickerVisualization"
|
import PickerVisualization from "../interactive/PickerVisualization"
|
||||||
import BoxVisualizationWordcloud from "../interactive/BoxVisualizationWordcloud"
|
import BoxVisualizationWordcloud from "../interactive/BoxVisualizationWordcloud"
|
||||||
|
@ -29,6 +28,9 @@ import BoxFilterDatetime from "../interactive/BoxFilterDatetime"
|
||||||
import BoxFilterHasPlace from "../interactive/BoxFilterHasPlace"
|
import BoxFilterHasPlace from "../interactive/BoxFilterHasPlace"
|
||||||
import BoxFilterHasImage from "../interactive/BoxFilterHasImage"
|
import BoxFilterHasImage from "../interactive/BoxFilterHasImage"
|
||||||
import BoxFilterIsNotRetweet from "../interactive/BoxFilterIsNotRetweet"
|
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 }) {
|
export default function RepositoryViewer({ id, className, ...props }) {
|
||||||
|
@ -49,7 +51,11 @@ export default function RepositoryViewer({ id, className, ...props }) {
|
||||||
const mapViewHook = useMapAreaState()
|
const mapViewHook = useMapAreaState()
|
||||||
|
|
||||||
// Repository
|
// Repository
|
||||||
const repositoryBr = useBackendResource(
|
const {
|
||||||
|
resource: repository,
|
||||||
|
error: repositoryError,
|
||||||
|
firstLoad: repositoryFirstLoad,
|
||||||
|
} = useBackendResource(
|
||||||
`/api/v1/repositories/${id}`,
|
`/api/v1/repositories/${id}`,
|
||||||
{
|
{
|
||||||
retrieve: true,
|
retrieve: true,
|
||||||
|
@ -58,11 +64,13 @@ export default function RepositoryViewer({ id, className, ...props }) {
|
||||||
action: false,
|
action: false,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
const repository = repositoryBr.error ? null : repositoryBr.resource
|
|
||||||
|
|
||||||
|
|
||||||
// Tweets
|
// Tweets
|
||||||
const rawTweetsBv = useBackendViewset(
|
const {
|
||||||
|
resources: tweets,
|
||||||
|
error: tweetsError,
|
||||||
|
firstLoad: tweetsFirstLoad,
|
||||||
|
} = useBackendViewset(
|
||||||
`/api/v1/repositories/${id}/tweets/`,
|
`/api/v1/repositories/${id}/tweets/`,
|
||||||
"snowflake",
|
"snowflake",
|
||||||
{
|
{
|
||||||
|
@ -75,63 +83,73 @@ export default function RepositoryViewer({ id, className, ...props }) {
|
||||||
action: false,
|
action: false,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
const rawTweets = rawTweetsBv.resources && rawTweetsBv.error ? [] : rawTweetsBv.resources
|
|
||||||
|
|
||||||
|
|
||||||
// Filtering
|
// Filtering
|
||||||
let tweets = rawTweets
|
const filteredTweets = useMemo(
|
||||||
for(const filter of filters) {
|
() => {
|
||||||
tweets = tweets.filter(tweet => filter.exec(tweet))
|
let tempTweets = tweets
|
||||||
}
|
for(const filter of filters) {
|
||||||
|
tempTweets = tempTweets.filter(tweet => filter.exec(tweet))
|
||||||
|
}
|
||||||
|
return tempTweets
|
||||||
|
},
|
||||||
|
[tweets, filters]
|
||||||
|
)
|
||||||
|
|
||||||
// Words
|
// Words
|
||||||
const words = useMemo(
|
const words = useMemo(
|
||||||
() => objectToWordcloudFormat(countTweetWords(tweets)),
|
() => objectToWordcloudFormat(countTweetWords(filteredTweets)),
|
||||||
[tweets],
|
[filteredTweets],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Page header
|
||||||
let contents
|
const header = useMemo(
|
||||||
if(!repositoryBr.firstLoad || !rawTweetsBv.firstLoad) {
|
() => (
|
||||||
contents = <>
|
<BoxHeader>
|
||||||
<BoxHeader className={Style.Header}>
|
<FontAwesomeIcon icon={repository?.is_active ? faFolderOpen : faFolder}/> {repository?.name}
|
||||||
<Loading/>
|
|
||||||
</BoxHeader>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
else if(repository === null) {
|
|
||||||
contents = <>
|
|
||||||
<BoxHeader className={Style.Header}>
|
|
||||||
<FontAwesomeIcon icon={faTrash}/> <i>{strings.repoDeleted}</i>
|
|
||||||
</BoxHeader>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
contents = <>
|
|
||||||
<BoxHeader className={Style.Header}>
|
|
||||||
<FontAwesomeIcon icon={repository.is_active ? faFolderOpen : faFolder}/> {repository.name}
|
|
||||||
</BoxHeader>
|
</BoxHeader>
|
||||||
|
),
|
||||||
|
[repository?.is_active, repository?.name]
|
||||||
|
)
|
||||||
|
|
||||||
<BoxRepositoryTweets className={Style.Tweets}/>
|
// Page contents
|
||||||
<PickerVisualization className={Style.VisualizationPicker}/>
|
const contents = useMemo(
|
||||||
{visualizationTab === "wordcloud" ? <BoxVisualizationWordcloud className={Style.Visualization}/> : null}
|
() => {
|
||||||
{visualizationTab === "chart" ? <BoxVisualizationChart className={Style.Visualization}/> : null}
|
if(!repositoryFirstLoad || !tweetsFirstLoad) {
|
||||||
{visualizationTab === "map" ? <BoxVisualizationMap className={Style.Visualization}/> : null}
|
return <Loading/>
|
||||||
{visualizationTab === "stats" ? <BoxVisualizationStats className={Style.Visualization}/> : null}
|
}
|
||||||
|
else if(repositoryError) {
|
||||||
|
return <AlertError error={repositoryError}/>
|
||||||
|
}
|
||||||
|
else if(tweetsError) {
|
||||||
|
return <AlertError error={tweetsError}/>
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return (
|
||||||
|
<div className={Style.RepositoryViewer}>
|
||||||
|
<BoxRepositoryTweets className={Style.Tweets}/>
|
||||||
|
<PickerVisualization className={Style.VisualizationPicker}/>
|
||||||
|
{visualizationTab === "wordcloud" ? <BoxVisualizationWordcloud className={Style.Visualization}/> : null}
|
||||||
|
{visualizationTab === "chart" ? <BoxVisualizationChart className={Style.Visualization}/> : null}
|
||||||
|
{visualizationTab === "map" ? <BoxVisualizationMap className={Style.Visualization}/> : null}
|
||||||
|
{visualizationTab === "stats" ? <BoxVisualizationStats className={Style.Visualization}/> : null}
|
||||||
|
|
||||||
<BoxFilters className={Style.Filters}/>
|
<BoxFilters className={Style.Filters}/>
|
||||||
<PickerFilter className={Style.FilterPicker}/>
|
<PickerFilter className={Style.FilterPicker}/>
|
||||||
{filterTab === "contains" ? <BoxFilterContains className={Style.AddFilter}/> : null}
|
{filterTab === "contains" ? <BoxFilterContains className={Style.AddFilter}/> : null}
|
||||||
{filterTab === "hashtag" ? <BoxFilterHashtag className={Style.AddFilter}/> : null}
|
{filterTab === "hashtag" ? <BoxFilterHashtag className={Style.AddFilter}/> : null}
|
||||||
{filterTab === "user" ? <BoxFilterUser className={Style.AddFilter}/> : null}
|
{filterTab === "user" ? <BoxFilterUser className={Style.AddFilter}/> : null}
|
||||||
{filterTab === "retweet" ? <BoxFilterIsNotRetweet className={Style.AddFilter}/> : null}
|
{filterTab === "retweet" ? <BoxFilterIsNotRetweet className={Style.AddFilter}/> : null}
|
||||||
{filterTab === "image" ? <BoxFilterHasImage className={Style.AddFilter}/> : null}
|
{filterTab === "image" ? <BoxFilterHasImage className={Style.AddFilter}/> : null}
|
||||||
{filterTab === "time" ? <BoxFilterDatetime className={Style.AddFilter}/> : null}
|
{filterTab === "time" ? <BoxFilterDatetime className={Style.AddFilter}/> : null}
|
||||||
{filterTab === "place" ? <BoxFilterHasPlace className={Style.AddFilter}/> : null}
|
{filterTab === "place" ? <BoxFilterHasPlace className={Style.AddFilter}/> : null}
|
||||||
{filterTab === "location" ? <BoxFilterLocation className={Style.AddFilter}/> : null}
|
{filterTab === "location" ? <BoxFilterLocation className={Style.AddFilter}/> : null}
|
||||||
</>
|
</div>
|
||||||
}
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[repositoryFirstLoad, tweetsFirstLoad, visualizationTab, filterTab]
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ContextRepositoryViewer.Provider
|
<ContextRepositoryViewer.Provider
|
||||||
|
@ -145,20 +163,16 @@ export default function RepositoryViewer({ id, className, ...props }) {
|
||||||
appendFilter,
|
appendFilter,
|
||||||
spliceFilter,
|
spliceFilter,
|
||||||
removeFilter,
|
removeFilter,
|
||||||
repositoryBr,
|
|
||||||
repository,
|
repository,
|
||||||
rawTweetsBv,
|
rawTweets: tweets,
|
||||||
rawTweets,
|
tweets: filteredTweets,
|
||||||
tweets,
|
|
||||||
words,
|
words,
|
||||||
mapViewHook,
|
mapViewHook,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<PageWithHeader header={header}>
|
||||||
<div className={classNames(Style.RepositoryViewer, className)} {...props}>
|
|
||||||
{contents}
|
{contents}
|
||||||
</div>
|
</PageWithHeader>
|
||||||
|
|
||||||
</ContextRepositoryViewer.Provider>
|
</ContextRepositoryViewer.Provider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
"h h"
|
|
||||||
"a b"
|
"a b"
|
||||||
"a c"
|
"a c"
|
||||||
"d e"
|
"d e"
|
||||||
|
@ -10,16 +9,12 @@
|
||||||
|
|
||||||
grid-gap: 10px;
|
grid-gap: 10px;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
grid-template-rows: auto auto 1fr auto auto;
|
grid-template-rows: auto 1fr auto auto;
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Header {
|
|
||||||
grid-area: h;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Tweets {
|
.Tweets {
|
||||||
grid-area: a;
|
grid-area: a;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
import React, { useContext } from "react"
|
import React, { useContext } from "react"
|
||||||
import Style from "./PageLogin.module.css"
|
|
||||||
import classNames from "classnames"
|
|
||||||
import BoxSetServer from "../components/interactive/BoxSetServer"
|
import BoxSetServer from "../components/interactive/BoxSetServer"
|
||||||
import BoxLogin from "../components/interactive/BoxLogin"
|
import BoxLogin from "../components/interactive/BoxLogin"
|
||||||
import ContextUser from "../contexts/ContextUser"
|
import ContextUser from "../contexts/ContextUser"
|
||||||
|
@ -9,9 +7,11 @@ import BoxHeader from "../components/base/BoxHeader"
|
||||||
import useStrings from "../hooks/useStrings"
|
import useStrings from "../hooks/useStrings"
|
||||||
import BoxFull from "../components/base/BoxFull"
|
import BoxFull from "../components/base/BoxFull"
|
||||||
import SelectLanguage from "../components/interactive/SelectLanguage"
|
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({ ...props }) {
|
||||||
const {user} = useContext(ContextUser)
|
const {user} = useContext(ContextUser)
|
||||||
const strings = useStrings()
|
const strings = useStrings()
|
||||||
|
|
||||||
|
@ -20,15 +20,21 @@ export default function PageLogin({ className, ...props }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(Style.PageLogin, className)} {...props}>
|
<PageWithHeader
|
||||||
<BoxHeader>
|
header={
|
||||||
{strings.welcomeToNest}
|
<BoxHeader>
|
||||||
</BoxHeader>
|
{strings.welcomeToNest}
|
||||||
<BoxSetServer/>
|
</BoxHeader>
|
||||||
<BoxLogin/>
|
}
|
||||||
<BoxFull header={strings.changeLang}>
|
{...props}
|
||||||
<SelectLanguage/>
|
>
|
||||||
</BoxFull>
|
<BodyFlex>
|
||||||
</div>
|
<BoxSetServer/>
|
||||||
|
<BoxLogin/>
|
||||||
|
<BoxFull header={strings.changeLang}>
|
||||||
|
<SelectLanguage/>
|
||||||
|
</BoxFull>
|
||||||
|
</BodyFlex>
|
||||||
|
</PageWithHeader>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
import React, { useCallback, useContext } from "react"
|
import React, { useCallback, useContext } from "react"
|
||||||
import Style from "./PageRepositoriesList.module.css"
|
|
||||||
import classNames from "classnames"
|
|
||||||
import useBackendViewset from "../hooks/useBackendViewset"
|
import useBackendViewset from "../hooks/useBackendViewset"
|
||||||
import BoxRepositories from "../components/interactive/BoxRepositories"
|
import BoxRepositories from "../components/interactive/BoxRepositories"
|
||||||
import { useHistory } from "react-router"
|
import { useHistory } from "react-router"
|
||||||
|
@ -8,7 +6,9 @@ import ContextLanguage from "../contexts/ContextLanguage"
|
||||||
import BoxHeader from "../components/base/BoxHeader"
|
import BoxHeader from "../components/base/BoxHeader"
|
||||||
import { faHome, faPlus } from "@fortawesome/free-solid-svg-icons"
|
import { faHome, faPlus } from "@fortawesome/free-solid-svg-icons"
|
||||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"
|
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({ children, className, ...props }) {
|
||||||
|
@ -27,40 +27,47 @@ export default function PageRepositoriesList({ children, className, ...props })
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(Style.PageRepositories, className)} {...props}>
|
<PageWithHeader
|
||||||
<BoxHeader className={Style.Header}>
|
header={
|
||||||
<FontAwesomeIcon icon={faHome}/> {strings.dashboard}
|
<BoxHeader>
|
||||||
</BoxHeader>
|
<FontAwesomeIcon icon={faHome}/> {strings.dashboard}
|
||||||
<div className={Style.Buttons}>
|
</BoxHeader>
|
||||||
<Button
|
}
|
||||||
|
buttons={
|
||||||
|
<ButtonHeader
|
||||||
icon={faPlus}
|
icon={faPlus}
|
||||||
color={"Green"}
|
color={"Green"}
|
||||||
onClick={() => history.push("/repositories/create")}
|
onClick={() => history.push("/repositories/create")}
|
||||||
>
|
>
|
||||||
{strings.createRepo}
|
{strings.createRepo}
|
||||||
</Button>
|
</ButtonHeader>
|
||||||
</div>
|
}
|
||||||
<BoxRepositories
|
>
|
||||||
className={Style.ActiveRepositories}
|
<BodyHorizontalHalves
|
||||||
header={strings.menuActive}
|
upper={
|
||||||
loading={!bv.firstLoad}
|
<BoxRepositories
|
||||||
running={bv.running}
|
header={strings.menuActive}
|
||||||
repositories={bv.resources.filter(r => r.is_active)}
|
loading={!bv.firstLoad}
|
||||||
view={pk => history.push(`/repositories/${pk}`)}
|
running={bv.running}
|
||||||
share={pk => history.push(`/repositories/${pk}/share`)}
|
repositories={bv.resources.filter(r => r.is_active)}
|
||||||
alerts={pk => history.push(`/repositories/${pk}/alerts`)}
|
view={pk => history.push(`/repositories/${pk}`)}
|
||||||
archive={archive}
|
share={pk => history.push(`/repositories/${pk}/share`)}
|
||||||
edit={pk => history.push(`/repositories/${pk}/edit`)}
|
alerts={pk => history.push(`/repositories/${pk}/alerts`)}
|
||||||
|
archive={archive}
|
||||||
|
edit={pk => history.push(`/repositories/${pk}/edit`)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
lower={
|
||||||
|
<BoxRepositories
|
||||||
|
header={strings.menuArchived}
|
||||||
|
loading={!bv.firstLoad}
|
||||||
|
running={bv.running}
|
||||||
|
repositories={bv.resources.filter(r => !r.is_active)}
|
||||||
|
view={pk => history.push(`/repositories/${pk}`)}
|
||||||
|
destroy={bv.destroyResource}
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<BoxRepositories
|
</PageWithHeader>
|
||||||
className={Style.ArchivedRepositories}
|
|
||||||
header={strings.menuArchived}
|
|
||||||
loading={!bv.firstLoad}
|
|
||||||
running={bv.running}
|
|
||||||
repositories={bv.resources.filter(r => !r.is_active)}
|
|
||||||
view={pk => history.push(`/repositories/${pk}`)}
|
|
||||||
destroy={bv.destroyResource}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -1,27 +1,40 @@
|
||||||
import React, { useContext } from "react"
|
import React, { useContext } from "react"
|
||||||
import Style from "./PageRepositoryAlerts.module.css"
|
import Style from "./PageRepositoryAlerts.module.css"
|
||||||
import classNames from "classnames"
|
|
||||||
import BoxFull from "../components/base/BoxFull"
|
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 { 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({ children, className, ...props }) {
|
||||||
const { strings } = useContext(ContextLanguage)
|
const { strings } = useContext(ContextLanguage)
|
||||||
const { id } = useParams()
|
const { id } = useParams()
|
||||||
|
const history = useHistory()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(Style.PageAlerts, className)} {...props}>
|
<PageWithHeader
|
||||||
<BoxHeader className={Style.Header}>
|
header={
|
||||||
{strings.alerts}
|
<BoxHeader className={Style.Header}>
|
||||||
</BoxHeader>
|
{makeIcon(faBell)} {strings.alerts}
|
||||||
|
</BoxHeader>
|
||||||
|
}
|
||||||
|
buttons={
|
||||||
|
<ButtonHeader
|
||||||
|
icon={faPlus}
|
||||||
|
color={"Green"}
|
||||||
|
onClick={() => history.push(`/repositories/${id}/alerts/create`)}
|
||||||
|
>
|
||||||
|
{strings.alertCreate}
|
||||||
|
</ButtonHeader>
|
||||||
|
}
|
||||||
|
>
|
||||||
<BoxFull header={strings.alertTitle} className={Style.YourAlerts}>
|
<BoxFull header={strings.alertTitle} className={Style.YourAlerts}>
|
||||||
{strings.notImplemented}
|
{strings.notImplemented}
|
||||||
</BoxFull>
|
</BoxFull>
|
||||||
<BoxFull header={strings.alertCreate} className={Style.CreateAlert}>
|
</PageWithHeader>
|
||||||
{strings.notImplemented}
|
|
||||||
</BoxFull>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
"a"
|
"a x"
|
||||||
"b"
|
"b b";
|
||||||
"c";
|
grid-template-columns: 4fr 1fr;
|
||||||
grid-template-rows: auto 1fr 1fr;
|
grid-template-rows: auto 1fr;
|
||||||
|
|
||||||
grid-gap: 10px;
|
grid-gap: 10px;
|
||||||
|
|
||||||
|
@ -17,10 +17,19 @@
|
||||||
grid-area: a;
|
grid-area: a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.Buttons {
|
||||||
|
grid-area: x;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Buttons > * {
|
||||||
|
box-shadow: none;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.YourAlerts {
|
.YourAlerts {
|
||||||
grid-area: b;
|
grid-area: b;
|
||||||
}
|
}
|
||||||
|
|
||||||
.CreateAlert {
|
|
||||||
grid-area: c;
|
|
||||||
}
|
|
|
@ -1,20 +1,25 @@
|
||||||
import React, { useContext } from "react"
|
import React, { useContext } from "react"
|
||||||
import Style from "./PageRepositoryCreate.module.css"
|
|
||||||
import classNames from "classnames"
|
|
||||||
import BoxHeader from "../components/base/BoxHeader"
|
import BoxHeader from "../components/base/BoxHeader"
|
||||||
import RepositoryEditor from "../components/providers/RepositoryEditor"
|
import RepositoryEditor from "../components/providers/RepositoryEditor"
|
||||||
import ContextLanguage from "../contexts/ContextLanguage"
|
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({ ...props }) {
|
||||||
const { strings } = useContext(ContextLanguage)
|
const { strings } = useContext(ContextLanguage)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(Style.PageHome, className)} {...props}>
|
<PageWithHeader
|
||||||
<BoxHeader className={Style.Header}>
|
header={
|
||||||
{strings.dashboardTitle}
|
<BoxHeader>
|
||||||
</BoxHeader>
|
{makeIcon(faPlus)} {strings.dashboardTitle}
|
||||||
<RepositoryEditor className={Style.RepositoryEditor}/>
|
</BoxHeader>
|
||||||
</div>
|
}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<RepositoryEditor/>
|
||||||
|
</PageWithHeader>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -1,6 +1,4 @@
|
||||||
import React, { useContext } from "react"
|
import React, { useContext } from "react"
|
||||||
import Style from "./PageRepositoryCreate.module.css"
|
|
||||||
import classNames from "classnames"
|
|
||||||
import BoxHeader from "../components/base/BoxHeader"
|
import BoxHeader from "../components/base/BoxHeader"
|
||||||
import RepositoryEditor from "../components/providers/RepositoryEditor"
|
import RepositoryEditor from "../components/providers/RepositoryEditor"
|
||||||
import useBackendImmediately from "../hooks/useBackendImmediately"
|
import useBackendImmediately from "../hooks/useBackendImmediately"
|
||||||
|
@ -8,6 +6,9 @@ import ContextUser from "../contexts/ContextUser"
|
||||||
import renderContents from "../utils/renderContents"
|
import renderContents from "../utils/renderContents"
|
||||||
import { useParams } from "react-router"
|
import { useParams } from "react-router"
|
||||||
import ContextLanguage from "../contexts/ContextLanguage"
|
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({ className, ...props }) {
|
||||||
|
@ -20,16 +21,20 @@ export default function PageRepositoryEdit({ className, ...props }) {
|
||||||
repositoryRequest,
|
repositoryRequest,
|
||||||
data => {
|
data => {
|
||||||
console.debug("Data: ", data)
|
console.debug("Data: ", data)
|
||||||
return <RepositoryEditor className={Style.RepositoryEditor} {...data}/>
|
return <RepositoryEditor {...data}/>
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(Style.PageHome, className)} {...props}>
|
<PageWithHeader
|
||||||
<BoxHeader className={Style.Header}>
|
header={
|
||||||
{strings.repoEdit}
|
<BoxHeader>
|
||||||
</BoxHeader>
|
{makeIcon(faPencilAlt)} {strings.repoEdit}
|
||||||
|
</BoxHeader>
|
||||||
|
}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
{contents}
|
{contents}
|
||||||
</div>
|
</PageWithHeader>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,36 @@
|
||||||
import React, { useContext } from "react"
|
import React, { useContext } from "react"
|
||||||
import Style from "./PageSettings.module.css"
|
|
||||||
import classNames from "classnames"
|
|
||||||
import BoxFull from "../components/base/BoxFull"
|
import BoxFull from "../components/base/BoxFull"
|
||||||
import SelectTheme from "../components/interactive/SelectTheme"
|
import SelectTheme from "../components/interactive/SelectTheme"
|
||||||
import BoxLoggedIn from "../components/interactive/BoxLoggedIn"
|
import BoxLoggedIn from "../components/interactive/BoxLoggedIn"
|
||||||
import SelectLanguage from "../components/interactive/SelectLanguage"
|
import SelectLanguage from "../components/interactive/SelectLanguage"
|
||||||
import ContextLanguage from "../contexts/ContextLanguage"
|
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({ children, className, ...props }) {
|
||||||
const { strings } = useContext(ContextLanguage)
|
const { strings } = useContext(ContextLanguage)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(Style.PageSettings, className)} {...props}>
|
<PageWithHeader
|
||||||
<BoxLoggedIn/>
|
header={
|
||||||
<BoxFull header={strings.changeLang}>
|
<BoxHeader>
|
||||||
<SelectLanguage/>
|
{makeIcon(faCog)} {strings.settings}
|
||||||
</BoxFull>
|
</BoxHeader>
|
||||||
<BoxFull header={strings.switchTheme}>
|
}
|
||||||
<SelectTheme/>
|
>
|
||||||
</BoxFull>
|
<BodyFlex>
|
||||||
</div>
|
<BoxLoggedIn/>
|
||||||
|
<BoxFull header={strings.changeLang}>
|
||||||
|
<SelectLanguage/>
|
||||||
|
</BoxFull>
|
||||||
|
<BoxFull header={strings.switchTheme}>
|
||||||
|
<SelectTheme/>
|
||||||
|
</BoxFull>
|
||||||
|
</BodyFlex>
|
||||||
|
</PageWithHeader>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
.PageSettings {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
grid-gap: 10px;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
|
@ -1,14 +1,13 @@
|
||||||
import React, { useCallback, useContext } from "react"
|
import React, { useCallback, useContext } from "react"
|
||||||
import classNames from "classnames"
|
|
||||||
import BoxHeader from "../components/base/BoxHeader"
|
import BoxHeader from "../components/base/BoxHeader"
|
||||||
import ContextLanguage from "../contexts/ContextLanguage"
|
|
||||||
import Style from "./PageShare.module.css"
|
|
||||||
import BoxUserList from "../components/interactive/BoxUserList"
|
import BoxUserList from "../components/interactive/BoxUserList"
|
||||||
import useBackendViewset from "../hooks/useBackendViewset"
|
import useBackendViewset from "../hooks/useBackendViewset"
|
||||||
import { useParams } from "react-router"
|
import { useParams } from "react-router"
|
||||||
import ContextUser from "../contexts/ContextUser"
|
import ContextUser from "../contexts/ContextUser"
|
||||||
import Alert from "../components/base/Alert"
|
|
||||||
import useStrings from "../hooks/useStrings"
|
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({ className, ...props }) {
|
||||||
|
@ -71,30 +70,35 @@ export default function PageShare({ className, ...props }) {
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(Style.PageShare, className)} {...props}>
|
<PageWithHeader
|
||||||
<BoxHeader className={Style.Header}>
|
header={
|
||||||
{strings.repoShare}
|
<BoxHeader>
|
||||||
</BoxHeader>
|
{strings.repoShare}
|
||||||
<BoxUserList
|
</BoxHeader>
|
||||||
className={Style.UserList}
|
}
|
||||||
users={users.filter(user => user["email"] !== loggedUser["email"] && !authorizations.map(a => a.email).includes(user.email))}
|
>
|
||||||
shareWithUser={shareWith}
|
<BodyHorizontalHalves
|
||||||
header={strings.availableUsers}
|
upper={
|
||||||
running={usersBvRunning && authBvRunning}
|
<BoxUserList
|
||||||
|
users={users.filter(user => user["email"] !== loggedUser["email"] && !authorizations.map(a => a.email).includes(user.email))}
|
||||||
|
shareWithUser={shareWith}
|
||||||
|
header={strings.availableUsers}
|
||||||
|
running={usersBvRunning || authBvRunning}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
lower={<>
|
||||||
|
<BoxUserList
|
||||||
|
users={users.filter(user => user["email"] === loggedUser["email"] || authorizations.map(a => a.email).includes(user.email))}
|
||||||
|
unshareWithUser={unshareWith}
|
||||||
|
header={strings.sharingWith}
|
||||||
|
running={usersBvRunning || authBvRunning}
|
||||||
|
/>
|
||||||
|
</>}
|
||||||
|
error={<>
|
||||||
|
{authBvError ? <AlertError error={authBvError}/> : null}
|
||||||
|
{usersBvError ? <AlertError error={usersBvError}/> : null}
|
||||||
|
</>}
|
||||||
/>
|
/>
|
||||||
<BoxUserList
|
</PageWithHeader>
|
||||||
className={Style.SharingWith}
|
|
||||||
users={users.filter(user => user["email"] === loggedUser["email"] || authorizations.map(a => a.email).includes(user.email))}
|
|
||||||
unshareWithUser={unshareWith}
|
|
||||||
header={strings.sharingWith}
|
|
||||||
running={usersBvRunning && authBvRunning}
|
|
||||||
/>
|
|
||||||
{authBvError ?
|
|
||||||
<Alert color={"Red"} className={Style.Error}>{strings[authBvError?.data?.code ?? "errorUnknownError"]}</Alert>
|
|
||||||
: null}
|
|
||||||
{usersBvError ?
|
|
||||||
<Alert color={"Red"} className={Style.Error}>{strings[usersBvError?.data?.code ?? "errorUnknownError"]}</Alert>
|
|
||||||
: null}
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -1,29 +1,45 @@
|
||||||
import React, { useContext } from "react"
|
import React, { useContext } from "react"
|
||||||
import Style from "./PageUsers.module.css"
|
|
||||||
import classNames from "classnames"
|
|
||||||
import BoxHeader from "../components/base/BoxHeader"
|
import BoxHeader from "../components/base/BoxHeader"
|
||||||
import BoxUserCreate from "../components/interactive/BoxUserCreate"
|
import BoxUserCreate from "../components/interactive/BoxUserCreate"
|
||||||
import useBackendViewset from "../hooks/useBackendViewset"
|
import useBackendViewset from "../hooks/useBackendViewset"
|
||||||
import BoxUserList from "../components/interactive/BoxUserList"
|
import BoxUserList from "../components/interactive/BoxUserList"
|
||||||
import ContextLanguage from "../contexts/ContextLanguage"
|
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({ children, className, ...props }) {
|
||||||
const { strings } = useContext(ContextLanguage)
|
const { strings } = useContext(ContextLanguage)
|
||||||
|
|
||||||
const bv = useBackendViewset("/api/v1/users/", "email")
|
const {createResource, running, resources, destroyResource, error} = useBackendViewset("/api/v1/users/", "email")
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(Style.PageUsers, className)} {...props}>
|
<PageWithHeader
|
||||||
<BoxHeader className={Style.Header}>
|
header={
|
||||||
{strings.manageUsers}
|
<BoxHeader>
|
||||||
</BoxHeader>
|
{makeIcon(faUserCog)} {strings.manageUsers}
|
||||||
<BoxUserCreate className={Style.CreateUser} createUser={bv.createResource} running={bv.running}/>
|
</BoxHeader>
|
||||||
<BoxUserList className={Style.UserList} users={bv.resources} destroyUser={bv.destroyResource} running={bv.running}/>
|
}
|
||||||
{bv.error ?
|
>
|
||||||
<Alert className={Style.Error} color={"red"}>{strings[bv.error?.data?.code ?? "errorUnknownError"]}</Alert>
|
<BodyHorizontalUpperGrow
|
||||||
: null}
|
upper={
|
||||||
</div>
|
<BoxUserList
|
||||||
|
users={resources}
|
||||||
|
destroyUser={destroyResource}
|
||||||
|
running={running}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
lower={
|
||||||
|
<BoxUserCreate
|
||||||
|
createUser={createResource}
|
||||||
|
running={running}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
error={error ? <AlertError error={error}/> : null}
|
||||||
|
/>
|
||||||
|
</PageWithHeader>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -4,7 +4,7 @@ import sw from "stopword"
|
||||||
const stopwords = [...sw.it, ...sw.en, "rt"]
|
const stopwords = [...sw.it, ...sw.en, "rt"]
|
||||||
|
|
||||||
|
|
||||||
export default function countTweetWords(tweets = {}) {
|
export default function countTweetWords(tweets = []) {
|
||||||
let words = {}
|
let words = {}
|
||||||
for(const tweet of tweets) {
|
for(const tweet of tweets) {
|
||||||
if(!tweet.content) {
|
if(!tweet.content) {
|
||||||
|
|
Loading…
Reference in a new issue