diff --git a/src/common/api/operations.ts b/src/common/api/operations.ts index 69c3266a4f8..ecbb180c075 100644 --- a/src/common/api/operations.ts +++ b/src/common/api/operations.ts @@ -1090,6 +1090,33 @@ export const proposalVoteKc = (account: string, proposal: number, approve: boole return keychain.broadcast(account, [op], "Active"); }; +export const createProposal = ( + account: string, + receiver: string, + start: any, + end: any, + dailyPay: any, + subject: string, + link: string + ) => { + const op: Operation = [ + "create_proposal", + { + creator: account, + receiver: receiver, + start_date: start, + end_date: end, + daily_pay: `${Number(dailyPay).toFixed(3)} HBD`, + subject: subject, + permlink: link + } + ] + keychain.broadcast(account, [op], "Active") + .then(result => console.log(result)) + .catch(err => console.log(err)); + // return broadcastPostingJSON(account, 'amount', op) +} + export const subscribe = ( username: string, community: string diff --git a/src/common/app.tsx b/src/common/app.tsx index fb47877eef5..4f9640b33f7 100644 --- a/src/common/app.tsx +++ b/src/common/app.tsx @@ -23,6 +23,7 @@ import { pageMapDispatchToProps, pageMapStateToProps } from "./pages/common"; import { connect } from "react-redux"; import loadable from "@loadable/component"; import Announcement from "./components/announcement"; +import { ProposalCreationContainer } from "./components/create-proposal"; // Define lazy pages const ProfileContainer = loadable(() => import("./pages/profile-functional")); @@ -38,7 +39,7 @@ const WitnessesContainer = loadable(() => import("./pages/witnesses")); const WitnessesPage = (props: any) => ; const AuthContainer = loadable(() => import("./pages/auth")); -const AuthPage = (props: any) => ; +const AuthPage = (props: any) => ; const SubmitContainer = loadable(() => import("./pages/submit")); const SubmitPage = (props: any) => ; @@ -132,6 +133,12 @@ const App = (props: any) => { path={routes.PROPOSAL_DETAIL} component={ProposalDetailContainer} /> + diff --git a/src/common/components/create-proposal/index.scss b/src/common/components/create-proposal/index.scss new file mode 100644 index 00000000000..65de9d5c661 --- /dev/null +++ b/src/common/components/create-proposal/index.scss @@ -0,0 +1,14 @@ +.create-proposal{ + display: flex; + flex-direction: column; + padding: 10px; + width: 75%; + align-self: center; + + .date-picker{ + width: 50%; + display: inline-block; + padding: 5px; + } +} + diff --git a/src/common/components/create-proposal/index.tsx b/src/common/components/create-proposal/index.tsx new file mode 100644 index 00000000000..4347ed35700 --- /dev/null +++ b/src/common/components/create-proposal/index.tsx @@ -0,0 +1,261 @@ +import React, { Fragment, useCallback, useEffect, useState } from "react"; +import { connect } from "react-redux"; +import Theme from "../theme"; +import NavBar from "../navbar"; +import LinearProgress from "../linear-progress"; +import { _t } from "../../i18n"; +import { pageMapDispatchToProps, pageMapStateToProps } from "../../pages/common"; +import { Button, Form } from "react-bootstrap"; +import { createProposal } from "../../api/operations"; +import { getAccount } from "../../api/hive"; +import _ from "lodash"; + +const ProposalCreationPage = (props: any) => { + const { activeUser } = props; + + const [loading, setLoading] = useState(true); + const [formInput, steFormInput] = useState({}); + const [error, setError] = useState({}); + const [hbdBalanceError, setHbdBalanceError] = useState(false); + const [total, setTotal] = useState(0); + const [receieverData, setReceiverData] = useState(""); + const [receiverError, setReceiverError] = useState("") + + useEffect(() => { + setLoading(false) + }, []) + + const handleFormError = () => { + const { subject, start, end, funding, link, total } = formInput; + const newError: any = {}; + + if(!subject || subject === "") newError.subject = "Please enter subject" + if(!start || start === "") newError.start = "Please select start date" + if(!end || end === "") newError.end = "Please select end date" + if(!funding || funding === "") newError.funding = "Please enter funding amount" + if(!link) newError.link = "Please enter post link" + if(Number(total) <= 0) newError.total = "Invalid amount" + + return newError; + } + + const handleChange = (field: any, value: any) => { + steFormInput({ + ...formInput, + [field]:value + }); + + if(!!error){ + setError({ + ...error, + [field]:null + }) + }; + }; + + const getDateDifference = (start: any, end: any) => { + const startDate = new Date(start); + const endDate = new Date(end); + const difference: any = endDate.getTime() - startDate.getTime(); + const daysDifference: number = difference / (1000 * 3600 * 24); + return daysDifference + } + + const onSubmit = (e: any) => { + const hbdBalance = Number(props.activeUser.data.hbd_balance.replace("HBD","")) + e.preventDefault(); + const formErrors = handleFormError(); + if(Object.keys(formErrors).length > 0){ + setError(formErrors) + } else if ( hbdBalance < 10 ){ + setHbdBalanceError(true) + } else { + createProposal( + activeUser.username, + formInput.receiver || activeUser.username, + `${formInput.start}T00:00:00`, + `${formInput.end}T00:00:00`, + formInput.funding, + formInput.subject, + formInput.link.split("/")[5] + ) + return; + } + }; + + const validateccount = async (account: string) => { + setLoading(true) + return getAccount(account) + .then((resp) => { + if (resp) { + console.log(resp) + setReceiverError("") + setReceiverData(resp) + } else { + console.log("user not found") + setReceiverError("user not found") + } + return resp; + }) + .catch((err) => { + console.log(err) + }) + .finally(() => { + setLoading(false) + }); + } + + const delayedVerify = useCallback(_.debounce(validateccount, 3000, { leading: true }), []); + + return ( + <> + {loading && } +
+ + { NavBar({ ...props })} +
+
+
+

{_t("create-proposal.title")}

+ + {hbdBalanceError &&

+ {_t("create-proposal.hbd-balance-error")} +

} + +
+ + {_t("create-proposal.creator-username")} + handleChange("username", e.target.value)} + /> + + {error.username} + + + + {_t("create-proposal.description")} + handleChange("subject", e.target.value)} + /> + + {error.subject} + + + + {_t("create-proposal.link")} + handleChange("link", e.target.value)} + /> + + {error.link} + + + + {_t("create-proposal.daily-funding")} + { + handleChange("funding", e.target.value); + setTotal(getDateDifference(formInput.start, formInput.end) * Number(e.target.value)) + }} + /> + + {error.funding} + + + + {_t("create-proposal.total")} + handleChange("total", Number(total))} + readOnly + /> + + {error.total} + + + + {_t("create-proposal.start-date")} + { + handleChange("start", e.target.value); + setTotal(getDateDifference(e.target.value, formInput.end) * Number(formInput.funding)) + }} + /> + + {error.start} + + + + {_t("create-proposal.end-date")} + { + handleChange("end", e.target.value) + setTotal(getDateDifference(formInput.start, e.target.value) * Number(formInput.funding)) + }} + /> + + {error.end} + + + + {_t("create-proposal.receiver")} + { + handleChange("receiver", e.target.value); + delayedVerify(e.target.value) + }} + /> + + {receiverError} + + + + + +
+
+
+ + ); +} + +export const ProposalCreationContainer = connect( + pageMapStateToProps, + pageMapDispatchToProps +)(ProposalCreationPage); diff --git a/src/common/i18n/locales/en-US.json b/src/common/i18n/locales/en-US.json index 9301d8d5857..abb2ad4a24b 100644 --- a/src/common/i18n/locales/en-US.json +++ b/src/common/i18n/locales/en-US.json @@ -1334,6 +1334,23 @@ "daily-budget": "daily budget", "total-budget": "total budget" }, + "create-proposal": { + "title": "Create Proposal", + "hbd-balance-error": "You must have at least 10HBD to submit a proposal", + "creator-username": "Creator", + "description": "Proposal Description", + "subject": "Write a brief subject", + "link": "Post Link", + "link-placeholder": "Enter post link", + "daily-funding": "Daily Funding (HBD)", + "daily-funding-placeholder": "Daily amount", + "total": "Total", + "start-date": "Select Start Date", + "end-date": "Select End Date", + "receiver": "Receiver", + "submit": "Submit Proposal", + "receiver-placeholder": "eg @demo" + }, "gallery": { "title": "Gallery", "copied": "Copied to clipboard" diff --git a/src/common/pages/proposals.tsx b/src/common/pages/proposals.tsx index 5c12bbc3559..db2e951774b 100644 --- a/src/common/pages/proposals.tsx +++ b/src/common/pages/proposals.tsx @@ -43,6 +43,8 @@ import parseDate from "../helper/parse-date"; import { closeSvg } from "../img/svg"; import moment from "moment"; +import { Button } from "react-bootstrap"; +// import { CreateProposal } from "../components/create-proposal"; enum Filter { ALL = "all", @@ -306,7 +308,14 @@ class ProposalsPage extends BaseComponent { ); })} - + +
+ + + +
+ + {(() => { if (inProgress) { diff --git a/src/common/routes.ts b/src/common/routes.ts index 09da21a6d4f..54129f1632c 100644 --- a/src/common/routes.ts +++ b/src/common/routes.ts @@ -37,5 +37,6 @@ export default { WITNESSES: `/witnesses`, PROPOSALS: `/proposals`, PROPOSAL_DETAIL: `/proposals/:id(\\d+)`, + PROPOSAL_CREATE: `/:section(proposals)/:create`, PURCHASE: "/purchase" }; diff --git a/src/style/_components.scss b/src/style/_components.scss index 62d2989a1af..414e78da1b8 100644 --- a/src/style/_components.scss +++ b/src/style/_components.scss @@ -55,6 +55,7 @@ @import "../common/components/login"; @import "../common/components/comment"; @import "../common/components/comment-engagement"; +@import "../common/components/create-proposal"; @import "../common/components/transfer"; @import "../common/components/or-divider"; @import "../common/components/notifications";