Compare commits
9 Commits
475abec08b
...
206ed2e63a
Author | SHA1 | Date |
---|---|---|
Daniel Ternyak | 206ed2e63a | |
Daniel Ternyak | 819c15ba9c | |
Daniel Ternyak | c0e05a86e6 | |
Daniel Ternyak | b92a89d8ea | |
Daniel Ternyak | 424ca4d283 | |
Daniel Ternyak | e12b4e1162 | |
Daniel Ternyak | 7f065b4163 | |
Daniel Ternyak | 1a38eea631 | |
Daniel Ternyak | e8e7004f17 |
|
@ -0,0 +1,32 @@
|
|||
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
||||
|
||||
name: Node.js CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- develop
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [12.x]
|
||||
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: cd frontend && yarn && && yarn run lint && yarn run tsc
|
|
@ -0,0 +1,32 @@
|
|||
# This workflow will install Python dependencies, run tests and lint with a single version of Python
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
|
||||
|
||||
name: Python application
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- develop
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python 3.7
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.7
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
cd backend && pip install -r requirements/dev.txt
|
||||
- name: Test with flask test
|
||||
run: |
|
||||
cd backend && cp .env.example .env && flask test
|
|
@ -2,27 +2,11 @@ import React from 'react';
|
|||
import BN from 'bn.js';
|
||||
import { view } from 'react-easy-state';
|
||||
import { RouteComponentProps, withRouter } from 'react-router';
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
Card,
|
||||
Col,
|
||||
Collapse,
|
||||
Input,
|
||||
message,
|
||||
Popconfirm,
|
||||
Row,
|
||||
Tag,
|
||||
} from 'antd';
|
||||
import { Alert, Button, Card, Col, Collapse, Input, message, Popconfirm, Row, Switch, Tag } from 'antd';
|
||||
import TextArea from 'antd/lib/input/TextArea';
|
||||
import store from 'src/store';
|
||||
import { formatDateSeconds, formatDurationSeconds } from 'util/time';
|
||||
import {
|
||||
MILESTONE_STAGE,
|
||||
PROPOSAL_ARBITER_STATUS,
|
||||
PROPOSAL_STAGE,
|
||||
PROPOSAL_STATUS,
|
||||
} from 'src/types';
|
||||
import { MILESTONE_STAGE, PROPOSAL_ARBITER_STATUS, PROPOSAL_STAGE, PROPOSAL_STATUS } from 'src/types';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Back from 'components/Back';
|
||||
import Markdown from 'components/Markdown';
|
||||
|
@ -30,6 +14,7 @@ import ArbiterControl from 'components/ArbiterControl';
|
|||
import { fromZat, toZat } from 'src/util/units';
|
||||
import FeedbackModal from '../FeedbackModal';
|
||||
import { formatUsd } from 'util/formatters';
|
||||
|
||||
import './index.less';
|
||||
|
||||
type Props = RouteComponentProps<any>;
|
||||
|
@ -58,6 +43,8 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
|||
return 'loading proposal...';
|
||||
}
|
||||
|
||||
console.log(p.fundedByZomg);
|
||||
|
||||
const needsArbiter =
|
||||
PROPOSAL_ARBITER_STATUS.MISSING === p.arbiter.status &&
|
||||
p.status === PROPOSAL_STATUS.LIVE &&
|
||||
|
@ -94,9 +81,9 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
|||
</p>
|
||||
)
|
||||
}
|
||||
placement="left"
|
||||
cancelText="cancel"
|
||||
okText="confirm"
|
||||
placement='left'
|
||||
cancelText='cancel'
|
||||
okText='confirm'
|
||||
visible={this.state.showCancelAndRefundPopover}
|
||||
okButtonProps={{
|
||||
loading: store.proposalDetailCanceling,
|
||||
|
@ -105,8 +92,8 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
|||
onConfirm={this.handleConfirmCancel}
|
||||
>
|
||||
<Button
|
||||
icon="close-circle"
|
||||
className="ProposalDetail-controls-control"
|
||||
icon='close-circle'
|
||||
className='ProposalDetail-controls-control'
|
||||
loading={store.proposalDetailCanceling}
|
||||
onClick={this.handleCancelAndRefundClick}
|
||||
disabled={disabled}
|
||||
|
@ -128,9 +115,9 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
|||
with funding? This cannot be undone.
|
||||
</p>
|
||||
}
|
||||
placement="left"
|
||||
cancelText="cancel"
|
||||
okText="confirm"
|
||||
placement='left'
|
||||
cancelText='cancel'
|
||||
okText='confirm'
|
||||
visible={this.state.showChangeToAcceptedWithFundingPopover}
|
||||
okButtonProps={{
|
||||
loading: store.proposalDetailCanceling,
|
||||
|
@ -139,8 +126,8 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
|||
onConfirm={this.handleChangeToAcceptWithFundingConfirm}
|
||||
>
|
||||
<Button
|
||||
icon="close-circle"
|
||||
className="ProposalDetail-controls-control"
|
||||
icon='close-circle'
|
||||
className='ProposalDetail-controls-control'
|
||||
loading={store.proposalDetailChangingToAcceptedWithFunding}
|
||||
onClick={this.handleChangeToAcceptedWithFunding}
|
||||
block
|
||||
|
@ -170,7 +157,7 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
|||
p.status === PROPOSAL_STATUS.APPROVED && (
|
||||
<Alert
|
||||
showIcon
|
||||
type="success"
|
||||
type='success'
|
||||
message={`Approved on ${formatDateSeconds(p.dateApproved)}`}
|
||||
description={`
|
||||
This proposal has been approved and will become live when a team-member
|
||||
|
@ -206,25 +193,25 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
|||
<Col span={isVersionTwo ? 16 : 24}>
|
||||
<Alert
|
||||
showIcon
|
||||
type="warning"
|
||||
message="Review Discussion"
|
||||
type='warning'
|
||||
message='Review Discussion'
|
||||
description={
|
||||
<div>
|
||||
<p>Please review this proposal and render your judgment.</p>
|
||||
<Button
|
||||
className="ProposalDetail-review"
|
||||
className='ProposalDetail-review'
|
||||
loading={store.proposalDetailApprovingDiscussion}
|
||||
icon="check"
|
||||
type="primary"
|
||||
icon='check'
|
||||
type='primary'
|
||||
onClick={() => this.handleApproveDiscussion()}
|
||||
>
|
||||
Open for Public Review
|
||||
</Button>
|
||||
<Button
|
||||
className="ProposalDetail-review"
|
||||
className='ProposalDetail-review'
|
||||
loading={store.proposalDetailApprovingDiscussion}
|
||||
icon="warning"
|
||||
type="default"
|
||||
icon='warning'
|
||||
type='default'
|
||||
onClick={() => {
|
||||
FeedbackModal.open({
|
||||
title: 'Request changes to this proposal?',
|
||||
|
@ -237,10 +224,10 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
|||
Request Changes
|
||||
</Button>
|
||||
<Button
|
||||
className="ProposalDetail-review"
|
||||
className='ProposalDetail-review'
|
||||
loading={store.proposalDetailRejectingPermanently}
|
||||
icon="close"
|
||||
type="danger"
|
||||
icon='close'
|
||||
type='danger'
|
||||
onClick={() => {
|
||||
FeedbackModal.open({
|
||||
title: 'Reject this proposal permanently?',
|
||||
|
@ -269,27 +256,27 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
|||
<Col span={isVersionTwo ? 16 : 24}>
|
||||
<Alert
|
||||
showIcon
|
||||
type="warning"
|
||||
message="Review Pending"
|
||||
type='warning'
|
||||
message='Review Pending'
|
||||
description={
|
||||
<div>
|
||||
<p>Please review this proposal and render your judgment.</p>
|
||||
|
||||
<>
|
||||
<Button
|
||||
className="ProposalDetail-review"
|
||||
className='ProposalDetail-review'
|
||||
loading={store.proposalDetailAcceptingProposal}
|
||||
icon="check"
|
||||
type="primary"
|
||||
icon='check'
|
||||
type='primary'
|
||||
onClick={() => this.handleAcceptProposal(true, true)}
|
||||
>
|
||||
Approve With Funding
|
||||
</Button>
|
||||
<Button
|
||||
className="ProposalDetail-review"
|
||||
className='ProposalDetail-review'
|
||||
loading={store.proposalDetailAcceptingProposal}
|
||||
icon="check"
|
||||
type="default"
|
||||
icon='check'
|
||||
type='default'
|
||||
onClick={() => this.handleAcceptProposal(true, false)}
|
||||
>
|
||||
Approve Without Funding
|
||||
|
@ -297,10 +284,10 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
|||
</>
|
||||
|
||||
<Button
|
||||
className="ProposalDetail-review"
|
||||
className='ProposalDetail-review'
|
||||
loading={store.proposalDetailMarkingChangesAsResolved}
|
||||
icon="close"
|
||||
type="danger"
|
||||
icon='close'
|
||||
type='danger'
|
||||
onClick={() => {
|
||||
FeedbackModal.open({
|
||||
title: 'Request changes to this proposal?',
|
||||
|
@ -325,8 +312,8 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
|||
p.status === PROPOSAL_STATUS.REJECTED && (
|
||||
<Alert
|
||||
showIcon
|
||||
type="error"
|
||||
message="Changes requested"
|
||||
type='error'
|
||||
message='Changes requested'
|
||||
description={
|
||||
<div>
|
||||
<p>
|
||||
|
@ -346,8 +333,8 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
|||
p.changesRequestedDiscussion && (
|
||||
<Alert
|
||||
showIcon
|
||||
type="error"
|
||||
message="Changes requested"
|
||||
type='error'
|
||||
message='Changes requested'
|
||||
description={
|
||||
<div>
|
||||
<p>
|
||||
|
@ -360,10 +347,10 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
|||
<br />
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
|
||||
<Button
|
||||
className="ProposalDetail-review"
|
||||
className='ProposalDetail-review'
|
||||
loading={false}
|
||||
icon="check"
|
||||
type="danger"
|
||||
icon='check'
|
||||
type='danger'
|
||||
onClick={this.handleMarkChangesAsResolved}
|
||||
>
|
||||
Mark Request as Resolved
|
||||
|
@ -381,8 +368,8 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
|||
{!p.kycApproved ? (
|
||||
<Alert
|
||||
showIcon
|
||||
type="error"
|
||||
message="KYC approval required"
|
||||
type='error'
|
||||
message='KYC approval required'
|
||||
description={
|
||||
<div>
|
||||
<p>
|
||||
|
@ -390,10 +377,10 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
|||
with payouts.
|
||||
</p>
|
||||
<Button
|
||||
className="ProposalDetail-review"
|
||||
className='ProposalDetail-review'
|
||||
loading={store.proposalDetailApprovingKyc}
|
||||
icon="check"
|
||||
type="primary"
|
||||
icon='check'
|
||||
type='primary'
|
||||
onClick={() => this.handleApproveKYC()}
|
||||
>
|
||||
KYC Approved
|
||||
|
@ -404,8 +391,8 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
|||
) : (
|
||||
<Alert
|
||||
showIcon
|
||||
type="warning"
|
||||
message="No arbiter on live proposal"
|
||||
type='warning'
|
||||
message='No arbiter on live proposal'
|
||||
description={
|
||||
<div>
|
||||
<p>An arbiter is required to review milestone payout requests.</p>
|
||||
|
@ -422,8 +409,8 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
|||
p.status === PROPOSAL_STATUS.LIVE && (
|
||||
<Alert
|
||||
showIcon
|
||||
type="info"
|
||||
message="Arbiter has been nominated"
|
||||
type='info'
|
||||
message='Arbiter has been nominated'
|
||||
description={
|
||||
<div>
|
||||
<p>
|
||||
|
@ -469,9 +456,9 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
|||
|
||||
return (
|
||||
<Alert
|
||||
className="ProposalDetail-alert"
|
||||
className='ProposalDetail-alert'
|
||||
showIcon
|
||||
type="warning"
|
||||
type='warning'
|
||||
message={null}
|
||||
description={
|
||||
<div>
|
||||
|
@ -487,9 +474,9 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
|||
</p>{' '}
|
||||
<pre>{p.payoutAddress}</pre>
|
||||
<Input.Search
|
||||
placeholder="please enter payment txid"
|
||||
placeholder='please enter payment txid'
|
||||
value={this.state.paidTxId}
|
||||
enterButton="Mark Paid"
|
||||
enterButton='Mark Paid'
|
||||
onChange={e => this.setState({ paidTxId: e.target.value })}
|
||||
onSearch={this.handlePaidMilestone}
|
||||
/>
|
||||
|
@ -503,7 +490,7 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
|||
p.isFailed && (
|
||||
<Alert
|
||||
showIcon
|
||||
type="error"
|
||||
type='error'
|
||||
message={
|
||||
p.stage === PROPOSAL_STAGE.FAILED ? 'Proposal failed' : 'Proposal canceled'
|
||||
}
|
||||
|
@ -525,17 +512,16 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
|||
);
|
||||
|
||||
const renderDeetItem = (name: string, val: any) => (
|
||||
<div className="ProposalDetail-deet">
|
||||
<div className='ProposalDetail-deet'>
|
||||
<span>{name}</span>
|
||||
{val}
|
||||
</div>
|
||||
);
|
||||
|
||||
console.log(p);
|
||||
|
||||
// @ts-ignore
|
||||
return (
|
||||
<div className="ProposalDetail">
|
||||
<Back to="/proposals" text="Proposals" />
|
||||
<div className='ProposalDetail'>
|
||||
<Back to='/proposals' text='Proposals' />
|
||||
<h1>{p.title}</h1>
|
||||
<Row gutter={16}>
|
||||
{/* MAIN */}
|
||||
|
@ -550,22 +536,22 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
|||
{renderMilestoneAccepted()}
|
||||
{renderFailed()}
|
||||
<Collapse defaultActiveKey={['brief', 'content', 'milestones']}>
|
||||
<Collapse.Panel key="brief" header="brief">
|
||||
<Collapse.Panel key='brief' header='brief'>
|
||||
{p.brief}
|
||||
</Collapse.Panel>
|
||||
|
||||
<Collapse.Panel key="content" header="content">
|
||||
<Collapse.Panel key='content' header='content'>
|
||||
<Markdown source={p.content} />
|
||||
</Collapse.Panel>
|
||||
|
||||
<Collapse.Panel key="milestones" header="milestones">
|
||||
<Collapse.Panel key='milestones' header='milestones'>
|
||||
{p.milestones.map((milestone, i) => (
|
||||
<Card
|
||||
title={
|
||||
<>
|
||||
{milestone.title + ' '}
|
||||
{milestone.immediatePayout && (
|
||||
<Tag color="magenta">Immediate Payout</Tag>
|
||||
<Tag color='magenta'>Immediate Payout</Tag>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
|
@ -590,7 +576,7 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
|||
))}
|
||||
</Collapse.Panel>
|
||||
|
||||
<Collapse.Panel key="json" header="json">
|
||||
<Collapse.Panel key='json' header='json'>
|
||||
<pre>{JSON.stringify(p, null, 4)}</pre>
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
|
@ -599,26 +585,38 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
|||
{/* RIGHT SIDE */}
|
||||
<Col span={6}>
|
||||
{p.isVersionTwo &&
|
||||
!p.acceptedWithFunding &&
|
||||
p.stage === PROPOSAL_STAGE.WIP && (
|
||||
<Alert
|
||||
message="Accepted without funding"
|
||||
description="This proposal has been posted publicly, but isn't being funded by the Zcash Foundation."
|
||||
type="info"
|
||||
showIcon
|
||||
/>
|
||||
)}
|
||||
!p.acceptedWithFunding &&
|
||||
p.stage === PROPOSAL_STAGE.WIP && (
|
||||
<Alert
|
||||
message='Accepted without funding'
|
||||
description="This proposal has been posted publicly, but isn't being funded by the Zcash Foundation."
|
||||
type='info'
|
||||
showIcon
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* ACTIONS */}
|
||||
<Card size="small" className="ProposalDetail-controls">
|
||||
<Card size='small' className='ProposalDetail-controls'>
|
||||
{renderCancelControl()}
|
||||
{renderArbiterControl()}
|
||||
|
||||
{
|
||||
p.acceptedWithFunding &&
|
||||
<div style={{ marginTop: '10px' }}>
|
||||
<Switch checkedChildren='Funded by ZOMG'
|
||||
unCheckedChildren='Funded by ZF'
|
||||
onChange={this.handleSwitchFunder}
|
||||
loading={store.proposalDetailSwitchingFunder}
|
||||
checked={p.fundedByZomg} />
|
||||
</div>
|
||||
}
|
||||
|
||||
{shouldShowChangeToAcceptedWithFunding &&
|
||||
renderChangeToAcceptedWithFundingControl()}
|
||||
renderChangeToAcceptedWithFundingControl()}
|
||||
</Card>
|
||||
|
||||
{/* DETAILS */}
|
||||
<Card title="Details" size="small">
|
||||
<Card title='Details' size='small'>
|
||||
{renderDeetItem('id', p.proposalId)}
|
||||
{renderDeetItem('created', formatDateSeconds(p.dateCreated))}
|
||||
{renderDeetItem(
|
||||
|
@ -630,10 +628,10 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
|||
formatDurationSeconds(p.deadlineDuration),
|
||||
)}
|
||||
{p.datePublished &&
|
||||
renderDeetItem(
|
||||
'(deadline)',
|
||||
formatDateSeconds(p.datePublished + p.deadlineDuration),
|
||||
)}
|
||||
renderDeetItem(
|
||||
'(deadline)',
|
||||
formatDateSeconds(p.datePublished + p.deadlineDuration),
|
||||
)}
|
||||
{renderDeetItem('isFailed', JSON.stringify(p.isFailed))}
|
||||
{renderDeetItem('status', p.status)}
|
||||
{renderDeetItem('stage', p.stage)}
|
||||
|
@ -662,14 +660,14 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
|||
</>,
|
||||
)}
|
||||
{p.rfp &&
|
||||
renderDeetItem(
|
||||
'rfp',
|
||||
<Link to={`/rfps/${p.rfp.id}`}>{p.rfp.title}</Link>,
|
||||
)}
|
||||
renderDeetItem(
|
||||
'rfp',
|
||||
<Link to={`/rfps/${p.rfp.id}`}>{p.rfp.title}</Link>,
|
||||
)}
|
||||
</Card>
|
||||
|
||||
{/* TEAM */}
|
||||
<Card title="Team" size="small">
|
||||
<Card title='Team' size='small'>
|
||||
{p.team.map(t => (
|
||||
<div key={t.userid}>
|
||||
<Link to={`/users/${t.userid}`}>{t.displayName}</Link>
|
||||
|
@ -783,6 +781,10 @@ class ProposalDetailNaked extends React.Component<Props, State> {
|
|||
await store.markMilestonePaid(pid, mid, this.state.paidTxId);
|
||||
message.success('Marked milestone paid.');
|
||||
};
|
||||
|
||||
private handleSwitchFunder = async (checkValue: boolean) => {
|
||||
store.switchProposalFunder(checkValue);
|
||||
};
|
||||
}
|
||||
|
||||
const ProposalDetail = withRouter(view(ProposalDetailNaked));
|
||||
|
|
|
@ -142,6 +142,11 @@ async function approveDiscussion(
|
|||
return data;
|
||||
}
|
||||
|
||||
async function switchProposalFunder(id: number, fundedByZomg: boolean) {
|
||||
const { data } = await api.put(`/admin/proposals/${id}/adjust-funder`, {fundedByZomg});
|
||||
return data;
|
||||
}
|
||||
|
||||
async function approveProposalKYC(id: number) {
|
||||
const { data } = await api.put(`/admin/proposals/${id}/approve-kyc`);
|
||||
return data;
|
||||
|
@ -351,6 +356,7 @@ const app = store({
|
|||
proposalDetailMarkingChangesAsResolved: false,
|
||||
proposalDetailAcceptingProposal: false,
|
||||
proposalDetailApprovingKyc: false,
|
||||
proposalDetailSwitchingFunder: false,
|
||||
proposalDetailMarkingMilestonePaid: false,
|
||||
proposalDetailCanceling: false,
|
||||
proposalDetailUpdating: false,
|
||||
|
@ -695,6 +701,24 @@ const app = store({
|
|||
}
|
||||
},
|
||||
|
||||
async switchProposalFunder(fundedByZomg: boolean) {
|
||||
if (!app.proposalDetail) {
|
||||
const m = 'store.acceptProposal(): Expected proposalDetail to be populated!';
|
||||
app.generalError.push(m);
|
||||
console.error(m);
|
||||
return;
|
||||
}
|
||||
app.proposalDetailSwitchingFunder = true;
|
||||
try {
|
||||
const { proposalId } = app.proposalDetail;
|
||||
const res = await switchProposalFunder(proposalId, fundedByZomg);
|
||||
app.updateProposalInStore(res);
|
||||
} catch (e) {
|
||||
handleApiError(e);
|
||||
}
|
||||
app.proposalDetailSwitchingFunder = false;
|
||||
},
|
||||
|
||||
async approveProposalKYC() {
|
||||
if (!app.proposalDetail) {
|
||||
const m = 'store.acceptProposal(): Expected proposalDetail to be populated!';
|
||||
|
|
|
@ -124,6 +124,7 @@ export interface Proposal {
|
|||
changesRequestedDiscussion: boolean | null;
|
||||
changesRequestedDiscussionReason: string | null;
|
||||
kycApproved: null | boolean;
|
||||
fundedByZomg: boolean;
|
||||
}
|
||||
export interface Comment {
|
||||
id: number;
|
||||
|
|
|
@ -4,7 +4,7 @@ from functools import reduce
|
|||
|
||||
from flask import Blueprint, request
|
||||
from marshmallow import fields, validate
|
||||
from sqlalchemy import func, or_, text
|
||||
from sqlalchemy import func, text
|
||||
|
||||
import grant.utils.admin as admin
|
||||
import grant.utils.auth as auth
|
||||
|
@ -25,7 +25,7 @@ from grant.proposal.models import (
|
|||
admin_proposal_contributions_schema,
|
||||
)
|
||||
from grant.rfp.models import RFP, admin_rfp_schema, admin_rfps_schema
|
||||
from grant.user.models import User, UserSettings, admin_users_schema, admin_user_schema
|
||||
from grant.user.models import User, admin_users_schema, admin_user_schema
|
||||
from grant.utils import pagination
|
||||
from grant.utils.enums import (
|
||||
ProposalStatus,
|
||||
|
@ -390,6 +390,22 @@ def approve_proposal_kyc(id):
|
|||
return proposal_schema.dump(proposal)
|
||||
|
||||
|
||||
@blueprint.route('/proposals/<id>/adjust-funder', methods=['PUT'])
|
||||
@body({
|
||||
"fundedByZomg": fields.Bool(required=True),
|
||||
})
|
||||
@admin.admin_auth_required
|
||||
def adjust_funder(id, funded_by_zomg):
|
||||
proposal = Proposal.query.get(id)
|
||||
if not proposal:
|
||||
return {"message": "No proposal found."}, 404
|
||||
|
||||
proposal.funded_by_zomg = funded_by_zomg
|
||||
db.session.add(proposal)
|
||||
db.session.commit()
|
||||
return proposal_schema.dump(proposal)
|
||||
|
||||
|
||||
@blueprint.route('/proposals/<id>/accept', methods=['PUT'])
|
||||
@body({
|
||||
"isAccepted": fields.Bool(required=True),
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import datetime
|
||||
import json
|
||||
from typing import Optional
|
||||
from decimal import Decimal, ROUND_DOWN
|
||||
from functools import reduce
|
||||
from typing import Optional
|
||||
|
||||
from marshmallow import post_dump
|
||||
from sqlalchemy import func, or_, select, ForeignKey
|
||||
|
@ -10,15 +10,14 @@ from sqlalchemy.ext.hybrid import hybrid_property
|
|||
from sqlalchemy.orm import column_property
|
||||
|
||||
from grant.comment.models import Comment
|
||||
from grant.milestone.models import Milestone
|
||||
from grant.email.send import send_email
|
||||
from grant.extensions import ma, db
|
||||
from grant.milestone.models import Milestone
|
||||
from grant.settings import PROPOSAL_STAKING_AMOUNT, PROPOSAL_TARGET_MAX
|
||||
from grant.task.jobs import ContributionExpired
|
||||
from grant.utils.enums import (
|
||||
ProposalStatus,
|
||||
ProposalStage,
|
||||
Category,
|
||||
ContributionStatus,
|
||||
ProposalArbiterStatus,
|
||||
MilestoneStage,
|
||||
|
@ -332,43 +331,49 @@ class ProposalRevision(db.Model):
|
|||
if old_proposal.title != new_proposal.title:
|
||||
proposal_changes.append({"type": ProposalChange.PROPOSAL_EDIT_TITLE})
|
||||
|
||||
milestone_changes = ProposalRevision.calculate_milestone_changes(old_proposal.milestones, new_proposal.milestones)
|
||||
milestone_changes = ProposalRevision.calculate_milestone_changes(old_proposal.milestones,
|
||||
new_proposal.milestones)
|
||||
|
||||
return proposal_changes + milestone_changes
|
||||
|
||||
|
||||
def default_proposal_content():
|
||||
return """# Applicant background
|
||||
return """### If you have any doubts about the questions below, please reach out to anyone on the ZOMG on the [Zcash forums](https://forum.zcashcommunity.com/).
|
||||
|
||||
Summarize you and/or your team’s background and experience. Demonstrate that you have the skills and expertise necessary for the project that you’re proposing. Institutional bona fides are not required, but we want to hear about your track record.
|
||||
# Description of Problem or Opportunity
|
||||
In addition to describing the problem/opportunity, please give a sense of how serious or urgent of a need you believe this to be. What evidence do you have? What validation have you already done, or how do you think you could validate this?
|
||||
|
||||
# Motivation and overview
|
||||
# Proposed Solution
|
||||
Describe the solution at a high level. Please be specific about who the users and stakeholders are and how they would interact with your solution. E.g. retail ZEC holders, Zcash core devs, wallet devs, DeFi users, potential Zcash community participants.
|
||||
|
||||
What are your high-level goals? Why are they important? How is your project connected to [ZF’s mission](https://www.zfnd.org/about/#mission) and priorities? Whose needs will it serve?
|
||||
# Solution Format
|
||||
What is the exact form of the deliverable you’re creating? E.g. code shipped within the zcashd and zebra code bases, a website, a feature within a wallet, a text/markdown file, user manuals, etc.
|
||||
|
||||
# Technical approach
|
||||
|
||||
Dive into the _how_ of your project. Describe your approaches, components, workflows, methodology, etc. Bullet points and diagrams are appreciated!
|
||||
|
||||
# How big of a problem would it be to not solve this problem?
|
||||
|
||||
# Execution risks
|
||||
What obstacles do you expect? What is most likely to go wrong? Which unknown factors or dependencies could jeopardize success? Who would have to incorporate your work in order for it to be usable?
|
||||
|
||||
What obstacles do you expect? What is most likely to go wrong? Which unknown factors or dependencies could jeopardize success? What are your contingency plans? Will subsequent activities be required to maximize impact?
|
||||
|
||||
# Downsides
|
||||
|
||||
# Unintended Consequences Downsides
|
||||
What are the negative ramifications if your project is successful? Consider usability, stability, privacy, integrity, availability, decentralization, interoperability, maintainability, technical debt, requisite education, etc.
|
||||
|
||||
# Evaluation plan
|
||||
What metrics for success can you share with the community once you’re done? In addition to quantitative metrics, what qualitative metrics do you think you could report?
|
||||
|
||||
What will your project look like if successful? How will we be able to tell? Include quantifiable metrics if possible.
|
||||
|
||||
# Tasks and schedule
|
||||
|
||||
# Schedule and Milestones
|
||||
What is your timeline for the project? Include concrete milestones and the major tasks required to complete each milestone.
|
||||
|
||||
# Budget and justification
|
||||
# Budget and Payout Timeline
|
||||
|
||||
How much funding do you need, and how will it be allocated (e.g., compensation for your effort, specific equipment, specific external services)? Specify a total cost, break it up into budget items, and explain the rationale for each. Feel free to present multiple options in terms of scope and cost.
|
||||
How much funding do you need, and how will it be allocated (e.g., compensation for your effort, specific equipment, specific external services)? Please tie your payout timelines to the milestones presented in the previous step. Convention has been for applicants to base their budget on hours of work and an hourly rate, but we are open to proposals based on the value of outcomes instead.
|
||||
|
||||
# Applicant background
|
||||
Summarize you and/or your team’s background and experience. Demonstrate that you have the skills and expertise necessary for the project that you’re proposing. Institutional bona fides are not required, but we want to hear about your track record.
|
||||
|
||||
"""
|
||||
|
||||
|
@ -392,6 +397,7 @@ class Proposal(db.Model):
|
|||
date_published = db.Column(db.DateTime)
|
||||
reject_reason = db.Column(db.String())
|
||||
kyc_approved = db.Column(db.Boolean(), nullable=True, default=False)
|
||||
funded_by_zomg = db.Column(db.Boolean(), nullable=True, default=False)
|
||||
|
||||
accepted_with_funding = db.Column(db.Boolean(), nullable=True)
|
||||
changes_requested_discussion = db.Column(db.Boolean(), nullable=True)
|
||||
|
@ -422,21 +428,23 @@ class Proposal(db.Model):
|
|||
)
|
||||
followers_count = column_property(
|
||||
select([func.count(proposal_follower.c.proposal_id)])
|
||||
.where(proposal_follower.c.proposal_id == id)
|
||||
.correlate_except(proposal_follower)
|
||||
.where(proposal_follower.c.proposal_id == id)
|
||||
.correlate_except(proposal_follower)
|
||||
)
|
||||
likes = db.relationship(
|
||||
"User", secondary=proposal_liker, back_populates="liked_proposals"
|
||||
)
|
||||
likes_count = column_property(
|
||||
select([func.count(proposal_liker.c.proposal_id)])
|
||||
.where(proposal_liker.c.proposal_id == id)
|
||||
.correlate_except(proposal_liker)
|
||||
.where(proposal_liker.c.proposal_id == id)
|
||||
.correlate_except(proposal_liker)
|
||||
)
|
||||
live_draft_parent_id = db.Column(db.Integer, ForeignKey('proposal.id'))
|
||||
live_draft = db.relationship("Proposal", uselist=False, backref=db.backref('live_draft_parent', remote_side=[id], uselist=False))
|
||||
live_draft = db.relationship("Proposal", uselist=False,
|
||||
backref=db.backref('live_draft_parent', remote_side=[id], uselist=False))
|
||||
|
||||
revisions = db.relationship(ProposalRevision, foreign_keys=[ProposalRevision.proposal_id], lazy=True, cascade="all, delete-orphan")
|
||||
revisions = db.relationship(ProposalRevision, foreign_keys=[ProposalRevision.proposal_id], lazy=True,
|
||||
cascade="all, delete-orphan")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
@ -462,6 +470,7 @@ class Proposal(db.Model):
|
|||
self.deadline_duration = deadline_duration
|
||||
self.stage = stage
|
||||
self.version = '2'
|
||||
self.funded_by_zomg = True
|
||||
|
||||
@staticmethod
|
||||
def simple_validate(proposal):
|
||||
|
@ -535,7 +544,6 @@ class Proposal(db.Model):
|
|||
# Then run through regular validation
|
||||
Proposal.simple_validate(vars(self))
|
||||
|
||||
|
||||
def validate_milestone_days(self):
|
||||
for milestone in self.milestones:
|
||||
if milestone.immediate_payout:
|
||||
|
@ -612,11 +620,11 @@ class Proposal(db.Model):
|
|||
self.rfp_opt_in = opt_in
|
||||
|
||||
def create_contribution(
|
||||
self,
|
||||
amount,
|
||||
user_id: int = None,
|
||||
staking: bool = False,
|
||||
private: bool = True,
|
||||
self,
|
||||
amount,
|
||||
user_id: int = None,
|
||||
staking: bool = False,
|
||||
private: bool = True,
|
||||
):
|
||||
contribution = ProposalContribution(
|
||||
proposal_id=self.id,
|
||||
|
@ -923,8 +931,8 @@ class Proposal(db.Model):
|
|||
return False
|
||||
res = (
|
||||
db.session.query(proposal_follower)
|
||||
.filter_by(user_id=authed.id, proposal_id=self.id)
|
||||
.count()
|
||||
.filter_by(user_id=authed.id, proposal_id=self.id)
|
||||
.count()
|
||||
)
|
||||
if res:
|
||||
return True
|
||||
|
@ -939,8 +947,8 @@ class Proposal(db.Model):
|
|||
return False
|
||||
res = (
|
||||
db.session.query(proposal_liker)
|
||||
.filter_by(user_id=authed.id, proposal_id=self.id)
|
||||
.count()
|
||||
.filter_by(user_id=authed.id, proposal_id=self.id)
|
||||
.count()
|
||||
)
|
||||
if res:
|
||||
return True
|
||||
|
@ -1099,7 +1107,8 @@ class ProposalSchema(ma.Schema):
|
|||
"changes_requested_discussion",
|
||||
"changes_requested_discussion_reason",
|
||||
"live_draft_id",
|
||||
"kyc_approved"
|
||||
"kyc_approved",
|
||||
"funded_by_zomg"
|
||||
)
|
||||
|
||||
date_created = ma.Method("get_date_created")
|
||||
|
@ -1109,6 +1118,7 @@ class ProposalSchema(ma.Schema):
|
|||
is_version_two = ma.Method("get_is_version_two")
|
||||
tip_jar_view_key = ma.Method("get_tip_jar_view_key")
|
||||
live_draft_id = ma.Method("get_live_draft_id")
|
||||
funded_by_zomg = ma.Method("get_funded_by_zomg")
|
||||
|
||||
updates = ma.Nested("ProposalUpdateSchema", many=True)
|
||||
team = ma.Nested("UserSchema", many=True)
|
||||
|
@ -1118,6 +1128,14 @@ class ProposalSchema(ma.Schema):
|
|||
rfp = ma.Nested("RFPSchema", exclude=["accepted_proposals"])
|
||||
arbiter = ma.Nested("ProposalArbiterSchema", exclude=["proposal"])
|
||||
|
||||
def get_funded_by_zomg(self, obj):
|
||||
if obj.funded_by_zomg is None:
|
||||
return False
|
||||
elif obj.funded_by_zomg is False:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def get_proposal_id(self, obj):
|
||||
return obj.id
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 91b16dc2fd74
|
||||
Revises: d03c91f3038d
|
||||
Create Date: 2021-02-01 17:00:23.721765
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '91b16dc2fd74'
|
||||
down_revision = 'd03c91f3038d'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('proposal', sa.Column('funded_by_zomg', sa.Boolean(), nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('proposal', 'funded_by_zomg')
|
||||
# ### end Alembic commands ###
|
|
@ -86,11 +86,11 @@ export const STAGE_UI: { [key in PROPOSAL_FILTERS]: StageUI } = {
|
|||
color: '#8e44ad',
|
||||
},
|
||||
ACCEPTED_WITH_FUNDING: {
|
||||
label: 'Funded by ZF',
|
||||
label: 'Funded',
|
||||
color: '#8e44ad',
|
||||
},
|
||||
ACCEPTED_WITHOUT_FUNDING: {
|
||||
label: 'Not Funded by ZF',
|
||||
label: 'Not Funded',
|
||||
color: '#8e44ad',
|
||||
},
|
||||
WIP: {
|
||||
|
|
|
@ -80,17 +80,17 @@ class HeaderDrawer extends React.Component<Props> {
|
|||
<Menu.Item key="/proposals">
|
||||
<Link to="/proposals">Browse proposals</Link>
|
||||
</Menu.Item>
|
||||
{/*<Menu.Item key="/create">*/}
|
||||
{/* <Link to="/create">Start a proposal</Link>*/}
|
||||
{/*</Menu.Item>*/}
|
||||
<Menu.Item key="/create">
|
||||
<Link to="/create">Start a proposal</Link>
|
||||
</Menu.Item>
|
||||
</Menu.ItemGroup>
|
||||
<Menu.ItemGroup title="Requests">
|
||||
<Menu.Item key="/requests">
|
||||
<Link to="/requests">Browse requests</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="/create-request">
|
||||
<Link to="/create-request">Create a Request</Link>
|
||||
</Menu.Item>
|
||||
{/*<Menu.Item key="/create-request">*/}
|
||||
{/* <Link to="/create-request">Create a Request</Link>*/}
|
||||
{/*</Menu.Item>*/}
|
||||
</Menu.ItemGroup>
|
||||
</Menu>
|
||||
</Drawer>
|
||||
|
|
|
@ -105,15 +105,6 @@ class Header extends React.Component<Props, State> {
|
|||
)}
|
||||
</Link>
|
||||
</div>
|
||||
{false && <div className="Header-links-button is-desktop">
|
||||
<Link to="/create-request">
|
||||
{Array.isArray(ccrDrafts) && ccrDrafts.length > 0 ? (
|
||||
<Button type={'primary'}>My Requests</Button>
|
||||
) : (
|
||||
<Button type={'primary'}>Create a Request</Button>
|
||||
)}
|
||||
</Link>
|
||||
</div>}
|
||||
|
||||
<HeaderAuth/>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { UserProposal, STATUS } from 'types';
|
||||
import { STATUS, UserProposal } from 'types';
|
||||
import './ProfileProposal.less';
|
||||
import UserRow from 'components/UserRow';
|
||||
import UnitDisplay from 'components/UnitDisplay';
|
||||
|
@ -23,7 +23,8 @@ export default class Profile extends React.Component<OwnProps> {
|
|||
isVersionTwo,
|
||||
acceptedWithFunding,
|
||||
status,
|
||||
changesRequestedDiscussionReason
|
||||
changesRequestedDiscussionReason,
|
||||
fundedByZomg,
|
||||
} = this.props.proposal;
|
||||
|
||||
// pulled from `variables.less`
|
||||
|
@ -31,18 +32,24 @@ export default class Profile extends React.Component<OwnProps> {
|
|||
const secondaryColor = '#2D2A26';
|
||||
|
||||
const isOpenForDiscussion = status === STATUS.DISCUSSION;
|
||||
const discussionColor = changesRequestedDiscussionReason ? 'red' : infoColor
|
||||
const discussionTag = changesRequestedDiscussionReason ? 'Changes Requested' : 'Open for Public Review'
|
||||
const discussionColor = changesRequestedDiscussionReason ? 'red' : infoColor;
|
||||
const discussionTag = changesRequestedDiscussionReason
|
||||
? 'Changes Requested'
|
||||
: 'Open for Public Review';
|
||||
|
||||
let tagColor = infoColor
|
||||
let tagMessage = 'Open for Contributions'
|
||||
let tagColor = infoColor;
|
||||
let tagMessage = 'Open for Contributions';
|
||||
|
||||
if (acceptedWithFunding) {
|
||||
tagColor = secondaryColor
|
||||
tagMessage = 'Funded by ZF'
|
||||
tagColor = secondaryColor;
|
||||
if (!fundedByZomg) {
|
||||
tagMessage = 'Funded by ZF';
|
||||
} else {
|
||||
tagMessage = 'Funded by ZOMG';
|
||||
}
|
||||
} else if (isOpenForDiscussion) {
|
||||
tagColor = discussionColor
|
||||
tagMessage = discussionTag
|
||||
tagColor = discussionColor;
|
||||
tagMessage = discussionTag;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import moment from 'moment';
|
||||
import { Icon, Popover, Tooltip, Alert } from 'antd';
|
||||
import { Alert, Icon, Popover, Tooltip } from 'antd';
|
||||
import { Proposal, STATUS } from 'types';
|
||||
import classnames from 'classnames';
|
||||
import { connect } from 'react-redux';
|
||||
|
@ -12,6 +12,8 @@ import Loader from 'components/Loader';
|
|||
import { PROPOSAL_STAGE } from 'api/constants';
|
||||
import { formatUsd } from 'utils/formatters';
|
||||
import ZFGrantsLogo from 'static/images/logo-name-light.svg';
|
||||
import ZomgLogo from 'static/images/zomg-logo.png';
|
||||
|
||||
import './style.less';
|
||||
|
||||
interface OwnProps {
|
||||
|
@ -134,7 +136,11 @@ export class ProposalCampaignBlock extends React.Component<Props, State> {
|
|||
isAcceptedWithFunding && (
|
||||
<div className="ProposalCampaignBlock-with-funding">
|
||||
Funded through
|
||||
<ZFGrantsLogo style={{ height: '1.5rem' }} />
|
||||
{proposal.fundedByZomg ? (
|
||||
<img src={ZomgLogo} alt={'Zomg logo'} style={{ height: '1.5rem' }} />
|
||||
) : (
|
||||
<ZFGrantsLogo style={{ height: '1.5rem' }} />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ export class ProposalCard extends React.Component<Proposal> {
|
|||
percentFunded,
|
||||
acceptedWithFunding,
|
||||
status,
|
||||
fundedByZomg,
|
||||
} = this.props;
|
||||
|
||||
// pulled from `variables.less`
|
||||
|
@ -46,7 +47,11 @@ export class ProposalCard extends React.Component<Proposal> {
|
|||
if (isVersionTwo && status === STATUS.LIVE) {
|
||||
if (acceptedWithFunding) {
|
||||
tagColor = secondaryColor;
|
||||
tagMessage = 'Funded by ZF';
|
||||
if (!fundedByZomg) {
|
||||
tagMessage = 'Funded by ZF';
|
||||
} else {
|
||||
tagMessage = 'Funded by ZOMG';
|
||||
}
|
||||
} else {
|
||||
tagColor = infoColor;
|
||||
tagMessage = 'Not Funded';
|
||||
|
|
|
@ -84,7 +84,7 @@ export function getCreateErrors(
|
|||
|
||||
// RFP opt-in
|
||||
if (!rfpOptIn) {
|
||||
errors.rfpOptIn = 'Please accept KYC to submit.'
|
||||
errors.rfpOptIn = 'Please accept KYC to submit.';
|
||||
}
|
||||
|
||||
// Title
|
||||
|
@ -260,6 +260,7 @@ export function makeProposalPreviewFromDraft(draft: ProposalDraft): ProposalDeta
|
|||
authedLiked: false,
|
||||
likesCount: 0,
|
||||
isVersionTwo: true,
|
||||
fundedByZomg: false,
|
||||
milestones: draft.milestones.map((m, idx) => ({
|
||||
id: idx,
|
||||
index: idx,
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 56 KiB |
|
@ -0,0 +1,64 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 1300.8 419.2" style="enable-background:new 0 0 1300.8 419.2;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{display:none;}
|
||||
.st1{display:inline;fill:#100400;}
|
||||
.st2{fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;}
|
||||
.st3{fill-rule:evenodd;clip-rule:evenodd;fill:#0F7000;}
|
||||
.st4{fill:#FFFFFF;}
|
||||
.st5{fill:#F8BB14;}
|
||||
.st6{fill:none;stroke:#FFFFFF;stroke-width:24.24;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;}
|
||||
.st7{font-family:'Roboto-Medium';}
|
||||
.st8{font-size:304.0703px;}
|
||||
</style>
|
||||
<g id="Layer_2" class="st0">
|
||||
<rect x="-102.5" y="-82" class="st1" width="1515.7" height="567.6"/>
|
||||
</g>
|
||||
<g id="Layer_1" xmlns:serif="http://www.serif.com/">
|
||||
<g>
|
||||
<g transform="matrix(0.347046,-4.1523e-17,-1.03807e-17,0.347046,307.53,12.3394)">
|
||||
<g id="Color">
|
||||
<g>
|
||||
<g id="Color1" transform="matrix(1.09544,1.31066e-16,3.27664e-17,1.09544,-953.204,-33.2755)" serif:id="Color">
|
||||
<g transform="matrix(1.19303,-3.26522e-48,2.90837e-48,1.06265,-101.088,-115.302)">
|
||||
<rect x="572.7" y="564.3" class="st2" width="37.4" height="265.4"/>
|
||||
</g>
|
||||
<g transform="matrix(1,-2.73691e-48,2.73691e-48,1,0,-21.0434)">
|
||||
<path class="st3" d="M970,472.4c-7.7-25.6-24.4,55.7-167.7,79.1c-112.6,18.3-168.6,129.2-148.5,161.9
|
||||
c13.1,21.4,111.2,52.7,194.7-8.3C931.7,644.3,977.7,498,970,472.4z M651.6,707c3,5.4,78.8-15.7,123.6-40.6
|
||||
c52.8-29.3,108.9-79.9,104.4-85.7c-2.9-3.8-63.8,23.9-123.7,57.7C704.1,667.6,646.4,697.7,651.6,707z"/>
|
||||
</g>
|
||||
<g transform="matrix(1,-2.73691e-48,2.73691e-48,1,0,-21.0434)">
|
||||
<path class="st3" d="M295.5,443.5C308,424.7,306.9,490,436.7,528c102,29.9,131.7,123.6,106.1,146.1
|
||||
c-16.7,14.7-115.4,25.3-181.2-33.5C295.9,581.9,283,462.2,295.5,443.5z M544.7,672c-2.8,3.9-52.5-14.7-91.1-39
|
||||
c-30-19-76.9-55.4-64.6-63.8c5.6-3.8,41.1,21,77.6,44.2C504.4,637.4,550.2,664.4,544.7,672z"/>
|
||||
</g>
|
||||
<g transform="matrix(0.927915,-1.11022e-16,-2.77556e-17,0.927915,854.005,46.6736)">
|
||||
<g transform="matrix(1,0,0,1,-581.95,-44.2319)">
|
||||
<path class="st4" d="M312.2,514.8c-131.2,0-238-106.8-238-238c0-131.2,106.8-238,238-238c131.2,0,238,106.8,238,238
|
||||
C550.2,408,443.4,514.8,312.2,514.8z M312.2-8.3C155-8.3,27.1,119.6,27.1,276.8S155,561.9,312.2,561.9
|
||||
S597.3,434,597.3,276.8S469.4-8.3,312.2-8.3z"/>
|
||||
</g>
|
||||
<path class="st5" d="M-269.8,7.8c-123.9,0-224.7,100.8-224.7,224.7c0,123.9,100.8,224.7,224.7,224.7S-45.1,356.4-45.1,232.5
|
||||
C-45.1,108.6-145.9,7.8-269.8,7.8z M-168.1,143.7l-22,27.9l-98.6,135.7h120.6v57.6h-77.8v47.6h-5.7v0.2h-36.3v-0.2h-5.7
|
||||
v-47.6h-77.8v-43.5l22-27.9l98.6-135.7h-120.6v-57.6h77.8V52.5h47.8v47.7h77.8V143.7z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="Hand" transform="matrix(1.23479,1.47739e-16,3.95e-17,1.32055,-1023.9,-346.117)">
|
||||
<path class="st6" d="M139.9,880c0,0,93-1.5,138.8-9.1c48.9-8.1,105.2-39.7,154.8-39.4c49.5,0.3,87.5,32.1,142.6,41.4
|
||||
c59,10,156.2,9.9,180.2,27.8c19.8,14.7,5.2,49.9-32.8,64c-26,9.7-73.4,19.4-110.9,19.1c-53.6-0.5-217.9-23.2-210.9-21.8
|
||||
c7,1.4,168.5,39.2,252.9,30.2c84.8-9,192.9-67.4,256.1-84.4c40-10.7,92.5-19.5,123.1-17.3c38.6,2.8,64,22.1,47.7,32.5
|
||||
c-57.7,36.8-263.6,156-393.9,188.1c-86.5,21.3-273,9.5-313,4.2c-60.9-8-98.3-24.4-139.4-53.2c-23.7-16.6-95.3-45.2-95.3-45.2
|
||||
V880z"/>
|
||||
</g>
|
||||
<g transform="matrix(121.473,1.45339e-14,3.31689e-15,110.889,-21599.6,-106761)">
|
||||
<text transform="matrix(2.598518e-02 0 0 2.598510e-02 181.6097 969.9001)" class="st4 st7 st8">zomg</text>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.7 KiB |
|
@ -170,6 +170,7 @@ export function generateProposal({
|
|||
liveDraftId: null,
|
||||
tipJarAddress: null,
|
||||
tipJarViewKey: null,
|
||||
fundedByZomg: false,
|
||||
arbiter: {
|
||||
status: PROPOSAL_ARBITER_STATUS.ACCEPTED,
|
||||
user: {
|
||||
|
|
|
@ -81,6 +81,7 @@ export interface Proposal extends Omit<ProposalDraft, 'target' | 'invites'> {
|
|||
liveDraftId: string | null;
|
||||
isTeamMember?: boolean; // FE derived
|
||||
isArbiter?: boolean; // FE derived
|
||||
fundedByZomg: boolean;
|
||||
}
|
||||
|
||||
export interface TeamInviteWithProposal extends TeamInvite {
|
||||
|
@ -124,6 +125,7 @@ export interface UserProposal {
|
|||
changesRequestedDiscussionReason: string | null;
|
||||
acceptedWithFunding: boolean | null;
|
||||
isVersionTwo: boolean;
|
||||
fundedByZomg: boolean;
|
||||
}
|
||||
|
||||
// NOTE: sync with backend/grant/proposal/models.py STATUSES
|
||||
|
|
Loading…
Reference in New Issue