Skip to content

Commit

Permalink
Show file tree
Hide file tree
Showing 26 changed files with 732 additions and 163 deletions.
7 changes: 6 additions & 1 deletion instances/treasury-devdao.near/widget/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,12 @@ function Page() {

return (
<Theme>
<AppLayout page={page} instance={instance} treasuryDaoID={treasuryDaoID}>
<AppLayout
page={page}
instance={instance}
treasuryDaoID={treasuryDaoID}
accountId={context.accountId}
>
<Page />
</AppLayout>
</Theme>
Expand Down
88 changes: 88 additions & 0 deletions instances/treasury-devdao.near/widget/components/BalanceBanner.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
const { getNearBalances } = VM.require(
"${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/lib.common"
);

const Container = styled.div`
.low-balance-warning {
background: rgba(255, 158, 0, 0.1);
color: var(--other-warning);
padding-inline: 0.8rem;
padding-block: 0.5rem;
font-weight: 500;
font-size: 13px;
h5 {
color: var(--other-warning) !important;
}
}
.insufficient-balance-warning {
background-color: rgba(217, 92, 74, 0.08);
color: var(--other-red);
padding-inline: 0.8rem;
padding-block: 0.5rem;
font-weight: 500;
font-size: 13px;
h5 {
color: var(--other-red) !important;
}
}
`;

function formatNearAmount(amount) {
return parseFloat(
Big(amount ?? "0")
.div(Big(10).pow(24))
.toFixed(2)
);
}

function BalanceBanner({ accountId, treasuryDaoID }) {
if (!treasuryDaoID || typeof getNearBalances !== "function" || !accountId) {
return <></>;
}

const nearBalances = getNearBalances(accountId);

const LOW_BALANCE_LIMIT = 0.7; // 0.7N
const INSUFFICIENT_BALANCE_LIMIT = 0.1; // 0.1N

const profile = Social.getr(`${accountId}/profile`);
const name = profile.name ?? accountId;

return (
<Container>
{!nearBalances ||
!nearBalances.availableParsed ||
nearBalances.availableParsed === "0.00" ||
parseFloat(nearBalances.availableParsed) > LOW_BALANCE_LIMIT ? (
<></>
) : parseFloat(nearBalances.availableParsed) <
INSUFFICIENT_BALANCE_LIMIT ? (
<div className="insufficient-balance-warning d-flex gap-3 p-3 rounded-3 m-3">
<i class="bi bi-exclamation-octagon error-icon h4 mb-0"></i>
<div>
<h5>Insufficient Funds</h5>
Hey {name}, you don't have enough NEAR to complete actions on your
treasury. You need at least {INSUFFICIENT_BALANCE_LIMIT}N. Please
add more funds to your account and try again
</div>
</div>
) : (
<div className="low-balance-warning d-flex gap-3 p-3 rounded-3 m-3">
<i class="bi bi-exclamation-triangle warning-icon h4 mb-0"></i>
<div>
<h5>Low Balance</h5>
Hey {name}, your NEAR balance is {nearBalances.availableParsed}N,
which is getting low. The minimum balance required is{" "}
{INSUFFICIENT_BALANCE_LIMIT}N. Please add more NEAR to your account
soon to avoid any issues completing actions on your treasury.
</div>
</div>
)}
</Container>
);
}

return { BalanceBanner };
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
const { getNearBalances } = VM.require(
"${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/lib.common"
);

const { Modal, ModalContent, ModalHeader, ModalFooter } = VM.require(
"${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/lib.modal"
);

function formatNearAmount(amount) {
return parseFloat(
Big(amount ?? "0")
.div(Big(10).pow(24))
.toFixed(2)
);
}

const { ActionButton, checkForDeposit, treasuryDaoID, callbackAction } = props;

const nearBalances = getNearBalances(context.accountId);
const profile = Social.getr(`${context.accountId}/profile`);
const name = profile.name ?? context.accountId;
const daoPolicy = useCache(
() => Near.asyncView(treasuryDaoID, "get_policy"),
"get_policy",
{ subscribe: false }
);

const [showModal, setShowModal] = useState(false);
const ADDITIONAL_AMOUNT = checkForDeposit
? formatNearAmount(daoPolicy?.proposal_bond)
: 0;

const INSUFFICIENT_BALANCE_LIMIT = ADDITIONAL_AMOUNT + 0.1; // 0.1N

function checkBalance() {
if (parseFloat(nearBalances?.availableParsed) < INSUFFICIENT_BALANCE_LIMIT) {
setShowModal(true);
} else {
callbackAction();
}
}

const WarningModal = () => (
<Modal sty>
<ModalHeader>
<div className="d-flex align-items-center justify-content-between mb-2">
<div className="d-flex gap-3">
<i class="bi bi-exclamation-octagon error-icon h4 mb-0"></i>
Insufficient Funds
</div>
<i
className="bi bi-x-lg h4 mb-0 cursor-pointer"
onClick={() => setShowModal(false)}
></i>
</div>
</ModalHeader>
<ModalContent>
Hey {name}, you don't have enough NEAR to complete actions on your
treasury. You need at least {INSUFFICIENT_BALANCE_LIMIT}N{" "}
{checkForDeposit &&
", which includes the proposal bond needed to create a proposal"}
. Please add more funds to your account and try again.
</ModalContent>
</Modal>
);

return (
<>
<div onClick={checkBalance}>
<ActionButton />
</div>
{showModal && <WarningModal />}
</>
);
85 changes: 56 additions & 29 deletions instances/treasury-devdao.near/widget/components/VoteActions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -229,58 +229,85 @@ return (
hasVotingPermission && (
<div className="d-flex gap-2 align-items-center">
<Widget
src={`${REPL_DEVHUB}/widget/devhub.components.molecule.Button`}
src={`${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/components.InsufficientBannerModal`}
props={{
classNames: {
root: "approve-btn p-2",
},
label: "Approve",
onClick: () => {
ActionButton: () => (
<Widget
src={`${REPL_DEVHUB}/widget/devhub.components.molecule.Button`}
props={{
classNames: {
root: "approve-btn p-2",
},
label: "Approve",
loading: isTxnCreated && vote === actions.APPROVE,
disabled: isTxnCreated,
}}
/>
),
checkForDeposit: false,
treasuryDaoID,
callbackAction: () => {
if (isInsufficientBalance) {
setShowWarning(true);
} else {
setVote(actions.APPROVE);
setConfirmModal(true);
}
},
loading: isTxnCreated && vote === actions.APPROVE,
disabled: isTxnCreated,
}}
/>
<Widget
src={`${REPL_DEVHUB}/widget/devhub.components.molecule.Button`}
src={`${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/components.InsufficientBannerModal`}
props={{
classNames: {
root: "reject-btn p-2",
},
label: "Reject",
onClick: () => {
ActionButton: () => (
<Widget
src={`${REPL_DEVHUB}/widget/devhub.components.molecule.Button`}
props={{
classNames: {
root: "reject-btn p-2",
},
label: "Reject",
loading: isTxnCreated && vote === actions.REJECT,
disabled: isTxnCreated,
}}
/>
),
checkForDeposit: false,
treasuryDaoID,
callbackAction: () => {
setVote(actions.REJECT);
setConfirmModal(true);
},
loading: isTxnCreated && vote === actions.REJECT,
disabled: isTxnCreated,
}}
/>
</div>
)
)}
{/* currently showing delete btn only for proposal creator */}
{hasDeletePermission && proposalCreator === accountId && (
<button
className="remove-btn p-2"
onClick={() => {
setVote(actions.REMOVE);
setConfirmModal(true);
<Widget
src={`${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/components.InsufficientBannerModal`}
props={{
ActionButton: () => (
<button
className="remove-btn p-2"
data-testid="delete-btn"
disabled={isTxnCreated}
>
<img
style={{ height: 30 }}
src="https://ipfs.near.social/ipfs/bafkreieobqzwouuadj7eneei7aadwfel6ubhj7qishnqwrlv5ldgcwuyt4"
/>
</button>
),
checkForDeposit: false,
treasuryDaoID,
callbackAction: () => {
setVote(actions.REMOVE);
setConfirmModal(true);
},
}}
data-testid="delete-btn"
disabled={isTxnCreated}
>
<img
style={{ height: 30 }}
src="https://ipfs.near.social/ipfs/bafkreieobqzwouuadj7eneei7aadwfel6ubhj7qishnqwrlv5ldgcwuyt4"
/>
</button>
/>
)}
</div>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
const { BalanceBanner } = VM.require(
`${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/components.BalanceBanner`
) || { BalanceBanner: () => <></> };

const AppHeader = ({ page, instance }) => (
<Widget
src="${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/components.Navbar"
Expand All @@ -8,7 +12,7 @@ const AppHeader = ({ page, instance }) => (
/>
);

function AppLayout({ page, instance, children, treasuryDaoID }) {
function AppLayout({ page, instance, children, treasuryDaoID, accountId }) {
const config = treasuryDaoID
? useCache(
() => Near.asyncView(treasuryDaoID, "get_config"),
Expand Down Expand Up @@ -379,6 +383,10 @@ function AppLayout({ page, instance, children, treasuryDaoID }) {
.warning-icon {
color: var(--other-warning) !important;
}
.error-icon {
color: var(--other-red) !important;
}
`;

return !config ? (
Expand All @@ -387,6 +395,7 @@ function AppLayout({ page, instance, children, treasuryDaoID }) {
<ParentContainer data-bs-theme={isDarkTheme ? "dark" : "light"}>
<Theme className="h-100 w-100">
<AppHeader page={page} instance={instance} />
<BalanceBanner accountId={accountId} treasuryDaoID={treasuryDaoID} />
<div className="px-3 py-2 w-100 h-100">{children}</div>
</Theme>
</ParentContainer>
Expand Down
6 changes: 2 additions & 4 deletions instances/treasury-devdao.near/widget/lib/common.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -426,10 +426,8 @@ function formatNearAmount(amount) {
.toFixed(2);
}

function getNearBalances(treasuryDaoID) {
const resp = fetch(
`https://api.fastnear.com/v1/account/${treasuryDaoID}/full`
);
function getNearBalances(accountId) {
const resp = fetch(`https://api.fastnear.com/v1/account/${accountId}/full`);
const storage = Big(resp?.body?.state?.storage_bytes ?? "0")
.mul(Big(10).pow(19))
.toFixed();
Expand Down
2 changes: 1 addition & 1 deletion instances/treasury-devdao.near/widget/lib/modal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ const NoButton = styled.button`

const ModalHeader = ({ children }) => (
<ModalHeaderDiv>
<h5 className="mb-0">{children}</h5>
<h5 className="mb-0 w-100">{children}</h5>
</ModalHeaderDiv>
);

Expand Down
19 changes: 13 additions & 6 deletions instances/treasury-devdao.near/widget/pages/payments/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,19 @@ const SidebarMenu = ({ currentTab }) => {
style={{ paddingBottom: "7px" }}
>
{hasCreatePermission && (
<button
className="primary py-1 px-3 rounded-2 h6 fw-bold d-flex align-items-center gap-2 mb-0"
onClick={() => setShowCreateRequest(true)}
>
<i class="bi bi-plus-lg h5 mb-0"></i>Create Request
</button>
<Widget
src={`${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/components.InsufficientBannerModal`}
props={{
ActionButton: () => (
<button className="primary py-1 px-3 rounded-2 h6 fw-bold d-flex align-items-center gap-2 mb-0">
<i class="bi bi-plus-lg h5 mb-0"></i>Create Request
</button>
),
checkForDeposit: true,
treasuryDaoID,
callbackAction: () => setShowCreateRequest(true),
}}
/>
)}
<Widget
src={`${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/components.SettingsDropdown`}
Expand Down
Loading

0 comments on commit 2062b7e

Please sign in to comment.