diff --git a/api/public/index.css b/api/public/index.css index 59ae4e31..5dd26b2e 100644 --- a/api/public/index.css +++ b/api/public/index.css @@ -639,3 +639,12 @@ nav i.material-icons { font-size: 24px; padding-top: 2px; } + +.mdc-data-table__cell { + color: #fff; +} + +.mdc-data-table__header-cell { + color: #fff; + font-weight: 600; +} diff --git a/api/src/frontend/details.tsx b/api/src/frontend/details.tsx index ba06c000..3bdc05e6 100644 --- a/api/src/frontend/details.tsx +++ b/api/src/frontend/details.tsx @@ -2,320 +2,482 @@ import * as React from "react"; import * as ReactDOMClient from "react-dom/client"; import { Tab, TabBar } from "@rmwc/tabs"; import { List, SimpleListItem } from "@rmwc/list"; +import { + DataTable, + DataTableBody, + DataTableContent, + DataTableHead, + DataTableHeadCell, + DataTableRow, + DataTableCell, +} from "@rmwc/data-table"; import * as utils from "./utils"; import * as polkadot from "./utils/polkadot"; -import { Currency, RoundType, Project, ProjectOnChain, User, ProjectState } from "./models"; -import "../../public/proposal.css" -import '@rmwc/list/styles'; +import { + Currency, + RoundType, + Project, + ProjectOnChain, + User, + ProjectState, +} from "./models"; +import "../../public/proposal.css"; +import "@rmwc/list/styles"; import { marked } from "marked"; -import { Contribute } from './components/contribute'; -import { Milestones } from './components/milestones'; -import { FundingInfo } from './components/fundingInfo'; -import { SubmitMilestone } from './components/submitMilestone'; -import { VoteOnMilestone } from './components/voteOnMilestone'; -import { ApproveMilestone } from './components/approveMilestone'; +import { Contribute } from "./components/contribute"; +import { Milestones } from "./components/milestones"; +import { FundingInfo } from "./components/fundingInfo"; +import { SubmitMilestone } from "./components/submitMilestone"; +import { VoteOnMilestone } from "./components/voteOnMilestone"; +import { ApproveMilestone } from "./components/approveMilestone"; import ChainService from "./services/chainService"; import { Withdraw } from "./components/withdraw"; type DetailsProps = { - imbueApi: polkadot.ImbueApiInfo, - projectId: string | number | null, - user: User, - chainService: ChainService -} + imbueApi: polkadot.ImbueApiInfo; + projectId: string | number | null; + user: User; + chainService: ChainService; +}; type DetailsState = { - activeTabIndex: number, - projectOnChain: ProjectOnChain, - lastApprovedMilestoneIndex: number, - firstPendingMilestoneIndex: number, - userIsInitiator: boolean, - showContributeComponent: boolean, - showSubmitMilestoneComponent: boolean, - showVoteComponent: boolean, - showApproveMilestoneComponent: boolean, - showWithdrawComponent: boolean -} + activeTabIndex: number; + projectOnChain: ProjectOnChain; + lastApprovedMilestoneIndex: number; + firstPendingMilestoneIndex: number; + userIsInitiator: boolean; + showContributeComponent: boolean; + showSubmitMilestoneComponent: boolean; + showVoteComponent: boolean; + showApproveMilestoneComponent: boolean; + showWithdrawComponent: boolean; + sortDir: number | null; +}; const initiatorHeadingMapping: Record = { - /* PROJECT HAS BEEN CREATED SUCCESSFULLY AND pending ADDITION TO FUNDING ROUND */ - [ProjectState.PendingProjectApproval]:

Funding for this project is not yet open. Check back soon for more updates!

, - - /* PROJECT IS PENDING FUNDING APPROVAL ROUND */ - [ProjectState.PendingFundingApproval]:

Pending funding approval. Funding round not complete or approved. Please contact the team for review.

, - - /* PROJECT IS IN THE CONTRIBUTION ROUND */ - [ProjectState.OpenForContribution]:

Your proposal has been created successfully and added to the funding round. Contributors can now fund your project

, - - /* PROJECT IS IN THE VOTING ROUND */ - - [ProjectState.OpenForVoting]: <>, - - [ProjectState.PendingMilestoneApproval]:

Pending milestone approval. Milestone voting round not complete or approved. Please contact the team for review.

, - - /* PROJECT IS pending MILESTONE SUBMISSION */ - [ProjectState.PendingMilestoneSubmission]:

Pending milestone submission

, - - [ProjectState.OpenForWithdraw]: <> - - -} + /* PROJECT HAS BEEN CREATED SUCCESSFULLY AND pending ADDITION TO FUNDING ROUND */ + [ProjectState.PendingProjectApproval]: ( +

+ Funding for this project is not yet open. Check back soon for more + updates! +

+ ), + + /* PROJECT IS PENDING FUNDING APPROVAL ROUND */ + [ProjectState.PendingFundingApproval]: ( +

+ Pending funding approval. Funding round not complete or approved. Please{" "} + contact the team for review. +

+ ), + + /* PROJECT IS IN THE CONTRIBUTION ROUND */ + [ProjectState.OpenForContribution]: ( +

+ Your proposal has been created successfully and added to the funding + round. Contributors can now fund your project +

+ ), + + /* PROJECT IS IN THE VOTING ROUND */ + + [ProjectState.OpenForVoting]: <>, + + [ProjectState.PendingMilestoneApproval]: ( +

+ Pending milestone approval. Milestone voting round not complete or + approved. Please{" "} + contact the team for review. +

+ ), + + /* PROJECT IS pending MILESTONE SUBMISSION */ + [ProjectState.PendingMilestoneSubmission]: ( +

Pending milestone submission

+ ), + + [ProjectState.OpenForWithdraw]: <>, +}; const contributorHeadingMapping: Record = { - /* PROJECT HAS BEEN CREATED SUCCESSFULLY AND pending ADDITION TO FUNDING ROUND */ - [ProjectState.PendingProjectApproval]:

Funding for this project is not yet open. Check back soon for more updates!

, - - /* PROJECT IS PENDING FUNDING APPROVAL ROUND */ - [ProjectState.PendingFundingApproval]:

Pending funding approval. Funding round not complete or approved. Please contact the team for review.

, - - /* PROJECT IS IN THE CONTRIBUTION ROUND */ - [ProjectState.OpenForContribution]:

This project is open for contributions!

, - - [ProjectState.PendingMilestoneSubmission]: <>, - - /* PROJECT IS IN THE VOTING ROUND */ - [ProjectState.OpenForVoting]:

Project contributors can now vote on milestones

, - - /* PROJECT IS IN THE VOTING ROUND */ - [ProjectState.PendingMilestoneApproval]: <>, - - /* PROJECT IS pending MILESTONE SUBMISSION */ - [ProjectState.PendingMilestoneSubmission]:

Pending milestone submission

, - - [ProjectState.OpenForWithdraw]: <> -} + /* PROJECT HAS BEEN CREATED SUCCESSFULLY AND pending ADDITION TO FUNDING ROUND */ + [ProjectState.PendingProjectApproval]: ( +

+ Funding for this project is not yet open. Check back soon for more + updates! +

+ ), + + /* PROJECT IS PENDING FUNDING APPROVAL ROUND */ + [ProjectState.PendingFundingApproval]: ( +

+ Pending funding approval. Funding round not complete or approved. Please{" "} + contact the team for review. +

+ ), + + /* PROJECT IS IN THE CONTRIBUTION ROUND */ + [ProjectState.OpenForContribution]: ( +

This project is open for contributions!

+ ), + + [ProjectState.PendingMilestoneSubmission]: <>, + + /* PROJECT IS IN THE VOTING ROUND */ + [ProjectState.OpenForVoting]: ( +

Project contributors can now vote on milestones

+ ), + + /* PROJECT IS IN THE VOTING ROUND */ + [ProjectState.PendingMilestoneApproval]: <>, + + /* PROJECT IS pending MILESTONE SUBMISSION */ + [ProjectState.PendingMilestoneSubmission]: ( +

Pending milestone submission

+ ), + + [ProjectState.OpenForWithdraw]: <>, +}; class Details extends React.Component { - state: DetailsState = { - activeTabIndex: 0, - projectOnChain: {} as ProjectOnChain, - lastApprovedMilestoneIndex: -1, - firstPendingMilestoneIndex: -1, - userIsInitiator: false, - showContributeComponent: false, - showSubmitMilestoneComponent: false, - showVoteComponent: false, - showApproveMilestoneComponent: false, - showWithdrawComponent: false + state: DetailsState = { + activeTabIndex: 0, + projectOnChain: {} as ProjectOnChain, + lastApprovedMilestoneIndex: -1, + firstPendingMilestoneIndex: -1, + userIsInitiator: false, + showContributeComponent: false, + showSubmitMilestoneComponent: false, + showVoteComponent: false, + showApproveMilestoneComponent: false, + showWithdrawComponent: false, + sortDir: null, + }; + + constructor(props: DetailsProps) { + super(props); + } + + setActiveTab(tabIndex: number) { + this.setState({ activeTabIndex: tabIndex }); + } + + async setProjectState(projectOnChain: ProjectOnChain): Promise { + let userIsInitiator = await this.props.chainService.isUserInitiator( + this.props.user, + projectOnChain + ); + + let showContributeComponent = false; + let showSubmitMilestoneComponent = false; + let showVoteComponent = false; + let showApproveMilestoneComponent = false; + let showWithdrawComponent = false; + + if (!projectOnChain.milestones) { + return; } - constructor(props: DetailsProps) { - super(props); + let lastApprovedMilestoneIndex = + this.props.chainService.findLastApprovedMilestone(projectOnChain); + let firstPendingMilestoneIndex = + this.props.chainService.findFirstPendingMilestone(projectOnChain); + + const projectState = projectOnChain.projectState; + + if (userIsInitiator) { + // SHOW WITHDRAW AND MILESTONE SUBMISSION components + showSubmitMilestoneComponent = + projectOnChain.fundingThresholdMet && firstPendingMilestoneIndex >= 0; + showWithdrawComponent = + lastApprovedMilestoneIndex >= 0 && + projectOnChain.raisedFunds > projectOnChain.withdrawnFunds; + showApproveMilestoneComponent = + projectOnChain.fundingThresholdMet && firstPendingMilestoneIndex >= 0; + } else { + switch (projectState) { + case ProjectState.OpenForContribution: { + showContributeComponent = true; + break; + } + case ProjectState.OpenForVoting: { + showVoteComponent = true; + break; + } + } } - setActiveTab(tabIndex: number) { - this.setState({ activeTabIndex: tabIndex }); + // USE THIS FOR DEMO + // projectOnChain.milestones[0].isApproved = true; + // projectOnChain.milestones[1].isApproved = true; + // lastApprovedMilestoneIndex = 1; + // lastPendingMilestoneIndex = 2; + // projectOnChain.projectState = ProjectState.OpenForVoting; + + this.setState({ + projectOnChain: projectOnChain, + lastApprovedMilestoneIndex: lastApprovedMilestoneIndex, + firstPendingMilestoneIndex: firstPendingMilestoneIndex, + userIsInitiator: userIsInitiator, + showContributeComponent: showContributeComponent, + showSubmitMilestoneComponent: showSubmitMilestoneComponent, + showVoteComponent: showVoteComponent, + showApproveMilestoneComponent: showApproveMilestoneComponent, + showWithdrawComponent: showWithdrawComponent, + sortDir: null, + }); + } + + async componentDidMount() { + if (this.props.projectId) { + const project: ProjectOnChain = await this.props.chainService.getProject( + this.props.projectId + ); + if (!project) { + utils.redirect("/not-found"); + location.reload(); + return; + } + this.setProjectState(project); } + } - async setProjectState(projectOnChain: ProjectOnChain): Promise { - let userIsInitiator = await this.props.chainService.isUserInitiator(this.props.user, projectOnChain); - - let showContributeComponent = false - let showSubmitMilestoneComponent = false; - let showVoteComponent = false; - let showApproveMilestoneComponent = false; - let showWithdrawComponent = false; - - if (!projectOnChain.milestones) { - return; - } - - let lastApprovedMilestoneIndex = this.props.chainService.findLastApprovedMilestone(projectOnChain); - let firstPendingMilestoneIndex = this.props.chainService.findFirstPendingMilestone(projectOnChain); - - const projectState = projectOnChain.projectState - - if (userIsInitiator) { - // SHOW WITHDRAW AND MILESTONE SUBMISSION components - showSubmitMilestoneComponent = projectOnChain.fundingThresholdMet && firstPendingMilestoneIndex >= 0; - showWithdrawComponent = lastApprovedMilestoneIndex >= 0 && projectOnChain.raisedFunds > projectOnChain.withdrawnFunds; - showApproveMilestoneComponent = projectOnChain.fundingThresholdMet && firstPendingMilestoneIndex >= 0; - } else { - switch (projectState) { - case ProjectState.OpenForContribution: { - showContributeComponent = true; - break + showProjectStateHeading(): JSX.Element { + if (this.state.userIsInitiator) { + return initiatorHeadingMapping[this.state.projectOnChain.projectState]; + } else { + return contributorHeadingMapping[this.state.projectOnChain.projectState]; + } + } + + render() { + if (this.state.projectOnChain.milestones) { + return ( +
+
+
+

+ {this.state.projectOnChain.name} +

+
+ +
+ + {this.showProjectStateHeading()} + +
+ {this.state.showContributeComponent ? ( + + ) : null} + {this.state.showSubmitMilestoneComponent ? ( + + ) : null} + {this.state.showVoteComponent ? ( + + ) : null} + {this.state.showApproveMilestoneComponent ? ( + + ) : null} + + {this.state.showWithdrawComponent ? ( + + ) : null} +
+ this.setActiveTab(evt.detail.index)} + > + + + + + + + + +
+
+

{this.state.projectOnChain.name}

+
+
    +
  • + +
  • +
  • + + + ${this.state.projectOnChain.currencyId as Currency} + {" "} + + {this.state.projectOnChain.requiredFundsFormatted.toLocaleString()} + + .00{" "} + required +
  • +
  • + + + {this.state.projectOnChain.website} + + +
  • +
+
- // USE THIS FOR DEMO - // projectOnChain.milestones[0].isApproved = true; - // projectOnChain.milestones[1].isApproved = true; - // lastApprovedMilestoneIndex = 1; - // lastPendingMilestoneIndex = 2; - // projectOnChain.projectState = ProjectState.OpenForVoting; - - - this.setState({ - projectOnChain: projectOnChain, - lastApprovedMilestoneIndex: lastApprovedMilestoneIndex, - firstPendingMilestoneIndex: firstPendingMilestoneIndex, - userIsInitiator: userIsInitiator, - showContributeComponent: showContributeComponent, - showSubmitMilestoneComponent: showSubmitMilestoneComponent, - showVoteComponent: showVoteComponent, - showApproveMilestoneComponent: showApproveMilestoneComponent, - showWithdrawComponent: showWithdrawComponent, - }) - } +
+ +
- async componentDidMount() { +
+ +

Coming soon.....

+
+
- if (this.props.projectId) { - const project: ProjectOnChain = await this.props.chainService.getProject(this.props.projectId); - if (!project) { - utils.redirect("/not-found"); - location.reload(); - return; - } - this.setProjectState(project); - } - } +
+ +

Coming soon.....

+
+
- showProjectStateHeading(): JSX.Element { - if (this.state.userIsInitiator) { - return initiatorHeadingMapping[this.state.projectOnChain.projectState]; - } else { - return contributorHeadingMapping[this.state.projectOnChain.projectState]; - } - } +
+ +

Coming soon.....

+
+
- render() { - if (this.state.projectOnChain.milestones) { - return
-
-

{this.state.projectOnChain.name}

- -
- - {this.showProjectStateHeading()} - -
- {this.state.showContributeComponent ? - - : null - } - {this.state.showSubmitMilestoneComponent ? - - : null - } - {this.state.showVoteComponent ? - - : null - } - {this.state.showApproveMilestoneComponent ? - - : null - } - - {this.state.showWithdrawComponent ? - - : null - } -
- this.setActiveTab(evt.detail.index)}> - - - - - - - -
-
-

{this.state.projectOnChain.name}

-
-
-
    -
  • -
  • - - ${this.state.projectOnChain.currencyId as Currency}{' '} - {this.state.projectOnChain.requiredFundsFormatted.toLocaleString()} - .00{' '} - required -
  • -
  • - - {this.state.projectOnChain.website} - -
  • -
-
- - -
- -
- -
- -

Coming soon.....

-
-
- -
- -

Coming soon.....

-
-
- -
- -

Coming soon.....

-
-
-
+
+ + + + + + Address + + { + // this.setState({ sortDir }); + // }} + > + Amount + + + + + {this.state.projectOnChain.contributions.map( + ({ accountId, value }, index) => ( + + {accountId} + + {(Number(value) / 1e12).toFixed(4)} + + + ) + )} + + +
- } +
+
+ ); } + } } document.addEventListener("DOMContentLoaded", async (event) => { - const imbueApi = await polkadot.initImbueAPIInfo(); - const projectId = await utils.getProjectId(); - const user = await utils.getCurrentUser(); - const chainService = new ChainService(imbueApi, user); - ReactDOMClient.createRoot(document.getElementById('project-body')!) - .render(
); -}); \ No newline at end of file + const imbueApi = await polkadot.initImbueAPIInfo(); + const projectId = await utils.getProjectId(); + const user = await utils.getCurrentUser(); + const chainService = new ChainService(imbueApi, user); + ReactDOMClient.createRoot(document.getElementById("project-body")!).render( +
+ ); +});