voting summary frontend

This commit is contained in:
Sendou
2020-01-06 02:36:12 +02:00
parent 6924ba411c
commit 17cb91cd87
12 changed files with 190 additions and 15 deletions

View File

@@ -1,10 +1,10 @@
import React from "react"
import { Loader } from "semantic-ui-react"
const Loading = ({ minHeight = "500px" }) => {
const Loading = ({ inverted = false, minHeight = "500px" }) => {
return (
<div style={{ minHeight: minHeight }}>
<Loader inverted active inline="centered">
<Loader active inverted={inverted} inline="centered">
Loading
</Loader>
</div>

View File

@@ -1,9 +1,11 @@
import React, { useState } from "react"
import { Image } from "semantic-ui-react"
const UserAvatar = ({ twitterName }) => {
const UserAvatar = ({ twitterName, paddingIfNull = false }) => {
const [imageError, setImageError] = useState(false)
if ((!twitterName || imageError) && paddingIfNull)
return <Image style={{ marginLeft: "2em" }} />
if (!twitterName || imageError) return null
return (

View File

@@ -101,7 +101,7 @@ const PlusFAQ = () => {
<Accordion.Content active={active === 5}>
<div style={{ marginLeft: "1.5em" }}>
If you got a high score in the latest voting you can vouch someone
to join a server. For +1 members this ratio is 90% and for +2 85%.
to join a server. For +1 members this ratio is 90% and for +2 80%.
+1 members can choose to vouch someone to either +1 or +2. If the
person you vouched gets kicked in their first voting you can't vouch
anyone for 6 months.

View File

@@ -0,0 +1,59 @@
import React from "react"
import UserAvatar from "../common/UserAvatar"
import { List, Icon, Popup } from "semantic-ui-react"
const getColor = score => (score < 50 ? { color: "red" } : { color: "green" })
const summaryMap = summary => {
console.log("summary", summary)
const { discord_user, score } = summary
return (
<List.Item
key={discord_user.username}
style={{
marginTop: "0.5em",
}}
>
<UserAvatar twitterName={discord_user.twitter_name} paddingIfNull />
<List.Content>
<List.Header as="a" href={`/u/${discord_user.discord_id}`}>
{discord_user.username}#{discord_user.discriminator}{" "}
</List.Header>
<List.Description>
<b>
<span style={{ ...getColor(score.total) }}>{score.total}</span>%
</b>{" "}
(EU <span style={getColor(score.eu)}>{score.eu}</span>% | NA{" "}
<span style={getColor(score.na)}>{score.na}</span>%)
{summary.vouched && (
<Popup
position="right"
content="User was vouched to the server last month"
trigger={<Icon name="bolt" color="teal" size="large" />}
/>
)}
</List.Description>
</List.Content>
</List.Item>
)
}
const SummaryLists = ({ summaries }) => {
console.log("summaries", summaries)
const members = []
const suggested = []
summaries.forEach(summary => {
if (summary.suggested) suggested.push(summary)
else members.push(summary)
})
return (
<>
<List>{members.map(summaryMap)}</List>
<h3>Suggested</h3>
<List>{suggested.map(summaryMap)}</List>
</>
)
}
export default SummaryLists

View File

@@ -0,0 +1,98 @@
import React, { useState, useEffect } from "react"
import { summaries } from "../../graphql/queries/summaries"
import { useQuery } from "@apollo/react-hooks"
import Loading from "../common/Loading"
import { userLean } from "../../graphql/queries/userLean"
import { Redirect } from "react-router-dom"
import Error from "../common/Error"
import { Dropdown } from "semantic-ui-react"
import { months } from "../../utils/lists"
import SummaryLists from "./SummaryLists"
const VotingHistory = () => {
const { data, loading, error } = useQuery(summaries)
const [monthChoices, setMonthChoices] = useState([])
const [forms, setForms] = useState({})
const {
data: userData,
error: userQueryError,
loading: userQueryLoading,
} = useQuery(userLean)
useEffect(() => {
if (loading || error || userQueryLoading || userQueryError) return
const monthsYears = data.summaries.reduce(
(acc, cur) => {
const { month, year } = cur
if (!acc.contains[year]) acc.contains[year] = {}
if (!acc.contains[year][month]) {
acc.contains[year][month] = true
const monthString = `${months[month]} ${year}`
acc.monthChoices.push({
key: monthString,
text: monthString,
value: monthString,
})
}
return acc
},
{ contains: {}, monthChoices: [] }
).monthChoices
setForms({
plus_server:
userData.user.plus.membership_status === "ONE" ? "ONE" : "TWO",
monthYear: monthsYears[0].value,
})
setMonthChoices(monthsYears)
}, [data, loading, error, userQueryLoading, userQueryError, userData])
if (loading || userQueryLoading || monthChoices.length === 0)
return <Loading />
if (error) return <Error errorMessage={error.message} />
if (userQueryError) return <Error errorMessage={userQueryError.message} />
if (!userData.user) return <Redirect to="/access" />
if (!data.summaries) return <Redirect to="/404" />
const parts = forms.monthYear.split(" ")
const month = months.indexOf(parts[0])
const year = parseInt(parts[1])
return (
<>
{userData.user.plus.membership_status === "ONE" && (
<Dropdown
selection
value={forms.plus_server}
onChange={(e, { value }) =>
setForms({ ...forms, plus_server: value })
}
options={["ONE", "TWO"].map(plus_server => ({
key: plus_server,
text: plus_server === "ONE" ? "+1" : "+2",
value: plus_server,
}))}
style={{ margin: "0 1em 1em 0" }}
/>
)}
<Dropdown
selection
value={forms.monthYear}
onChange={(e, { value }) => setForms({ ...forms, monthYear: value })}
options={monthChoices}
/>
{
<SummaryLists
summaries={data.summaries.filter(
summary =>
summary.month === month &&
summary.year === year &&
summary.plus_server === forms.plus_server
)}
/>
}
</>
)
}
export default VotingHistory

View File

@@ -134,7 +134,8 @@ const MainMenu = () => {
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
{data?.user?.plus?.membership_status && (
{(data?.user?.plus?.membership_status ||
data?.user?.plus?.vouch_status) && (
<Dropdown
item
text={data.user.plus.membership_status === "ONE" ? "+1" : "+2"}
@@ -146,9 +147,9 @@ const MainMenu = () => {
<Dropdown.Item as={NavLink} to="/plus/faq">
FAQ
</Dropdown.Item>
{/*<Dropdown.Item as={NavLink} to="/plus/history">
<Dropdown.Item as={NavLink} to="/plus/history">
Voting History
</Dropdown.Item>*/}
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
)}

View File

@@ -24,6 +24,7 @@ const BuildsBrowser = lazy(() => import("../builds/BuildsBrowser"))
const FreeAgentBrowser = lazy(() => import("../freeagents/FreeAgentBrowser"))
const PlusPage = lazy(() => import("../plus/PlusPage"))
const PlusFAQ = lazy(() => import("../plus/PlusFAQ"))
const VotingHistory = lazy(() => import("../plus/VotingHistory"))
const UserPage = lazy(() => import("../user/UserPage"))
const InfoPage = lazy(() => import("./InfoPage"))
const AdminPanel = lazy(() => import("../admin/AdminPanel"))
@@ -31,7 +32,7 @@ const PleaseLogIn = lazy(() => import("../common/PleaseLogIn"))
const Routes = () => {
return (
<Suspense fallback={<Loading />}>
<Suspense fallback={<Loading inverted />}>
<Switch>
<Route exact path="/">
<Page>
@@ -170,6 +171,15 @@ const Routes = () => {
<PlusFAQ />
</Page>
</Route>
<Route exact path="/plus/history">
<Page
title="Voting history"
subtitle="Results of the concluded votings."
icon="history"
>
<VotingHistory />
</Page>
</Route>
<Route path="/about">
<Page
title="Information about this website"

View File

@@ -4,7 +4,9 @@ export const summaries = gql`
{
summaries {
discord_user {
discord_id
username
discriminator
twitter_name
}
score {
@@ -15,6 +17,8 @@ export const summaries = gql`
plus_server
suggested
vouched
year
month
}
}
`

View File

@@ -11,6 +11,7 @@ export const userLean = gql`
plus {
membership_status
plus_region
vouch_status
}
}
}

View File

@@ -284,14 +284,14 @@ const resolvers = {
})
},
summaries: (root, args, ctx) => {
if (!ctx.user || !ctx.user.plus || ctx.user.plus.membership_status)
if (!ctx.user || !ctx.user.plus || !ctx.user.plus.membership_status)
return null
const searchCriteria =
ctx.user.plus.membership_status === "ONE" ? {} : { plus_server: "TWO" }
return Summary.find(searchCriteria)
.populate("discord_user")
.sort({ "score.total": "desc" })
.sort({ "score.total": "desc", year: "desc", month: "desc" })
},
},
Mutation: {
@@ -677,7 +677,7 @@ const resolvers = {
)
} else if (
arrays_plus_server === "TWO" &&
total_score >= 85 &&
total_score >= 80 &&
!can_not_vouch
) {
userUpdates.push(() =>

6
package-lock.json generated
View File

@@ -1610,9 +1610,9 @@
}
},
"mongoose": {
"version": "5.8.3",
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.8.3.tgz",
"integrity": "sha512-WnO4WJ8eZ5Hgwp11Gl2dOxkWYJe8xV7oCqDV3ZbTA7j2q1prc0lPWAd9ZK5R6OhQlp55CleEZXqXUPrZnjSEDQ==",
"version": "5.8.4",
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.8.4.tgz",
"integrity": "sha512-jQjLckUILEQUqBuG+ihjtA9OLmrqcIG5n+vaeHpR++TG8/ug5yy5ogkDnybTSq8Ql5OORud3+OCOc2Uw96q32w==",
"requires": {
"bson": "~1.1.1",
"kareem": "2.3.1",

View File

@@ -23,7 +23,7 @@
"express-session": "^1.17.0",
"graphql": "^14.5.8",
"lodash": "^4.17.15",
"mongoose": "^5.8.3",
"mongoose": "^5.8.4",
"mongoose-unique-validator": "^2.0.3",
"node-fetch": "^2.6.0",
"passport": "^0.4.1",