mirror of
https://github.com/pds-nest/nest.git
synced 2024-11-22 04:54:18 +00:00
🧹 Reformat code
This commit is contained in:
parent
df4b9bb368
commit
6a99effc8b
22 changed files with 225 additions and 159 deletions
|
@ -28,12 +28,12 @@ export default function Badge({ icon, color, onClickDelete, children, className,
|
||||||
</div>
|
</div>
|
||||||
{
|
{
|
||||||
onClickDelete ?
|
onClickDelete ?
|
||||||
<div>
|
<div>
|
||||||
<ButtonSmallX
|
<ButtonSmallX
|
||||||
onClick={onClickDelete}
|
onClick={onClickDelete}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,7 +4,15 @@ import BoxFull from "./BoxFull"
|
||||||
import { MapContainer, TileLayer } from "react-leaflet"
|
import { MapContainer, TileLayer } from "react-leaflet"
|
||||||
|
|
||||||
|
|
||||||
export default function BoxMap({ header, setMap, startingPosition = { lat: 41.89309, lng: 12.48289 }, startingZoom = 3, button, children, ...props }) {
|
export default function BoxMap({
|
||||||
|
header,
|
||||||
|
setMap,
|
||||||
|
startingPosition = { lat: 41.89309, lng: 12.48289 },
|
||||||
|
startingZoom = 3,
|
||||||
|
button,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<BoxFull
|
<BoxFull
|
||||||
header={header}
|
header={header}
|
||||||
|
|
|
@ -3,7 +3,15 @@ import Style from "./SummaryLabels.module.css"
|
||||||
import classNames from "classnames"
|
import classNames from "classnames"
|
||||||
|
|
||||||
|
|
||||||
export default function SummaryLabels({ children, upperLabel, upperValue, lowerLabel, lowerValue, className, ...props }) {
|
export default function SummaryLabels({
|
||||||
|
children,
|
||||||
|
upperLabel,
|
||||||
|
upperValue,
|
||||||
|
lowerLabel,
|
||||||
|
lowerValue,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className={classNames(Style.SummaryLabels, className)} {...props}>
|
<div className={classNames(Style.SummaryLabels, className)} {...props}>
|
||||||
<div className={classNames(Style.Label, Style.Upper)}>
|
<div className={classNames(Style.Label, Style.Upper)}>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React, { useContext } from "react"
|
import React from "react"
|
||||||
import BoxFullScrollable from "../base/BoxFullScrollable"
|
import BoxFullScrollable from "../base/BoxFullScrollable"
|
||||||
import Loading from "../base/Loading"
|
import Loading from "../base/Loading"
|
||||||
import ContextLanguage from "../../contexts/ContextLanguage"
|
|
||||||
import SummaryRepository from "./SummaryRepository"
|
import SummaryRepository from "./SummaryRepository"
|
||||||
import Empty from "./Empty"
|
import Empty from "./Empty"
|
||||||
|
|
||||||
|
@ -21,7 +20,17 @@ import Empty from "./Empty"
|
||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
export default function BoxRepositories({ repositories, view, archive, edit, destroy, loading, running, className, ...props }) {
|
export default function BoxRepositories({
|
||||||
|
repositories,
|
||||||
|
view,
|
||||||
|
archive,
|
||||||
|
edit,
|
||||||
|
destroy,
|
||||||
|
loading,
|
||||||
|
running,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}) {
|
||||||
let contents
|
let contents
|
||||||
if(loading) {
|
if(loading) {
|
||||||
contents = <Loading/>
|
contents = <Loading/>
|
||||||
|
|
|
@ -8,7 +8,7 @@ const locationRegex = /[{](?<lat>[0-9.]+),(?<lng>[0-9.]+)[}]/
|
||||||
|
|
||||||
export default function BoxVisualizationMap({ tweets, ...props }) {
|
export default function BoxVisualizationMap({ tweets, ...props }) {
|
||||||
// TODO: translate this
|
// TODO: translate this
|
||||||
const {strings} = useContext(ContextLanguage)
|
const { strings } = useContext(ContextLanguage)
|
||||||
|
|
||||||
console.debug(tweets)
|
console.debug(tweets)
|
||||||
const markers = tweets.filter(tweet => tweet.location).map(tweet => {
|
const markers = tweets.filter(tweet => tweet.location).map(tweet => {
|
||||||
|
@ -17,7 +17,7 @@ export default function BoxVisualizationMap({ tweets, ...props }) {
|
||||||
console.error("No match for location ", tweet.location)
|
console.error("No match for location ", tweet.location)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
const {lat, lng} = match.groups
|
const { lat, lng } = match.groups
|
||||||
return (
|
return (
|
||||||
<Marker key={tweet["snowflake"]} position={[Number.parseFloat(lat), Number.parseFloat(lng)]}>
|
<Marker key={tweet["snowflake"]} position={[Number.parseFloat(lat), Number.parseFloat(lng)]}>
|
||||||
<Popup>
|
<Popup>
|
||||||
|
|
|
@ -2,34 +2,33 @@ import React, { useMemo } from "react"
|
||||||
import FormLabelled from "../base/FormLabelled"
|
import FormLabelled from "../base/FormLabelled"
|
||||||
import FormLabel from "../base/formparts/FormLabel"
|
import FormLabel from "../base/formparts/FormLabel"
|
||||||
import BoxFullScrollable from "../base/BoxFullScrollable"
|
import BoxFullScrollable from "../base/BoxFullScrollable"
|
||||||
import tokenizeTweetWords from "../../utils/countTweetWords"
|
|
||||||
|
|
||||||
|
|
||||||
export default function BoxVisualizationStats({ tweets, words, totalTweetCount, ...props }) {
|
export default function BoxVisualizationStats({ tweets, words, totalTweetCount, ...props }) {
|
||||||
|
|
||||||
const tweetCount = useMemo(
|
const tweetCount = useMemo(
|
||||||
() => tweets.length,
|
() => tweets.length,
|
||||||
[tweets]
|
[tweets],
|
||||||
)
|
)
|
||||||
|
|
||||||
const tweetPct = useMemo(
|
const tweetPct = useMemo(
|
||||||
() => tweetCount / totalTweetCount * 100,
|
() => tweetCount / totalTweetCount * 100,
|
||||||
[tweetCount, totalTweetCount]
|
[tweetCount, totalTweetCount],
|
||||||
)
|
)
|
||||||
|
|
||||||
const tweetLocationCount = useMemo(
|
const tweetLocationCount = useMemo(
|
||||||
() => tweets.filter(tweet => tweet.location).length,
|
() => tweets.filter(tweet => tweet.location).length,
|
||||||
[tweets]
|
[tweets],
|
||||||
)
|
)
|
||||||
|
|
||||||
const tweetLocationPct = useMemo(
|
const tweetLocationPct = useMemo(
|
||||||
() => tweetLocationCount / tweetCount * 100,
|
() => tweetLocationCount / tweetCount * 100,
|
||||||
[tweetLocationCount, tweetCount]
|
[tweetLocationCount, tweetCount],
|
||||||
)
|
)
|
||||||
|
|
||||||
const tweetContent = useMemo(
|
const tweetContent = useMemo(
|
||||||
() => tweets.filter(tweet => tweet.content),
|
() => tweets.filter(tweet => tweet.content),
|
||||||
[tweets]
|
[tweets],
|
||||||
)
|
)
|
||||||
|
|
||||||
const tweetContentCount = useMemo(
|
const tweetContentCount = useMemo(
|
||||||
|
@ -45,51 +44,61 @@ export default function BoxVisualizationStats({ tweets, words, totalTweetCount,
|
||||||
console.debug(words)
|
console.debug(words)
|
||||||
|
|
||||||
const wordCount = useMemo(
|
const wordCount = useMemo(
|
||||||
() => words.map(word => word.value).reduce((a, b) => a+b),
|
() => words.map(word => word.value).reduce((a, b) => a + b),
|
||||||
[words]
|
[words],
|
||||||
)
|
)
|
||||||
|
|
||||||
const mostPopularWord = useMemo(
|
const mostPopularWord = useMemo(
|
||||||
() => {
|
() => {
|
||||||
return words.sort((wa, wb) => {
|
return words.sort((wa, wb) => {
|
||||||
if(wa.value > wb.value) return -1
|
if(wa.value > wb.value) {
|
||||||
if(wa.value < wb.value) return 1
|
return -1
|
||||||
|
}
|
||||||
|
if(wa.value < wb.value) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
return 0
|
return 0
|
||||||
})[0].text
|
})[0].text
|
||||||
},
|
},
|
||||||
[words]
|
[words],
|
||||||
)
|
)
|
||||||
|
|
||||||
const users = useMemo(
|
const users = useMemo(
|
||||||
() => tweets.map(tweet => tweet.poster),
|
() => tweets.map(tweet => tweet.poster),
|
||||||
[tweets]
|
[tweets],
|
||||||
)
|
)
|
||||||
|
|
||||||
const uniqueUsers = useMemo(
|
const uniqueUsers = useMemo(
|
||||||
() => [...new Set(users)],
|
() => [...new Set(users)],
|
||||||
[users]
|
[users],
|
||||||
)
|
)
|
||||||
|
|
||||||
const uniqueUsersCount = useMemo(
|
const uniqueUsersCount = useMemo(
|
||||||
() => uniqueUsers.length,
|
() => uniqueUsers.length,
|
||||||
[uniqueUsers]
|
[uniqueUsers],
|
||||||
)
|
)
|
||||||
|
|
||||||
const mostActiveUser = useMemo(
|
const mostActiveUser = useMemo(
|
||||||
() => {
|
() => {
|
||||||
if(uniqueUsers.length === 0) return null
|
if(uniqueUsers.length === 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
return uniqueUsers.map(user => {
|
return uniqueUsers.map(user => {
|
||||||
return {
|
return {
|
||||||
user: user,
|
user: user,
|
||||||
count: tweets.filter(tweet => tweet.poster === user).length
|
count: tweets.filter(tweet => tweet.poster === user).length,
|
||||||
}
|
}
|
||||||
}).sort((a, b) => {
|
}).sort((a, b) => {
|
||||||
if(a.count > b.count) return -1
|
if(a.count > b.count) {
|
||||||
if(a.count < b.count) return 1
|
return -1
|
||||||
|
}
|
||||||
|
if(a.count < b.count) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
return 0
|
return 0
|
||||||
})[0]
|
})[0]
|
||||||
},
|
},
|
||||||
[uniqueUsers, tweets]
|
[uniqueUsers, tweets],
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: tweets with picture count
|
// TODO: tweets with picture count
|
||||||
|
|
|
@ -4,7 +4,7 @@ import ContextLanguage from "../../contexts/ContextLanguage"
|
||||||
|
|
||||||
|
|
||||||
export default function BoxVisualizationWordcloud({ words, ...props }) {
|
export default function BoxVisualizationWordcloud({ words, ...props }) {
|
||||||
const {strings} = useContext(ContextLanguage)
|
const { strings } = useContext(ContextLanguage)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BoxWordcloud header={strings.wordcloud} words={words} {...props}/>
|
<BoxWordcloud header={strings.wordcloud} words={words} {...props}/>
|
||||||
|
|
|
@ -1,15 +1,24 @@
|
||||||
import React from "react"
|
import React from "react"
|
||||||
import ButtonIconOnly from "../base/ButtonIconOnly"
|
import ButtonIconOnly from "../base/ButtonIconOnly"
|
||||||
import { faAt, faChartBar, faClock, faCloud, faHashtag, faMap, faMapPin } from "@fortawesome/free-solid-svg-icons"
|
import { faAt, faClock, faHashtag, faMapPin } from "@fortawesome/free-solid-svg-icons"
|
||||||
|
|
||||||
|
|
||||||
export default function PickerFilter({ currentTab, setTab, ...props }) {
|
export default function PickerFilter({ currentTab, setTab, ...props }) {
|
||||||
return (
|
return (
|
||||||
<div {...props}>
|
<div {...props}>
|
||||||
<ButtonIconOnly onClick={() => setTab("hashtag")} disabled={currentTab === "hashtag"} color={"Grey"} icon={faHashtag}/>
|
<ButtonIconOnly
|
||||||
|
onClick={() => setTab("hashtag")} disabled={currentTab ===
|
||||||
|
"hashtag"} color={"Grey"} icon={faHashtag}
|
||||||
|
/>
|
||||||
<ButtonIconOnly onClick={() => setTab("user")} disabled={currentTab === "user"} color={"Grey"} icon={faAt}/>
|
<ButtonIconOnly onClick={() => setTab("user")} disabled={currentTab === "user"} color={"Grey"} icon={faAt}/>
|
||||||
<ButtonIconOnly onClick={() => setTab("location")} disabled={currentTab === "location"} color={"Grey"} icon={faMapPin}/>
|
<ButtonIconOnly
|
||||||
<ButtonIconOnly onClick={() => setTab("time")} disabled={currentTab === "time"} color={"Grey"} icon={faClock}/>
|
onClick={() => setTab("location")} disabled={currentTab ===
|
||||||
|
"location"} color={"Grey"} icon={faMapPin}
|
||||||
|
/>
|
||||||
|
<ButtonIconOnly
|
||||||
|
onClick={() => setTab("time")} disabled={currentTab ===
|
||||||
|
"time"} color={"Grey"} icon={faClock}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
import React from "react"
|
import React from "react"
|
||||||
import ButtonIconOnly from "../base/ButtonIconOnly"
|
import ButtonIconOnly from "../base/ButtonIconOnly"
|
||||||
import {
|
import { faChartBar, faCloud, faMap, faStar } from "@fortawesome/free-solid-svg-icons"
|
||||||
faAt,
|
|
||||||
faChartBar,
|
|
||||||
faClock,
|
|
||||||
faCloud,
|
|
||||||
faHashtag,
|
|
||||||
faMap,
|
|
||||||
faMapPin,
|
|
||||||
faStar,
|
|
||||||
} from "@fortawesome/free-solid-svg-icons"
|
|
||||||
|
|
||||||
|
|
||||||
export default function PickerVisualization({ currentTab, setTab, ...props }) {
|
export default function PickerVisualization({ currentTab, setTab, ...props }) {
|
||||||
return (
|
return (
|
||||||
<div {...props}>
|
<div {...props}>
|
||||||
<ButtonIconOnly onClick={() => setTab("wordcloud")} disabled={currentTab === "wordcloud"} color={"Grey"} icon={faCloud}/>
|
<ButtonIconOnly
|
||||||
<ButtonIconOnly onClick={() => setTab("histogram")} disabled={currentTab === "histogram"} color={"Grey"} icon={faChartBar}/>
|
onClick={() => setTab("wordcloud")} disabled={currentTab ===
|
||||||
|
"wordcloud"} color={"Grey"} icon={faCloud}
|
||||||
|
/>
|
||||||
|
<ButtonIconOnly
|
||||||
|
onClick={() => setTab("histogram")} disabled={currentTab ===
|
||||||
|
"histogram"} color={"Grey"} icon={faChartBar}
|
||||||
|
/>
|
||||||
<ButtonIconOnly onClick={() => setTab("map")} disabled={currentTab === "map"} color={"Grey"} icon={faMap}/>
|
<ButtonIconOnly onClick={() => setTab("map")} disabled={currentTab === "map"} color={"Grey"} icon={faMap}/>
|
||||||
<ButtonIconOnly onClick={() => setTab("stats")} disabled={currentTab === "stats"} color={"Grey"} icon={faStar}/>
|
<ButtonIconOnly
|
||||||
|
onClick={() => setTab("stats")} disabled={currentTab ===
|
||||||
|
"stats"} color={"Grey"} icon={faStar}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import React, { useContext } from "react"
|
import React, { useContext } from "react"
|
||||||
import { faArchive, faFolder, faFolderOpen, faPencilAlt, faTrash } from "@fortawesome/free-solid-svg-icons"
|
import { faArchive, faFolder, faFolderOpen, faPencilAlt, faTrash } from "@fortawesome/free-solid-svg-icons"
|
||||||
import { useHistory } from "react-router"
|
|
||||||
import ContextLanguage from "../../contexts/ContextLanguage"
|
import ContextLanguage from "../../contexts/ContextLanguage"
|
||||||
import SummaryBase from "../base/summary/SummaryBase"
|
import SummaryBase from "../base/summary/SummaryBase"
|
||||||
import SummaryLeft from "../base/summary/SummaryLeft"
|
import SummaryLeft from "../base/summary/SummaryLeft"
|
||||||
|
@ -45,37 +44,37 @@ export default function SummaryRepository(
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{destroy ?
|
{destroy ?
|
||||||
<SummaryButton
|
<SummaryButton
|
||||||
color={"Red"}
|
color={"Red"}
|
||||||
icon={faTrash}
|
icon={faTrash}
|
||||||
onClick={() => destroy(repo["id"])}
|
onClick={() => destroy(repo["id"])}
|
||||||
disabled={running}
|
disabled={running}
|
||||||
>
|
>
|
||||||
{strings.delete}
|
{strings.delete}
|
||||||
</SummaryButton>
|
</SummaryButton>
|
||||||
: null}
|
: null}
|
||||||
|
|
||||||
{archive ?
|
{archive ?
|
||||||
<SummaryButton
|
<SummaryButton
|
||||||
color={"Grey"}
|
color={"Grey"}
|
||||||
icon={faArchive}
|
icon={faArchive}
|
||||||
onClick={() => archive(repo["id"])}
|
onClick={() => archive(repo["id"])}
|
||||||
disabled={running}
|
disabled={running}
|
||||||
>
|
>
|
||||||
{strings.archive}
|
{strings.archive}
|
||||||
</SummaryButton>
|
</SummaryButton>
|
||||||
: null}
|
: null}
|
||||||
|
|
||||||
{edit ?
|
{edit ?
|
||||||
<SummaryButton
|
<SummaryButton
|
||||||
color={"Yellow"}
|
color={"Yellow"}
|
||||||
icon={faPencilAlt}
|
icon={faPencilAlt}
|
||||||
onClick={() => edit(repo["id"])}
|
onClick={() => edit(repo["id"])}
|
||||||
disabled={running}
|
disabled={running}
|
||||||
>
|
>
|
||||||
{strings.edit}
|
{strings.edit}
|
||||||
</SummaryButton>
|
</SummaryButton>
|
||||||
: null}
|
: null}
|
||||||
|
|
||||||
<SummaryRight/>
|
<SummaryRight/>
|
||||||
</SummaryBase>
|
</SummaryBase>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from "react"
|
import React from "react"
|
||||||
import SummaryBase from "../base/summary/SummaryBase"
|
import SummaryBase from "../base/summary/SummaryBase"
|
||||||
import SummaryLeft from "../base/summary/SummaryLeft"
|
import SummaryLeft from "../base/summary/SummaryLeft"
|
||||||
import { faComment, faLocationArrow, faMapMarker, faMapMarkerAlt, faMapPin } from "@fortawesome/free-solid-svg-icons"
|
import { faComment, faLocationArrow, faMapMarkerAlt } from "@fortawesome/free-solid-svg-icons"
|
||||||
import SummaryText from "../base/summary/SummaryText"
|
import SummaryText from "../base/summary/SummaryText"
|
||||||
import SummaryRight from "../base/summary/SummaryRight"
|
import SummaryRight from "../base/summary/SummaryRight"
|
||||||
|
|
||||||
|
|
|
@ -22,18 +22,18 @@ export default function SummaryUser({ user, destroyUser, running, ...props }) {
|
||||||
upperLabel={strings.type}
|
upperLabel={strings.type}
|
||||||
upperValue={user.isAdmin ? strings.admin : strings.user}
|
upperValue={user.isAdmin ? strings.admin : strings.user}
|
||||||
/>
|
/>
|
||||||
<SummaryButton
|
<SummaryButton
|
||||||
color={"Red"}
|
color={"Red"}
|
||||||
icon={faTrash}
|
icon={faTrash}
|
||||||
onClick={async event => {
|
onClick={async event => {
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
// TODO: Errors are not caught here. Where should they be displayed?
|
// TODO: Errors are not caught here. Where should they be displayed?
|
||||||
await destroyUser(user["email"])
|
await destroyUser(user["email"])
|
||||||
}}
|
}}
|
||||||
disabled={running}
|
disabled={running}
|
||||||
>
|
>
|
||||||
{strings.delete}
|
{strings.delete}
|
||||||
</SummaryButton>
|
</SummaryButton>
|
||||||
<SummaryRight/>
|
<SummaryRight/>
|
||||||
</SummaryBase>
|
</SummaryBase>
|
||||||
)
|
)
|
||||||
|
|
|
@ -76,7 +76,7 @@ export default function useBackendRequest() {
|
||||||
try {
|
try {
|
||||||
json = await response.json()
|
json = await response.json()
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch(error) {
|
||||||
throw new DecodeError(response.status, response.statusText, error)
|
throw new DecodeError(response.status, response.statusText, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
import { useCallback, useContext, useEffect, useState } from "react"
|
import { useCallback, useEffect, useState } from "react"
|
||||||
import ContextServer from "../contexts/ContextServer"
|
|
||||||
import ContextUser from "../contexts/ContextUser"
|
|
||||||
import makeURLSearchParams from "../utils/makeURLSearchParams"
|
|
||||||
import useBackendRequest from "./useBackendRequest"
|
import useBackendRequest from "./useBackendRequest"
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,15 +8,17 @@ import useBackendRequest from "./useBackendRequest"
|
||||||
* @param resourcePath - The path of the resource file.
|
* @param resourcePath - The path of the resource file.
|
||||||
* @param allowViews - An object with maps views to a boolean detailing if they're allowed in the viewset or not.
|
* @param allowViews - An object with maps views to a boolean detailing if they're allowed in the viewset or not.
|
||||||
*/
|
*/
|
||||||
export default function useBackendResource(resourcePath,
|
export default function useBackendResource(
|
||||||
{
|
resourcePath,
|
||||||
retrieve: allowRetrieve = true,
|
{
|
||||||
edit: allowEdit = true,
|
retrieve: allowRetrieve = true,
|
||||||
destroy: allowDestroy = true,
|
edit: allowEdit = true,
|
||||||
action: allowAction = false,
|
destroy: allowDestroy = true,
|
||||||
} = {}) {
|
action: allowAction = false,
|
||||||
|
} = {},
|
||||||
|
) {
|
||||||
|
|
||||||
const {abort, running, apiRequest} = useBackendRequest()
|
const { abort, running, apiRequest } = useBackendRequest()
|
||||||
|
|
||||||
const [firstLoad, setFirstLoad] = useState(false)
|
const [firstLoad, setFirstLoad] = useState(false)
|
||||||
const [resource, setResource] = useState(null)
|
const [resource, setResource] = useState(null)
|
||||||
|
@ -27,7 +26,9 @@ export default function useBackendResource(resourcePath,
|
||||||
|
|
||||||
const apiRetrieve = useCallback(
|
const apiRetrieve = useCallback(
|
||||||
async (init) => {
|
async (init) => {
|
||||||
if(!allowRetrieve) throw new ViewNotAllowedError("retrieve")
|
if(!allowRetrieve) {
|
||||||
|
throw new ViewNotAllowedError("retrieve")
|
||||||
|
}
|
||||||
return await apiRequest("GET", `${resourcePath}`, undefined, init)
|
return await apiRequest("GET", `${resourcePath}`, undefined, init)
|
||||||
},
|
},
|
||||||
[apiRequest, allowRetrieve, resourcePath],
|
[apiRequest, allowRetrieve, resourcePath],
|
||||||
|
@ -35,7 +36,9 @@ export default function useBackendResource(resourcePath,
|
||||||
|
|
||||||
const apiEdit = useCallback(
|
const apiEdit = useCallback(
|
||||||
async (data, init) => {
|
async (data, init) => {
|
||||||
if(!allowEdit) throw new ViewNotAllowedError("edit")
|
if(!allowEdit) {
|
||||||
|
throw new ViewNotAllowedError("edit")
|
||||||
|
}
|
||||||
return await apiRequest("PUT", `${resourcePath}`, data, init)
|
return await apiRequest("PUT", `${resourcePath}`, data, init)
|
||||||
},
|
},
|
||||||
[apiRequest, allowEdit, resourcePath],
|
[apiRequest, allowEdit, resourcePath],
|
||||||
|
@ -43,7 +46,9 @@ export default function useBackendResource(resourcePath,
|
||||||
|
|
||||||
const apiDestroy = useCallback(
|
const apiDestroy = useCallback(
|
||||||
async (init) => {
|
async (init) => {
|
||||||
if(!allowDestroy) throw new ViewNotAllowedError("destroy")
|
if(!allowDestroy) {
|
||||||
|
throw new ViewNotAllowedError("destroy")
|
||||||
|
}
|
||||||
return await apiRequest("DELETE", `${resourcePath}`, undefined, init)
|
return await apiRequest("DELETE", `${resourcePath}`, undefined, init)
|
||||||
},
|
},
|
||||||
[apiRequest, allowDestroy, resourcePath],
|
[apiRequest, allowDestroy, resourcePath],
|
||||||
|
@ -51,10 +56,12 @@ export default function useBackendResource(resourcePath,
|
||||||
|
|
||||||
const apiAction = useCallback(
|
const apiAction = useCallback(
|
||||||
async (method, command, data, init) => {
|
async (method, command, data, init) => {
|
||||||
if(!allowAction) throw new ViewNotAllowedError("action")
|
if(!allowAction) {
|
||||||
|
throw new ViewNotAllowedError("action")
|
||||||
|
}
|
||||||
return await apiRequest(method, `${resourcePath}/${command}`, data, init)
|
return await apiRequest(method, `${resourcePath}/${command}`, data, init)
|
||||||
},
|
},
|
||||||
[apiRequest, allowAction, resourcePath]
|
[apiRequest, allowAction, resourcePath],
|
||||||
)
|
)
|
||||||
|
|
||||||
const retrieveResource = useCallback(
|
const retrieveResource = useCallback(
|
||||||
|
|
|
@ -18,8 +18,9 @@ export default function useBackendViewset(resourcesPath, pkName,
|
||||||
destroy: allowDestroy = true,
|
destroy: allowDestroy = true,
|
||||||
command: allowCommand = false,
|
command: allowCommand = false,
|
||||||
action: allowAction = false,
|
action: allowAction = false,
|
||||||
} = {}) {
|
} = {},
|
||||||
const {abort, running, apiRequest} = useBackendRequest()
|
) {
|
||||||
|
const { abort, running, apiRequest } = useBackendRequest()
|
||||||
|
|
||||||
const [firstLoad, setFirstLoad] = useState(false)
|
const [firstLoad, setFirstLoad] = useState(false)
|
||||||
const [resources, setResources] = useState([])
|
const [resources, setResources] = useState([])
|
||||||
|
@ -27,7 +28,9 @@ export default function useBackendViewset(resourcesPath, pkName,
|
||||||
|
|
||||||
const apiList = useCallback(
|
const apiList = useCallback(
|
||||||
async (init) => {
|
async (init) => {
|
||||||
if(!allowList) throw new ViewNotAllowedError("list")
|
if(!allowList) {
|
||||||
|
throw new ViewNotAllowedError("list")
|
||||||
|
}
|
||||||
return await apiRequest("GET", `${resourcesPath}`, undefined, init)
|
return await apiRequest("GET", `${resourcesPath}`, undefined, init)
|
||||||
},
|
},
|
||||||
[apiRequest, allowList, resourcesPath],
|
[apiRequest, allowList, resourcesPath],
|
||||||
|
@ -35,7 +38,9 @@ export default function useBackendViewset(resourcesPath, pkName,
|
||||||
|
|
||||||
const apiRetrieve = useCallback(
|
const apiRetrieve = useCallback(
|
||||||
async (id, init) => {
|
async (id, init) => {
|
||||||
if(!allowRetrieve) throw new ViewNotAllowedError("retrieve")
|
if(!allowRetrieve) {
|
||||||
|
throw new ViewNotAllowedError("retrieve")
|
||||||
|
}
|
||||||
return await apiRequest("GET", `${resourcesPath}${id}`, undefined, init)
|
return await apiRequest("GET", `${resourcesPath}${id}`, undefined, init)
|
||||||
},
|
},
|
||||||
[apiRequest, allowRetrieve, resourcesPath],
|
[apiRequest, allowRetrieve, resourcesPath],
|
||||||
|
@ -43,7 +48,9 @@ export default function useBackendViewset(resourcesPath, pkName,
|
||||||
|
|
||||||
const apiCreate = useCallback(
|
const apiCreate = useCallback(
|
||||||
async (data, init) => {
|
async (data, init) => {
|
||||||
if(!allowCreate) throw new ViewNotAllowedError("create")
|
if(!allowCreate) {
|
||||||
|
throw new ViewNotAllowedError("create")
|
||||||
|
}
|
||||||
return await apiRequest("POST", `${resourcesPath}`, data, init)
|
return await apiRequest("POST", `${resourcesPath}`, data, init)
|
||||||
},
|
},
|
||||||
[apiRequest, allowCreate, resourcesPath],
|
[apiRequest, allowCreate, resourcesPath],
|
||||||
|
@ -51,7 +58,9 @@ export default function useBackendViewset(resourcesPath, pkName,
|
||||||
|
|
||||||
const apiEdit = useCallback(
|
const apiEdit = useCallback(
|
||||||
async (id, data, init) => {
|
async (id, data, init) => {
|
||||||
if(!allowEdit) throw new ViewNotAllowedError("edit")
|
if(!allowEdit) {
|
||||||
|
throw new ViewNotAllowedError("edit")
|
||||||
|
}
|
||||||
return await apiRequest("PUT", `${resourcesPath}${id}`, data, init)
|
return await apiRequest("PUT", `${resourcesPath}${id}`, data, init)
|
||||||
},
|
},
|
||||||
[apiRequest, allowEdit, resourcesPath],
|
[apiRequest, allowEdit, resourcesPath],
|
||||||
|
@ -59,7 +68,9 @@ export default function useBackendViewset(resourcesPath, pkName,
|
||||||
|
|
||||||
const apiDestroy = useCallback(
|
const apiDestroy = useCallback(
|
||||||
async (id, init) => {
|
async (id, init) => {
|
||||||
if(!allowDestroy) throw new ViewNotAllowedError("destroy")
|
if(!allowDestroy) {
|
||||||
|
throw new ViewNotAllowedError("destroy")
|
||||||
|
}
|
||||||
return await apiRequest("DELETE", `${resourcesPath}${id}`, undefined, init)
|
return await apiRequest("DELETE", `${resourcesPath}${id}`, undefined, init)
|
||||||
},
|
},
|
||||||
[apiRequest, allowDestroy, resourcesPath],
|
[apiRequest, allowDestroy, resourcesPath],
|
||||||
|
@ -67,18 +78,22 @@ export default function useBackendViewset(resourcesPath, pkName,
|
||||||
|
|
||||||
const apiCommand = useCallback(
|
const apiCommand = useCallback(
|
||||||
async (method, command, data, init) => {
|
async (method, command, data, init) => {
|
||||||
if(!allowCommand) throw new ViewNotAllowedError("command")
|
if(!allowCommand) {
|
||||||
|
throw new ViewNotAllowedError("command")
|
||||||
|
}
|
||||||
return await apiRequest(method, `${resourcesPath}${command}`, data, init)
|
return await apiRequest(method, `${resourcesPath}${command}`, data, init)
|
||||||
},
|
},
|
||||||
[apiRequest, allowCommand, resourcesPath]
|
[apiRequest, allowCommand, resourcesPath],
|
||||||
)
|
)
|
||||||
|
|
||||||
const apiAction = useCallback(
|
const apiAction = useCallback(
|
||||||
async (method, id, command, data, init) => {
|
async (method, id, command, data, init) => {
|
||||||
if(!allowAction) throw new ViewNotAllowedError("action")
|
if(!allowAction) {
|
||||||
|
throw new ViewNotAllowedError("action")
|
||||||
|
}
|
||||||
return await apiRequest(method, `${resourcesPath}${id}/${command}`, data, init)
|
return await apiRequest(method, `${resourcesPath}${id}/${command}`, data, init)
|
||||||
},
|
},
|
||||||
[apiRequest, allowAction, resourcesPath]
|
[apiRequest, allowAction, resourcesPath],
|
||||||
)
|
)
|
||||||
|
|
||||||
const listResources = useCallback(
|
const listResources = useCallback(
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, {useContext} from "react"
|
import React, { useContext } from "react"
|
||||||
import Style from "./PageDashboard.module.css"
|
import Style from "./PageDashboard.module.css"
|
||||||
import classNames from "classnames"
|
import classNames from "classnames"
|
||||||
import BoxHeader from "../components/base/BoxHeader"
|
import BoxHeader from "../components/base/BoxHeader"
|
||||||
|
|
|
@ -10,7 +10,7 @@ import ContextLanguage from "../contexts/ContextLanguage"
|
||||||
export default function PageRepositories({ children, className, ...props }) {
|
export default function PageRepositories({ children, className, ...props }) {
|
||||||
const bv = useBackendViewset("/api/v1/repositories/", "id")
|
const bv = useBackendViewset("/api/v1/repositories/", "id")
|
||||||
const history = useHistory()
|
const history = useHistory()
|
||||||
const {strings} = useContext(ContextLanguage)
|
const { strings } = useContext(ContextLanguage)
|
||||||
|
|
||||||
const archive = useCallback(
|
const archive = useCallback(
|
||||||
async (pk) => {
|
async (pk) => {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import PickerFilter from "../components/interactive/PickerFilter"
|
||||||
import useBackendViewset from "../hooks/useBackendViewset"
|
import useBackendViewset from "../hooks/useBackendViewset"
|
||||||
import useBackendResource from "../hooks/useBackendResource"
|
import useBackendResource from "../hooks/useBackendResource"
|
||||||
import { faFolder, faFolderOpen, faTrash } from "@fortawesome/free-solid-svg-icons"
|
import { faFolder, faFolderOpen, faTrash } from "@fortawesome/free-solid-svg-icons"
|
||||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
|
||||||
import { useParams } from "react-router"
|
import { useParams } from "react-router"
|
||||||
import Loading from "../components/base/Loading"
|
import Loading from "../components/base/Loading"
|
||||||
import BoxVisualizationStats from "../components/interactive/BoxVisualizationStats"
|
import BoxVisualizationStats from "../components/interactive/BoxVisualizationStats"
|
||||||
|
@ -17,14 +17,13 @@ import BoxVisualizationMap from "../components/interactive/BoxVisualizationMap"
|
||||||
import BoxVisualizationWordcloud from "../components/interactive/BoxVisualizationWordcloud"
|
import BoxVisualizationWordcloud from "../components/interactive/BoxVisualizationWordcloud"
|
||||||
import BoxFull from "../components/base/BoxFull"
|
import BoxFull from "../components/base/BoxFull"
|
||||||
import ContextLanguage from "../contexts/ContextLanguage"
|
import ContextLanguage from "../contexts/ContextLanguage"
|
||||||
import tokenizeTweetWords from "../utils/countTweetWords"
|
|
||||||
import countTweetWords from "../utils/countTweetWords"
|
import countTweetWords from "../utils/countTweetWords"
|
||||||
import objectToWordcloudFormat from "../utils/objectToWordcloudFormat"
|
import objectToWordcloudFormat from "../utils/objectToWordcloudFormat"
|
||||||
|
|
||||||
|
|
||||||
export default function PageRepository({ className, ...props }) {
|
export default function PageRepository({ className, ...props }) {
|
||||||
const {id} = useParams()
|
const { id } = useParams()
|
||||||
const {strings} = useContext(ContextLanguage)
|
const { strings } = useContext(ContextLanguage)
|
||||||
|
|
||||||
const [visualizationTab, setVisualizationTab] = useState("wordcloud")
|
const [visualizationTab, setVisualizationTab] = useState("wordcloud")
|
||||||
const [addFilterTab, setAddFilterTab] = useState("hashtag")
|
const [addFilterTab, setAddFilterTab] = useState("hashtag")
|
||||||
|
@ -36,7 +35,7 @@ export default function PageRepository({ className, ...props }) {
|
||||||
edit: true,
|
edit: true,
|
||||||
destroy: true,
|
destroy: true,
|
||||||
action: false,
|
action: false,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
const repository = repositoryBr.error ? null : repositoryBr.resource
|
const repository = repositoryBr.error ? null : repositoryBr.resource
|
||||||
|
|
||||||
|
@ -51,16 +50,16 @@ export default function PageRepository({ className, ...props }) {
|
||||||
destroy: false,
|
destroy: false,
|
||||||
command: false,
|
command: false,
|
||||||
action: false,
|
action: false,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
const tweets = tweetsBv.resources && tweetsBv.error ? [] : tweetsBv.resources
|
const tweets = tweetsBv.resources && tweetsBv.error ? [] : tweetsBv.resources
|
||||||
|
|
||||||
const words = useMemo(
|
const words = useMemo(
|
||||||
() => objectToWordcloudFormat(countTweetWords(tweets)),
|
() => objectToWordcloudFormat(countTweetWords(tweets)),
|
||||||
[tweets]
|
[tweets],
|
||||||
)
|
)
|
||||||
|
|
||||||
let contents;
|
let contents
|
||||||
if(!repositoryBr.firstLoad || !tweetsBv.firstLoad) {
|
if(!repositoryBr.firstLoad || !tweetsBv.firstLoad) {
|
||||||
contents = <>
|
contents = <>
|
||||||
<BoxHeader className={Style.Header}>
|
<BoxHeader className={Style.Header}>
|
||||||
|
@ -93,32 +92,32 @@ export default function PageRepository({ className, ...props }) {
|
||||||
setTab={setVisualizationTab}
|
setTab={setVisualizationTab}
|
||||||
/>
|
/>
|
||||||
{visualizationTab === "wordcloud" ?
|
{visualizationTab === "wordcloud" ?
|
||||||
<BoxVisualizationWordcloud
|
<BoxVisualizationWordcloud
|
||||||
className={Style.Wordcloud}
|
className={Style.Wordcloud}
|
||||||
tweets={tweets}
|
tweets={tweets}
|
||||||
words={words}
|
words={words}
|
||||||
/>
|
/>
|
||||||
: null}
|
: null}
|
||||||
{visualizationTab === "histogram" ?
|
{visualizationTab === "histogram" ?
|
||||||
<BoxVisualizationGraph
|
<BoxVisualizationGraph
|
||||||
className={Style.Wordcloud}
|
className={Style.Wordcloud}
|
||||||
tweets={tweets}
|
tweets={tweets}
|
||||||
/>
|
/>
|
||||||
: null}
|
: null}
|
||||||
{visualizationTab === "map" ?
|
{visualizationTab === "map" ?
|
||||||
<BoxVisualizationMap
|
<BoxVisualizationMap
|
||||||
className={Style.Wordcloud}
|
className={Style.Wordcloud}
|
||||||
tweets={tweets}
|
tweets={tweets}
|
||||||
/>
|
/>
|
||||||
: null}
|
: null}
|
||||||
{visualizationTab === "stats" ?
|
{visualizationTab === "stats" ?
|
||||||
<BoxVisualizationStats
|
<BoxVisualizationStats
|
||||||
className={Style.Wordcloud}
|
className={Style.Wordcloud}
|
||||||
tweets={tweets}
|
tweets={tweets}
|
||||||
words={words}
|
words={words}
|
||||||
totalTweetCount={tweets.length}
|
totalTweetCount={tweets.length}
|
||||||
/>
|
/>
|
||||||
: null}
|
: null}
|
||||||
|
|
||||||
<PickerFilter
|
<PickerFilter
|
||||||
className={Style.FilterPicker}
|
className={Style.FilterPicker}
|
||||||
|
|
|
@ -6,8 +6,7 @@
|
||||||
"a b"
|
"a b"
|
||||||
"a c"
|
"a c"
|
||||||
"d e"
|
"d e"
|
||||||
"d f"
|
"d f";
|
||||||
;
|
|
||||||
|
|
||||||
grid-gap: 10px;
|
grid-gap: 10px;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
|
|
|
@ -7,7 +7,7 @@ class ViewNotAllowedError extends NestError {
|
||||||
view
|
view
|
||||||
|
|
||||||
constructor(view) {
|
constructor(view) {
|
||||||
super();
|
super()
|
||||||
|
|
||||||
this.view = view
|
this.view = view
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,12 @@ export default function countTweetWords(tweets = {}) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for(const word of tweet.content.toLowerCase().split(/\s+/)) {
|
for(const word of tweet.content.toLowerCase().split(/\s+/)) {
|
||||||
if(stopwords.includes(word)) continue
|
if(stopwords.includes(word)) {
|
||||||
if(word.startsWith("https://")) continue
|
continue
|
||||||
|
}
|
||||||
|
if(word.startsWith("https://")) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if(!words.hasOwnProperty(word)) {
|
if(!words.hasOwnProperty(word)) {
|
||||||
words[word] = 0
|
words[word] = 0
|
||||||
|
|
|
@ -6,7 +6,7 @@ export default function objectToWordcloudFormat(words) {
|
||||||
}
|
}
|
||||||
result.push({
|
result.push({
|
||||||
text: word,
|
text: word,
|
||||||
value: words[word]
|
value: words[word],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
|
Loading…
Reference in a new issue