admin: more robust arbiter
This commit is contained in:
parent
9831bc03db
commit
5b44f5dcd7
|
@ -3,7 +3,7 @@ import React from 'react';
|
|||
import { view } from 'react-easy-state';
|
||||
import { Button, Modal, Input, Icon, List, Avatar, message } from 'antd';
|
||||
import store from 'src/store';
|
||||
import { Proposal, User } from 'src/types';
|
||||
import { Proposal, User, PROPOSAL_ARBITER_STATUS } from 'src/types';
|
||||
import Search from 'antd/lib/input/Search';
|
||||
import { ButtonProps } from 'antd/lib/button';
|
||||
import './index.less';
|
||||
|
@ -34,6 +34,13 @@ class ArbiterControlNaked extends React.Component<Props, State> {
|
|||
const { showSearch, searching } = this.state;
|
||||
const { results, search, error } = store.arbitersSearch;
|
||||
const showEmpty = !results.length && !searching;
|
||||
|
||||
const disp = {
|
||||
[PROPOSAL_ARBITER_STATUS.MISSING]: 'Nominate arbiter',
|
||||
[PROPOSAL_ARBITER_STATUS.NOMINATED]: 'Change nomination',
|
||||
[PROPOSAL_ARBITER_STATUS.ACCEPTED]: 'Change arbiter',
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* CONTROL */}
|
||||
|
@ -45,14 +52,14 @@ class ArbiterControlNaked extends React.Component<Props, State> {
|
|||
onClick={this.handleShowSearch}
|
||||
{...this.props.buttonProps}
|
||||
>
|
||||
{arbiter ? 'Change arbiter' : 'Set arbiter'}
|
||||
{disp[arbiter.status]}
|
||||
</Button>
|
||||
{/* SEARCH MODAL */}
|
||||
{showSearch && (
|
||||
<Modal
|
||||
title={
|
||||
<>
|
||||
<Icon type="crown" /> Select an arbiter
|
||||
<Icon type="crown" /> Nominate an arbiter
|
||||
</>
|
||||
}
|
||||
visible={true}
|
||||
|
@ -96,7 +103,7 @@ class ArbiterControlNaked extends React.Component<Props, State> {
|
|||
key="select"
|
||||
onClick={() => this.handleSelect(u)}
|
||||
>
|
||||
Select
|
||||
Nominate
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
|
@ -143,7 +150,7 @@ class ArbiterControlNaked extends React.Component<Props, State> {
|
|||
await store.setArbiter(this.props.proposalId, user.userid);
|
||||
message.success(
|
||||
<>
|
||||
Arbiter set for <b>{this.props.title}</b>
|
||||
Arbiter nominated for <b>{this.props.title}</b>
|
||||
</>,
|
||||
);
|
||||
} catch (e) {
|
||||
|
|
|
@ -30,7 +30,7 @@ class Home extends React.Component {
|
|||
<div>
|
||||
<Icon type="exclamation-circle" /> There are <b>{proposalNoArbiterCount}</b>{' '}
|
||||
live proposals <b>without an arbiter</b>.{' '}
|
||||
<Link to="/proposals?filters[]=STATUS_LIVE&filters[]=OTHER_ARBITER">
|
||||
<Link to="/proposals?filters[]=STATUS_LIVE&filters[]=ARBITER_MISSING">
|
||||
Click here
|
||||
</Link>{' '}
|
||||
to view them.
|
||||
|
|
|
@ -16,7 +16,7 @@ import {
|
|||
import TextArea from 'antd/lib/input/TextArea';
|
||||
import store from 'src/store';
|
||||
import { formatDateSeconds } from 'util/time';
|
||||
import { PROPOSAL_STATUS } from 'src/types';
|
||||
import { PROPOSAL_STATUS, PROPOSAL_ARBITER_STATUS } from 'src/types';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Back from 'components/Back';
|
||||
import Info from 'components/Info';
|
||||
|
@ -209,13 +209,13 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
|||
/>
|
||||
);
|
||||
|
||||
const renderSetArbiter = () =>
|
||||
!p.arbiter &&
|
||||
const renderNominateArbiter = () =>
|
||||
PROPOSAL_ARBITER_STATUS.MISSING === p.arbiter.status &&
|
||||
p.status === PROPOSAL_STATUS.LIVE && (
|
||||
<Alert
|
||||
showIcon
|
||||
type="warning"
|
||||
message="No Arbiter on Live Proposal"
|
||||
message="No arbiter on live proposal"
|
||||
description={
|
||||
<div>
|
||||
<p>An arbiter is required to review milestone payout requests.</p>
|
||||
|
@ -225,6 +225,25 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
|||
/>
|
||||
);
|
||||
|
||||
const renderNominatedArbiter = () =>
|
||||
PROPOSAL_ARBITER_STATUS.NOMINATED === p.arbiter.status &&
|
||||
p.status === PROPOSAL_STATUS.LIVE && (
|
||||
<Alert
|
||||
showIcon
|
||||
type="info"
|
||||
message="Arbiter has been nominated"
|
||||
description={
|
||||
<div>
|
||||
<p>
|
||||
<b>{p.arbiter.user!.displayName}</b> has been nominated for arbiter of
|
||||
this proposal but has not yet accepted.
|
||||
</p>
|
||||
<ArbiterControl {...p} />
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
const renderDeetItem = (name: string, val: any) => (
|
||||
<div className="ProposalDetail-deet">
|
||||
<span>{name}</span>
|
||||
|
@ -242,7 +261,8 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
|||
{renderApproved()}
|
||||
{renderReview()}
|
||||
{renderRejected()}
|
||||
{renderSetArbiter()}
|
||||
{renderNominateArbiter()}
|
||||
{renderNominatedArbiter()}
|
||||
<Collapse defaultActiveKey={['brief', 'content']}>
|
||||
<Collapse.Panel key="brief" header="brief">
|
||||
{p.brief}
|
||||
|
@ -279,11 +299,18 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
|||
{renderDeetItem('contributed', p.contributed)}
|
||||
{renderDeetItem('funded (inc. matching)', p.funded)}
|
||||
{renderDeetItem('matching', p.contributionMatching)}
|
||||
{p.arbiter &&
|
||||
renderDeetItem(
|
||||
'arbiter',
|
||||
<Link to={`/users/${p.arbiter.userid}`}>{p.arbiter.displayName}</Link>,
|
||||
)}
|
||||
|
||||
{renderDeetItem(
|
||||
'arbiter',
|
||||
<>
|
||||
{p.arbiter.user && (
|
||||
<Link to={`/users/${p.arbiter.user.userid}`}>
|
||||
{p.arbiter.user.displayName}
|
||||
</Link>
|
||||
)}
|
||||
({p.arbiter.status})
|
||||
</>,
|
||||
)}
|
||||
{p.rfp &&
|
||||
renderDeetItem(
|
||||
'rfp',
|
||||
|
|
|
@ -36,6 +36,17 @@ export interface RFPArgs {
|
|||
category: string;
|
||||
status?: string;
|
||||
}
|
||||
// NOTE: sync with backend/grant/utils/enums.py ProposalArbiterStatus
|
||||
export enum PROPOSAL_ARBITER_STATUS {
|
||||
MISSING = 'MISSING',
|
||||
NOMINATED = 'NOMINATED',
|
||||
ACCEPTED = 'ACCEPTED',
|
||||
}
|
||||
export interface ProposalArbiter {
|
||||
user?: User;
|
||||
proposal: Proposal;
|
||||
status: PROPOSAL_ARBITER_STATUS;
|
||||
}
|
||||
// NOTE: sync with backend/grant/utils/enums.py ProposalStatus
|
||||
export enum PROPOSAL_STATUS {
|
||||
DRAFT = 'DRAFT',
|
||||
|
@ -68,7 +79,7 @@ export interface Proposal {
|
|||
rejectReason: string;
|
||||
contributionMatching: number;
|
||||
rfp?: RFP;
|
||||
arbiter?: User;
|
||||
arbiter: ProposalArbiter;
|
||||
}
|
||||
export interface Comment {
|
||||
commentId: string;
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
import { PROPOSAL_STATUSES, RFP_STATUSES, CONTRIBUTION_STATUSES } from './statuses';
|
||||
import {
|
||||
PROPOSAL_STATUSES,
|
||||
RFP_STATUSES,
|
||||
CONTRIBUTION_STATUSES,
|
||||
PROPOSAL_ARBITER_STATUSES,
|
||||
} from './statuses';
|
||||
|
||||
export interface Filter {
|
||||
id: string;
|
||||
|
@ -29,14 +34,14 @@ const PROPOSAL_FILTERS = PROPOSAL_STATUSES.map(s => ({
|
|||
group: 'Status',
|
||||
}))
|
||||
// proposal has extra filters
|
||||
.concat([
|
||||
{
|
||||
id: `OTHER_ARBITER`,
|
||||
display: `Other: Arbiter`,
|
||||
color: '#cf00d5',
|
||||
group: 'Other',
|
||||
},
|
||||
]);
|
||||
.concat(
|
||||
PROPOSAL_ARBITER_STATUSES.map(s => ({
|
||||
id: `ARBITER_${s.id}`,
|
||||
display: `Arbiter: ${s.tagDisplay}`,
|
||||
color: s.tagColor,
|
||||
group: 'Arbiter',
|
||||
})),
|
||||
);
|
||||
|
||||
export const proposalFilters: Filters = {
|
||||
list: PROPOSAL_FILTERS,
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
import { PROPOSAL_STATUS, RFP_STATUS, CONTRIBUTION_STATUS } from 'src/types';
|
||||
import {
|
||||
PROPOSAL_STATUS,
|
||||
RFP_STATUS,
|
||||
CONTRIBUTION_STATUS,
|
||||
PROPOSAL_ARBITER_STATUS,
|
||||
} from 'src/types';
|
||||
|
||||
export interface StatusSoT<E> {
|
||||
id: E;
|
||||
|
@ -53,6 +58,27 @@ export const PROPOSAL_STATUSES: Array<StatusSoT<PROPOSAL_STATUS>> = [
|
|||
},
|
||||
];
|
||||
|
||||
export const PROPOSAL_ARBITER_STATUSES: Array<StatusSoT<PROPOSAL_ARBITER_STATUS>> = [
|
||||
{
|
||||
id: PROPOSAL_ARBITER_STATUS.MISSING,
|
||||
tagDisplay: 'Missing',
|
||||
tagColor: '#cf00d5',
|
||||
hint: 'Proposal does not have an arbiter.',
|
||||
},
|
||||
{
|
||||
id: PROPOSAL_ARBITER_STATUS.NOMINATED,
|
||||
tagDisplay: 'Nominated',
|
||||
tagColor: '#cf00d5',
|
||||
hint: 'An arbiter has been nominated for this proposal.',
|
||||
},
|
||||
{
|
||||
id: PROPOSAL_ARBITER_STATUS.ACCEPTED,
|
||||
tagDisplay: 'Accepted',
|
||||
tagColor: '#cf00d5',
|
||||
hint: 'Proposal has an arbiter.',
|
||||
},
|
||||
];
|
||||
|
||||
export const RFP_STATUSES: Array<StatusSoT<RFP_STATUS>> = [
|
||||
{
|
||||
id: RFP_STATUS.DRAFT,
|
||||
|
|
Loading…
Reference in New Issue