From 048cda821674345bb754b571b9992f8d9237fabb Mon Sep 17 00:00:00 2001 From: Aaron Date: Wed, 6 Feb 2019 16:37:45 -0600 Subject: [PATCH] FE: user profile arbitrations tab --- backend/grant/user/views.py | 10 ++- frontend/client/api/api.ts | 1 + .../components/Profile/ProfileArbitrated.less | 71 +++++++++++++++++++ .../components/Profile/ProfileArbitrated.tsx | 43 +++++++++++ frontend/client/components/Profile/index.tsx | 27 ++++++- frontend/client/modules/users/reducers.ts | 2 + frontend/client/utils/api.ts | 3 + 7 files changed, 153 insertions(+), 4 deletions(-) create mode 100644 frontend/client/components/Profile/ProfileArbitrated.less create mode 100644 frontend/client/components/Profile/ProfileArbitrated.tsx diff --git a/backend/grant/user/views.py b/backend/grant/user/views.py index 4dff1b08..edd2404c 100644 --- a/backend/grant/user/views.py +++ b/backend/grant/user/views.py @@ -65,13 +65,15 @@ def get_me(): parameter("withProposals", type=bool, required=False), parameter("withComments", type=bool, required=False), parameter("withFunded", type=bool, required=False), - parameter("withPending", type=bool, required=False) + parameter("withPending", type=bool, required=False), + parameter("withArbitrated", type=bool, required=False) ) -def get_user(user_id, with_proposals, with_comments, with_funded, with_pending): +def get_user(user_id, with_proposals, with_comments, with_funded, with_pending, with_arbitrated): user = User.get_by_id(user_id) if user: result = user_schema.dump(user) authed_user = get_authed_user() + is_self = authed_user and authed_user.id == user.id if with_proposals: proposals = Proposal.get_by_user(user) proposals_dump = user_proposals_schema.dump(proposals) @@ -86,7 +88,7 @@ def get_user(user_id, with_proposals, with_comments, with_funded, with_pending): comments = Comment.get_by_user(user) comments_dump = user_comments_schema.dump(comments) result["comments"] = comments_dump - if with_pending and authed_user and authed_user.id == user.id: + if with_pending and is_self: pending = Proposal.get_by_user(user, [ ProposalStatus.STAKING, ProposalStatus.PENDING, @@ -95,6 +97,8 @@ def get_user(user_id, with_proposals, with_comments, with_funded, with_pending): ]) pending_dump = user_proposals_schema.dump(pending) result["pendingProposals"] = pending_dump + if with_arbitrated and is_self: + result["arbitrated"] = user_proposals_schema.dump(user.arbitrated_proposals) return result else: message = "User with id matching {} not found".format(user_id) diff --git a/frontend/client/api/api.ts b/frontend/client/api/api.ts index 8cdc8c76..e74eade3 100644 --- a/frontend/client/api/api.ts +++ b/frontend/client/api/api.ts @@ -68,6 +68,7 @@ export function getUser(address: string): Promise<{ data: User }> { withComments: true, withFunded: true, withPending: true, + withArbitrated: true, }, }) .then(res => { diff --git a/frontend/client/components/Profile/ProfileArbitrated.less b/frontend/client/components/Profile/ProfileArbitrated.less new file mode 100644 index 00000000..6aa0e30b --- /dev/null +++ b/frontend/client/components/Profile/ProfileArbitrated.less @@ -0,0 +1,71 @@ +@import '~styles/variables.less'; +@small-query: ~'(max-width: 640px)'; + +.ProfileArbitrated { + display: flex; + padding-bottom: 1.2rem; + border-bottom: 1px solid rgba(0, 0, 0, 0.05); + margin-bottom: 1rem; + + &:last-child { + border-bottom: none; + padding-bottom: none; + } + + @media @small-query { + flex-direction: column; + padding-bottom: 0.6rem; + } + + &-title { + font-size: 1.2rem; + font-weight: 600; + color: inherit; + display: block; + margin-bottom: 0.1rem; + } + + &-block { + flex: 1 0 0%; + + &:last-child { + margin-left: 1.2rem; + flex: 0 0 0%; + min-width: 15rem; + + @media @small-query { + margin-left: 0; + margin-top: 0.6rem; + } + } + + &.is-actions { + display: flex; + justify-content: flex-end; + align-items: center; + + & button + button, + a + button { + margin-left: 0.5rem; + } + } + } + + .ant-tag { + vertical-align: text-top; + } + + &-status { + margin-bottom: 0.6rem; + + & q { + display: block; + margin: 0.5rem; + font-style: italic; + } + + & small { + opacity: 0.6; + } + } +} diff --git a/frontend/client/components/Profile/ProfileArbitrated.tsx b/frontend/client/components/Profile/ProfileArbitrated.tsx new file mode 100644 index 00000000..9abe175f --- /dev/null +++ b/frontend/client/components/Profile/ProfileArbitrated.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import { UserProposal } from 'types'; +import { connect } from 'react-redux'; +import { AppState } from 'store/reducers'; +import './ProfileArbitrated.less'; + +interface OwnProps { + proposal: UserProposal; +} + +interface StateProps { + user: AppState['auth']['user']; +} + +type Props = OwnProps & StateProps; + +class ProfileArbitrated extends React.Component { + render() { + const { title, proposalId } = this.props.proposal; + + return ( +
+
+ + {title} + +
+ You are the arbiter for this proposal. You are responsible for reviewing + milestone payout requests. +
+
+
+ {/* TODO - review milestone button & etc. */} +
+
+ ); + } +} + +export default connect(state => ({ + user: state.auth.user, +}))(ProfileArbitrated); diff --git a/frontend/client/components/Profile/index.tsx b/frontend/client/components/Profile/index.tsx index 04eb5257..53ca829b 100644 --- a/frontend/client/components/Profile/index.tsx +++ b/frontend/client/components/Profile/index.tsx @@ -26,6 +26,7 @@ import ContributionModal from 'components/ContributionModal'; import LinkableTabs from 'components/LinkableTabs'; import './style.less'; import { UserContribution } from 'types'; +import ProfileArbitrated from './ProfileArbitrated'; interface StateProps { usersMap: AppState['users']['map']; @@ -86,11 +87,19 @@ class Profile extends React.Component { return ; } - const { proposals, pendingProposals, contributions, comments, invites } = user; + const { + proposals, + pendingProposals, + contributions, + comments, + invites, + arbitrated, + } = user; const nonePending = pendingProposals.length === 0; const noneCreated = proposals.length === 0; const noneFunded = contributions.length === 0; const noneCommented = comments.length === 0; + const noneArbitrated = arbitrated.length === 0; const noneInvites = user.hasFetchedInvites && invites.length === 0; return ( @@ -185,6 +194,22 @@ class Profile extends React.Component { )} + {isAuthedUser && ( + + {noneArbitrated && ( + + )} + {arbitrated.map(arb => ( + + ))} + + )} diff --git a/frontend/client/modules/users/reducers.ts b/frontend/client/modules/users/reducers.ts index 74606b70..a843f347 100644 --- a/frontend/client/modules/users/reducers.ts +++ b/frontend/client/modules/users/reducers.ts @@ -20,6 +20,7 @@ export interface UserState extends User { isUpdating: boolean; updateError: string | null; pendingProposals: UserProposal[]; + arbitrated: UserProposal[]; proposals: UserProposal[]; contributions: UserContribution[]; comments: UserComment[]; @@ -51,6 +52,7 @@ export const INITIAL_USER_STATE: UserState = { isUpdating: false, updateError: null, pendingProposals: [], + arbitrated: [], proposals: [], contributions: [], comments: [], diff --git a/frontend/client/utils/api.ts b/frontend/client/utils/api.ts index 13557672..3170b492 100644 --- a/frontend/client/utils/api.ts +++ b/frontend/client/utils/api.ts @@ -29,6 +29,9 @@ export function formatUserFromGet(user: UserState) { if (user.pendingProposals) { user.pendingProposals = user.pendingProposals.map(bnUserProp); } + if (user.arbitrated) { + user.arbitrated = user.arbitrated.map(bnUserProp); + } user.proposals = user.proposals.map(bnUserProp); user.contributions = user.contributions.map(c => { c.amount = toZat((c.amount as any) as string);