mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-08-20 01:55:26 -05:00
voting summary frontend
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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.
|
||||
|
||||
59
frontend-react/src/components/plus/SummaryLists.js
Normal file
59
frontend-react/src/components/plus/SummaryLists.js
Normal 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
|
||||
98
frontend-react/src/components/plus/VotingHistory.js
Normal file
98
frontend-react/src/components/plus/VotingHistory.js
Normal 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
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
@@ -11,6 +11,7 @@ export const userLean = gql`
|
||||
plus {
|
||||
membership_status
|
||||
plus_region
|
||||
vouch_status
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
6
package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user