Tokens available:{" "}
{option.value === "NEAR"
? getNearAvailableBalance(option.tokenBalance)
@@ -226,7 +223,7 @@ return (
>
+
+
+
+ {showInProgress ? (
+
+
+
+ Processing your request ...
+
+
+ ) : (
+
+
+
+ Something went wrong. Please try resubmitting the request.
+
+
+ )}
+
+
+
+ ) : null;
+}
+return { TransactionLoader };
diff --git a/instances/treasury-devdao.near/widget/components/ValidatorsDropDownWithSearch.jsx b/instances/treasury-devdao.near/widget/components/ValidatorsDropDownWithSearch.jsx
index 8a3b5e3e..5b08120c 100644
--- a/instances/treasury-devdao.near/widget/components/ValidatorsDropDownWithSearch.jsx
+++ b/instances/treasury-devdao.near/widget/components/ValidatorsDropDownWithSearch.jsx
@@ -61,12 +61,6 @@ const Container = styled.div`
width: 100%;
}
- .dropdown-item.active,
- .dropdown-item:active {
- background-color: #f0f0f0 !important;
- color: black;
- }
-
.custom-select {
position: relative;
}
@@ -77,15 +71,7 @@ const Container = styled.div`
}
.selected {
- background-color: #f0f0f0;
- }
-
- input {
- background-color: #f8f9fa;
- }
-
- .cursor-pointer {
- cursor: pointer;
+ background-color: var(--grey-04);
}
.text-wrap {
@@ -93,18 +79,10 @@ const Container = styled.div`
white-space: normal;
}
- .text-muted {
- color: rgba(142, 142, 147, 1) !important;
- }
-
.text-orange {
color: rgba(255, 149, 0, 1) !important;
}
- .text-dark-grey {
- color: rgba(85, 85, 85, 1) !important;
- }
-
.disabled {
background-color: rgba(244, 244, 244, 1) !important;
color: #999999 !important;
@@ -132,7 +110,7 @@ const BalanceDisplay = ({ balance, label }) => {
return (
-
{label}
+
{label}
{formatNearAmount(balance)} NEAR
);
@@ -161,7 +139,7 @@ return (
>
{selectedOption.pool_id ?? defaultLabel}
@@ -211,7 +189,7 @@ return (
}}
>
- {fee}% Fee
+ {fee}% Fee
Active
{pool_id}
diff --git a/instances/treasury-devdao.near/widget/components/VoteActions.jsx b/instances/treasury-devdao.near/widget/components/VoteActions.jsx
index d10e460f..29afd4ca 100644
--- a/instances/treasury-devdao.near/widget/components/VoteActions.jsx
+++ b/instances/treasury-devdao.near/widget/components/VoteActions.jsx
@@ -4,6 +4,9 @@ if (!instance) {
}
const { treasuryDaoID } = VM.require(`${instance}/widget/config.data`);
+const { TransactionLoader } = VM.require(
+ `${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/components.TransactionLoader`
+) || { TransactionLoader: () => <>> };
const votes = props.votes ?? {};
const proposalId = props.proposalId;
@@ -37,6 +40,7 @@ const [isInsufficientBalance, setInsufficientBal] = useState(false);
const [showWarning, setShowWarning] = useState(false);
const [isReadyToBeWithdrawn, setIsReadyToBeWithdrawn] = useState(true);
const [showConfirmModal, setConfirmModal] = useState(null);
+const [showErrorToast, setShowErrorToast] = useState(false);
useEffect(() => {
if (!avoidCheckForBalance) {
@@ -74,41 +78,47 @@ function getProposalData() {
(result) => result
);
}
-
-function getProposalStatus(votes) {
- const votesArray = Object.values(votes);
- return {
- isApproved:
- votesArray.filter((i) => i === "Approve").length >= requiredVotes,
- isRejected:
- votesArray.filter((i) => i === "Reject").length >= requiredVotes,
- };
-}
-
useEffect(() => {
if (isTxnCreated) {
+ let checkTxnTimeout = null;
+ let errorTimeout = null;
+
const checkForVoteOnProposal = () => {
getProposalData().then((proposal) => {
if (JSON.stringify(proposal.votes) !== JSON.stringify(votes)) {
checkProposalStatus();
+ clearTimeout(errorTimeout);
setTxnCreated(false);
} else {
- setTimeout(() => checkForVoteOnProposal(), 1000);
+ checkTxnTimeout = setTimeout(checkForVoteOnProposal, 1000);
}
});
};
+
checkForVoteOnProposal();
+
+ // if in 20 seconds there is no change, show error condition
+ errorTimeout = setTimeout(() => {
+ setShowErrorToast(true);
+ setTxnCreated(false);
+ clearTimeout(checkTxnTimeout);
+ }, 20000);
+
+ return () => {
+ clearTimeout(checkTxnTimeout);
+ clearTimeout(errorTimeout);
+ };
}
}, [isTxnCreated]);
const Container = styled.div`
.reject-btn {
- background-color: #2c3e50;
+ background-color: var(--other-primary);
border-radius: 5px;
color: white;
&:hover {
- background-color: #2c3e50;
+ background-color: var(--other-primary);
color: white;
}
}
@@ -120,23 +130,15 @@ const Container = styled.div`
}
.approve-btn {
- background-color: #04a46e;
+ background-color: var(--other-green);
border-radius: 5px;
color: white;
&:hover {
- background-color: #04a46e;
+ background-color: var(--other-green);
color: white;
}
}
-
- .fw-semi-bold {
- font-weight: 500;
- }
-
- .text-dark-grey {
- color: rgba(85, 85, 85, 1) !important;
- }
`;
useEffect(() => {
@@ -152,7 +154,10 @@ const InsufficientBalanceWarning = () => {
The request cannot be approved because the treasury balance is
@@ -162,8 +167,15 @@ const InsufficientBalanceWarning = () => {
) : null;
};
+
return (
+ setShowErrorToast(false)}
+ />
+
}
>
-
+
) : (
hasVotingPermission && (
{
+ ActionButton: () => (
+
+ ),
+ checkForDeposit: false,
+ treasuryDaoID,
+ disabled: isTxnCreated,
+ callbackAction: () => {
if (isInsufficientBalance) {
setShowWarning(true);
} else {
@@ -231,23 +255,31 @@ return (
setConfirmModal(true);
}
},
- loading: isTxnCreated && vote === actions.APPROVE,
- disabled: isTxnCreated,
}}
/>
{
+ ActionButton: () => (
+
+ ),
+ disabled: isTxnCreated,
+ checkForDeposit: false,
+ treasuryDaoID,
+ callbackAction: () => {
setVote(actions.REJECT);
setConfirmModal(true);
},
- loading: isTxnCreated && vote === actions.REJECT,
- disabled: isTxnCreated,
}}
/>
@@ -255,20 +287,30 @@ return (
)}
{/* currently showing delete btn only for proposal creator */}
{hasDeletePermission && proposalCreator === accountId && (
-
{
- setVote(actions.REMOVE);
- setConfirmModal(true);
+ (
+
+
+
+ ),
+ checkForDeposit: false,
+ treasuryDaoID,
+ disabled: isTxnCreated,
+ callbackAction: () => {
+ setVote(actions.REMOVE);
+ setConfirmModal(true);
+ },
}}
- data-testid="delete-btn"
- disabled={isTxnCreated}
- >
-
-
+ />
)}
)}
diff --git a/instances/treasury-devdao.near/widget/components/Votes.jsx b/instances/treasury-devdao.near/widget/components/Votes.jsx
index d9070667..e8068da2 100644
--- a/instances/treasury-devdao.near/widget/components/Votes.jsx
+++ b/instances/treasury-devdao.near/widget/components/Votes.jsx
@@ -8,9 +8,9 @@ const getPercentage = (value) =>
const Container = styled.div`
.bar {
- background-color: #e2e6ec;
+ background-color: var(--grey-04);
width: 100px;
- height: 20px;
+ height: 10px;
overflow: hidden;
}
.flex-item {
@@ -22,7 +22,7 @@ const Container = styled.div`
}
.green {
- color: #04a46e;
+ color: var(--other-green);
text-align: left;
}
@@ -49,7 +49,10 @@ return (
diff --git a/instances/treasury-devdao.near/widget/components/templates/AppLayout.jsx b/instances/treasury-devdao.near/widget/components/templates/AppLayout.jsx
index 02e2b253..53df5cd8 100644
--- a/instances/treasury-devdao.near/widget/components/templates/AppLayout.jsx
+++ b/instances/treasury-devdao.near/widget/components/templates/AppLayout.jsx
@@ -1,48 +1,6 @@
-const data = fetch(`https://httpbin.org/headers`);
-const gatewayURL = data?.body?.headers?.Origin ?? "";
-
-// we need fixed positioning for near social and not for org
-const ParentContainer = gatewayURL.includes("near.org")
- ? styled.div`
- width: 100%;
- `
- : styled.div`
- position: fixed;
- inset: 73px 0px 0px;
- width: 100%;
- overflow-y: scroll;
- background: var(--theme-bg-color) !important;
- `;
-
-const Theme = styled.div`
- display: flex;
- flex-direction: column;
- padding-top: calc(-1 * var(--body-top-padding));
- background: var(--theme-bg-color) !important;
-
- // remove up/down arrow in input of type = number
- /* For Chrome, Safari, and Edge */
- input[type="number"]::-webkit-outer-spin-button,
- input[type="number"]::-webkit-inner-spin-button {
- -webkit-appearance: none;
- margin: 0;
- }
-
- /* For Firefox */
- input[type="number"] {
- -moz-appearance: textfield;
- }
-
- .card {
- border-color: #e2e6ec !important;
- border-width: 1px !important;
- border-radius: 14px;
- }
-`;
-
-const Container = styled.div`
- width: 100%;
-`;
+const { BalanceBanner } = VM.require(
+ `${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/components.BalanceBanner`
+) || { BalanceBanner: () => <>> };
const AppHeader = ({ page, instance }) => (
(
/>
);
-function AppLayout({ page, instance, children }) {
- return (
-
-
-
-
- {children}
-
+function AppLayout({ page, instance, children, treasuryDaoID, accountId }) {
+ const config = treasuryDaoID
+ ? useCache(
+ () => Near.asyncView(treasuryDaoID, "get_config"),
+ treasuryDaoID + "_get_config",
+ { subscribe: false }
+ )
+ : null;
+ const metadata = JSON.parse(atob(config.metadata ?? ""));
+
+ const data = fetch(`https://httpbin.org/headers`);
+ const gatewayURL = data?.body?.headers?.Origin ?? "";
+ const isDarkTheme = metadata.theme === "dark";
+
+ const getColors = (isDarkTheme) => `
+ ${metadata.primaryColor ? `--theme-color: ${metadata.primaryColor};` : ""}
+ --bg-header-color: ${isDarkTheme ? "#222222" : "#2C3E50"};
+ --bg-page-color: ${isDarkTheme ? "#222222" : "#FFFFFF"};
+ --bg-system-color: ${isDarkTheme ? "#131313" : "#f4f4f4"};
+ --text-color: ${isDarkTheme ? "#CACACA" : "#1B1B18"};
+ --text-secondary-color: ${isDarkTheme ? "#878787" : "#999999"};
+ --text-alt-color: ${isDarkTheme ? "#FFFFFF" : "#FFFFFF"};
+ --link-inactive-color: ${isDarkTheme ? "" : "white"};
+ --link-active-color: ${isDarkTheme ? "" : "white"};
+ --border-color: ${isDarkTheme ? "#3B3B3B" : "rgba(226, 230, 236, 1)"};
+ --grey-01: ${isDarkTheme ? "#F4F4F4" : "#1B1B18"};
+ --grey-02: ${isDarkTheme ? "#B3B3B3" : "#555555"};
+ --grey-03: ${isDarkTheme ? "#555555" : "#B3B3B3"};
+ --grey-04: ${isDarkTheme ? "#323232" : "#F4F4F4"};
+ --grey-05: ${isDarkTheme ? "#1B1B18" : "#F7F7F7"};
+ --icon-color: ${isDarkTheme ? "#CACACA" : "#060606"};
+ --other-primary:#2775C9;
+ --other-warning:#B17108;
+ --other-green:#3CB179;
+ --other-red:#D95C4A;
+
+ // bootstrap theme color
+ --bs-body-bg: var(--bg-page-color);
+ --bs-border-color: var(--border-color);
+ --bs-dropdown-link-hover-color: var(--grey-04);
+`;
+
+ const ParentContainer = styled.div`
+ ${() => getColors(isDarkTheme)}
+ width: 100%;
+ background: var(--bg-system-color) !important;
+ ${() =>
+ gatewayURL.includes("near.org")
+ ? `
+ /* Styles specific to near.org */
+ position: static;
+ `
+ : `
+ /* Styles specific to other URLs */
+ position: fixed;
+ inset: 73px 0px 0px;
+ overflow-y: scroll;
+ `}
+ `;
+
+ const Theme = styled.div`
+ padding-top: calc(-1 * var(--body-top-padding));
+
+ // remove up/down arrow in input of type = number
+ /* For Chrome, Safari, and Edge */
+ input[type="number"]::-webkit-outer-spin-button,
+ input[type="number"]::-webkit-inner-spin-button {
+ -webkit-appearance: none;
+ margin: 0;
+ }
+
+ /* For Firefox */
+ input[type="number"] {
+ -moz-appearance: textfield;
+ }
+
+ .card {
+ border-color: var(--border-color) !important;
+ border-width: 1px !important;
+ border-radius: 14px;
+ background-color: var(--bg-page-color) !important;
+ }
+
+ .dropdown-menu {
+ background-color: var(--bg-page-color) !important;
+ color: var(--text-color) !important;
+ }
+
+ .dropdown-item.active,
+ .dropdown-item:active {
+ background-color: var(--grey-04) !important;
+ color: inherit !important;
+ }
+
+ .offcanvas {
+ background-color: var(--bg-page-color) !important;
+ color: var(--text-color) !important;
+ }
+
+ color: var(--text-color);
+
+ a {
+ text-decoration: none;
+ color: var(--link-inactive-color) !important;
+ &.active {
+ color: var(--link-active-color) !important;
+ font-weight: 700 !important;
+ }
+
+ &:hover {
+ text-decoration: none;
+ color: var(--link-active-color) !important;
+ font-weight: 700 !important;
+ }
+ }
+
+ button.primary {
+ background: var(--theme-color) !important;
+ color: var(--text-alt-color) !important;
+ border: none !important;
+ padding-block: 0.7rem !important;
+ i {
+ color: var(--text-alt-color) !important;
+ }
+ }
+
+ .primary-button {
+ background: var(--theme-color) !important;
+ color: var(--text-alt-color) !important;
+ border: none !important;
+ i {
+ color: var(--text-alt-color) !important;
+ }
+ }
+
+ .text-lg {
+ font-size: 15px;
+ }
+
+ .fw-semi-bold {
+ font-weight: 500;
+ }
+
+ .text-secondary {
+ color: var(--text-secondary-color) !important;
+ }
+
+ .max-w-100 {
+ max-width: 100%;
+ }
+
+ .custom-truncate {
+ display: -webkit-box;
+ -webkit-line-clamp: 3;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ line-height: 1.5;
+ max-height: 4.5em;
+ text-align: left;
+ }
+
+ .display-none {
+ display: none;
+ }
+
+ .text-right {
+ text-align: end;
+ }
+
+ .text-left {
+ text-align: left;
+ }
+ .text-underline {
+ text-decoration: underline !important;
+ }
+
+ .bg-highlight {
+ background-color: rgb(185, 185, 185, 0.2);
+ }
+
+ .cursor-pointer {
+ cursor: pointer;
+ }
+
+ .theme-btn {
+ background: var(--theme-color) !important;
+ color: white !important;
+ border: none;
+ }
+
+ .theme-btn.btn:hover {
+ color: white !important;
+ }
+
+ .toast-container {
+ right: 10px !important;
+ bottom: 10px !important;
+ }
+ .toast {
+ border-radius: 10px;
+ overflow: hidden;
+ color: var(--text-color) !important;
+ background: var(--bg-page-color) !important;
+ border-color: var(--border-color) !important;
+
+ a {
+ color: inherit !important;
+ &:active {
+ color: inherit !important;
+ }
+ &:hover {
+ color: inherit !important;
+ }
+ }
+ }
+
+ .toast-header {
+ background-color: var(--bg-system-color) !important;
+ color: var(--text-secondary-color) !important;
+ }
+
+ .text-md {
+ font-size: 15px;
+ }
+
+ .primary-text-color {
+ color: var(--theme-color);
+ a {
+ color: var(--theme-color) !important;
+ }
+
+ i {
+ color: var(--theme-color) !important;
+ }
+ }
+
+ .btn-outline.btn:hover {
+ color: inherit !important;
+ }
+
+ .primary-text-color.btn:hover {
+ color: inherit !important;
+ }
+
+ .btn-outline-plain {
+ padding-block: 8px !important;
+ padding-inline: 10px !important;
+ border-radius: 0.375rem !important;
+ border: 1.5px solid var(--border-color) !important;
+ background-color: var(--bg-page-color) !important;
+ color: var(--text-color) !important;
+
+ &:hover {
+ background-color: white;
+ color: black;
+ }
+ }
+
+ h6,
+ .h6,
+ h5,
+ .h5,
+ h4,
+ .h4,
+ h3,
+ .h3,
+ h2,
+ .h2,
+ h1,
+ .h1 {
+ color: var(--text-color) !important;
+ }
+
+ .btn:disabled,
+ .btn.disabled,
+ fieldset:disabled {
+ border-color: transparent !important;
+ }
+
+ .table {
+ border-color: var(--border-color) !important;
+ color: var(--text-color) !important;
+ }
+
+ .bg-white {
+ background-color: var(--bg-page-color) !important;
+ color: var(--text-color) !important;
+ }
+
+ .bg-dropdown {
+ background-color: var(--bg-page-color) !important;
+ color: var(--text-color) !important;
+ }
+
+ .bg-custom-overlay {
+ background-color: var(--bg-page-color) !important;
+ color: var(--text-color) !important;
+ }
+
+ .fill-accent {
+ fill: var(--theme-color);
+ }
+
+ .use-max-bg {
+ color: #007aff;
+ cursor: pointer;
+ }
+
+ .bg-validator-info {
+ background: rgba(0, 16, 61, 0.06);
+ color: #1b1b18;
+ padding-inline: 0.8rem;
+ padding-block: 0.5rem;
+ font-weight: 500;
+ font-size: 13px;
+ }
+
+ .bg-validator-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;
+ }
+
+ .bg-withdraw-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;
+ }
+
+ .text-sm {
+ font-size: 13px;
+ }
+
+ .text-secondary a {
+ color: inherit !important;
+ }
+
+ .text-delete {
+ color: var(--other-red) !important;
+
+ i {
+ color: var(--other-red) !important;
+ }
+ }
+
+ .btn-outline.btn:hover {
+ color: inherit !important;
+ }
+
+ .border-right {
+ border-right: 1px solid var(--border-color);
+ }
+
+ .cursor-pointer {
+ cursor: pointer;
+ }
+
+ .success-icon {
+ color: var(--other-green) !important;
+ }
+
+ .warning-icon {
+ color: var(--other-warning) !important;
+ }
+
+ .error-icon {
+ color: var(--other-red) !important;
+ }
+ `;
+
+ return !config ? (
+ <>>
+ ) : (
+
+
+
+
+ {children}
);
diff --git a/instances/treasury-devdao.near/widget/config/css.jsx b/instances/treasury-devdao.near/widget/config/css.jsx
index 5446ba50..aae9f279 100644
--- a/instances/treasury-devdao.near/widget/config/css.jsx
+++ b/instances/treasury-devdao.near/widget/config/css.jsx
@@ -1,66 +1,5 @@
const Theme = styled.div`
--theme-color: #05a36e;
- --theme-bg-color: #f4f4f4;
- --text-color: white;
- --link-inactive-color: white;
- --link-active-color: white;
- --border-color: rgba(226, 230, 236, 1);
- --light-grey-color: rgba(185, 185, 185, 1);
- --dark-grey-color: rgba(103, 103, 103, 1);
-
- a {
- text-decoration: none;
- color: var(--link-inactive-color) !important;
- &.active {
- color: var(--link-active-color) !important;
- font-weight: 700 !important;
- }
-
- &:hover {
- text-decoration: none;
- color: var(--link-active-color) !important;
- font-weight: 700 !important;
- }
- }
-
- button {
- &.primary {
- background: var(--theme-color);
- color: var(--text-color);
- border: none !important;
- padding-block: 0.7rem !important;
- }
- }
-
- .text-light-grey {
- color: var(--light-grey-color);
- }
-
- .text-muted {
- color: var(--dark-grey-color);
- }
-
- .text-md {
- font-size: 15px;
- }
-
- .primary-text-color {
- color: var(--theme-color);
- }
-
- .btn-outline-plain {
- padding-block: 8px;
- padding-inline: 10px;
- border-radius: 0.375rem;
- border: 1.5px solid #e2e6ec;
- background-color: white;
- color: black;
-
- &:hover {
- background-color: white;
- color: black;
- }
- }
`;
return { Theme };
diff --git a/instances/treasury-devdao.near/widget/lib/common.jsx b/instances/treasury-devdao.near/widget/lib/common.jsx
index 3ac04750..8ec3cc59 100644
--- a/instances/treasury-devdao.near/widget/lib/common.jsx
+++ b/instances/treasury-devdao.near/widget/lib/common.jsx
@@ -1,5 +1,7 @@
function getApproversAndThreshold(treasuryDaoID, kind, isDeleteCheck) {
- const daoPolicy = Near.view(treasuryDaoID, "get_policy", {});
+ const daoPolicy = treasuryDaoID
+ ? Near.view(treasuryDaoID, "get_policy", {})
+ : null;
const groupWithPermission = (daoPolicy.roles ?? []).filter((role) => {
const permissions = isDeleteCheck
? ["*:*", `${kind}:*`, `${kind}:VoteRemove`, "*:VoteRemove"]
@@ -84,7 +86,10 @@ function getRoleWiseData(treasuryDaoID) {
}
function getPolicyApproverGroup(treasuryDaoID) {
- const daoPolicy = Near.view(treasuryDaoID, "get_policy", {});
+ const daoPolicy = treasuryDaoID
+ ? Near.view(treasuryDaoID, "get_policy", {})
+ : null;
+
const groupWithPermission = (daoPolicy.roles ?? []).filter((role) => {
const policyPermissions = [
"*:*",
@@ -359,7 +364,10 @@ function getMembersAndPermissions(treasuryDaoID) {
}
function getDaoRoles(treasuryDaoID) {
- const daoPolicy = Near.view(treasuryDaoID, "get_policy", {});
+ const daoPolicy = treasuryDaoID
+ ? Near.view(treasuryDaoID, "get_policy", {})
+ : null;
+
if (Array.isArray(daoPolicy.roles)) {
return daoPolicy.roles.map((role) => role.name);
}
@@ -372,7 +380,10 @@ function hasPermission(treasuryDaoID, accountId, kindName, actionType) {
return false;
}
const isAllowed = false;
- const daoPolicy = Near.view(treasuryDaoID, "get_policy", {});
+ const daoPolicy = treasuryDaoID
+ ? Near.view(treasuryDaoID, "get_policy", {})
+ : null;
+
if (Array.isArray(daoPolicy.roles)) {
const permissions = daoPolicy.roles.map((role) => {
if (
@@ -426,10 +437,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();
@@ -483,7 +492,7 @@ function formatSubmissionTimeStamp(submissionTime, proposalPeriod) {
? "Expired"
: `${totalDays}d ${remainingHours}h ${remainingMinutes}m`}
-
+
{hours}:{minutes} {day} {month} {year}
diff --git a/instances/treasury-devdao.near/widget/lib/modal.jsx b/instances/treasury-devdao.near/widget/lib/modal.jsx
index 336bf784..36921f0b 100644
--- a/instances/treasury-devdao.near/widget/lib/modal.jsx
+++ b/instances/treasury-devdao.near/widget/lib/modal.jsx
@@ -25,11 +25,6 @@ const ModalDiv = styled.div`
.btn {
font-size: 14px;
}
-
- .theme-btn {
- background: var(--theme-color) !important;
- color: white;
- }
`;
const ModalBackdrop = styled.div`
@@ -100,7 +95,7 @@ const NoButton = styled.button`
const ModalHeader = ({ children }) => (
- {children}
+ {children}
);
diff --git a/instances/treasury-devdao.near/widget/lib/skeleton.jsx b/instances/treasury-devdao.near/widget/lib/skeleton.jsx
index c7407f97..4c37b049 100644
--- a/instances/treasury-devdao.near/widget/lib/skeleton.jsx
+++ b/instances/treasury-devdao.near/widget/lib/skeleton.jsx
@@ -1,5 +1,5 @@
const Skeleton = styled.div`
- background: #efefef;
+ background: var(--grey-04);
animation: pulse 1.5s ease-in-out infinite;
@keyframes pulse {
diff --git a/instances/treasury-devdao.near/widget/pages/dashboard/Chart.jsx b/instances/treasury-devdao.near/widget/pages/dashboard/Chart.jsx
index 317153ba..333ea80d 100644
--- a/instances/treasury-devdao.near/widget/pages/dashboard/Chart.jsx
+++ b/instances/treasury-devdao.near/widget/pages/dashboard/Chart.jsx
@@ -1,4 +1,10 @@
-const { nearPrice, ftTokens, accountId, title } = props;
+const { nearPrice, ftTokens, accountId, title, instance } = props;
+
+if (!instance) {
+ return <>>;
+}
+
+const { treasuryDaoID } = VM.require(`${instance}/widget/config.data`);
const { Skeleton } = VM.require(
"${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/lib.skeleton"
@@ -18,10 +24,10 @@ const [selectedPeriod, setSelectedPeriod] = useState({
const [selectedToken, setSelectedToken] = useState("near");
const [isLoading, setIsLoading] = useState(true);
const [balanceDate, setBalanceDate] = useState({ balance: 0, date: "" });
-const nearTokenIcon = "${REPL_NEAR_TOKEN_ICON}";
+
const nearTokenInfo = {
contract: "near",
- ft_meta: { symbol: "NEAR", icon: nearTokenIcon },
+ ft_meta: { symbol: "NEAR" },
};
const tokens = Array.isArray(ftTokens)
@@ -43,8 +49,20 @@ function formatCurrency(amount) {
.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
+const config = treasuryDaoID ? Near.view(treasuryDaoID, "get_config") : null;
+const metadata = JSON.parse(atob(config.metadata ?? ""));
+
+const isDarkTheme = metadata.theme === "dark";
+const bgPageColor = isDarkTheme ? "#222222" : "#FFFFFF";
+const borderColor = isDarkTheme ? "#3B3B3B" : "rgba(226, 230, 236, 1)";
+const iconColor = isDarkTheme ? "#CACACA" : "#060606";
+const textColor = isDarkTheme ? "#CACACA" : "#1B1B18";
+const fillStyle = isDarkTheme
+ ? "rgba(27, 27, 24, 0.1)"
+ : "rgba(255, 255, 255, 0.7)";
+
const code = `
-
+
@@ -53,24 +71,24 @@ const code = `
-
-
-
-
+
+
+`;
+
+const SubmitToast = () => {
+ return (
+ showToastStatus && (
+
+
+
+ Just Now
+ setToastStatus(null)}
+ >
+
+
+
+
+ )
+ );
+};
+
+function getLastProposalId() {
+ return Near.asyncView(treasuryDaoID, "get_last_proposal_id").then(
+ (result) => result
+ );
+}
+
+useEffect(() => {
+ getLastProposalId().then((i) => setLastProposalId(i));
+}, []);
+
+useEffect(() => {
+ if (isTxnCreated) {
+ let checkTxnTimeout = null;
+ let errorTimeout = null;
+
+ const checkForNewProposal = () => {
+ getLastProposalId().then((id) => {
+ if (lastProposalId !== id) {
+ setToastStatus(true);
+ setTxnCreated(false);
+ clearTimeout(errorTimeout);
+ } else {
+ checkTxnTimeout = setTimeout(() => checkForNewProposal(), 1000);
+ }
+ });
+ };
+ checkForNewProposal();
+
+ // if in 20 seconds there is no change, show error condition
+ errorTimeout = setTimeout(() => {
+ setShowErrorToast(true);
+ setTxnCreated(false);
+ clearTimeout(checkTxnTimeout);
+ }, 20000);
+
+ return () => {
+ clearTimeout(checkTxnTimeout);
+ clearTimeout(errorTimeout);
+ };
+ }
+}, [isTxnCreated]);
+
+function toBase64(json) {
+ return Buffer.from(JSON.stringify(json)).toString("base64");
+}
+
+function onSubmitClick() {
+ setTxnCreated(true);
+ const deposit = daoPolicy?.proposal_bond || 100000000000000000000000;
+
+ const description = {
+ title: "Update Config - Theme & logo",
+ };
+ Near.call([
+ {
+ contractName: treasuryDaoID,
+ methodName: "add_proposal",
+ args: {
+ proposal: {
+ description: encodeToMarkdown(description),
+ kind: {
+ ChangeConfig: {
+ config: {
+ name: config.name,
+ purpose: config.purpose,
+ metadata: toBase64({
+ ...metadata,
+ primaryColor: color,
+ flagLogo: image,
+ theme: selectedTheme.value,
+ }),
+ },
+ },
+ },
+ },
+ },
+ gas: 200000000000000,
+ },
+ ]);
+}
+
+function setDefault() {
+ setImage(metadata?.flagLogo ?? defaultImage);
+ setColor(metadata?.primaryColor ?? defaultColor);
+ setSelectedTheme(
+ ThemeOptions.find((i) => i.value === metadata?.theme) ?? ThemeOptions[0]
+ );
+}
+
+useEffect(() => {
+ if (metadata) {
+ setDefault();
+ }
+}, [metadata]);
+
+return (
+
+
+ setShowErrorToast(false)}
+ />
+
+
Theme & Logo
+ {!metadata ? (
+
+
+
+ ) : (
+
+
+
+
+ {error && (
+
+
+ {error}
+
+ )}
+
+
+ Theme
+
+
+
+
+ (
+
+ ),
+ checkForDeposit: true,
+ treasuryDaoID,
+ disabled: !hasCreatePermission || error || isTxnCreated,
+ callbackAction: onSubmitClick,
+ }}
+ />
+
+
+ )}
+
+
+);
diff --git a/instances/treasury-devdao.near/widget/pages/settings/Thresholds.jsx b/instances/treasury-devdao.near/widget/pages/settings/Thresholds.jsx
index 2690a2d0..0f50f89f 100644
--- a/instances/treasury-devdao.near/widget/pages/settings/Thresholds.jsx
+++ b/instances/treasury-devdao.near/widget/pages/settings/Thresholds.jsx
@@ -1,22 +1,34 @@
-const { encodeToMarkdown } = VM.require(
+const { href } = VM.require("${REPL_DEVHUB}/widget/core.lib.url") || {
+ href: () => {},
+};
+const { TransactionLoader } = VM.require(
+ `${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/components.TransactionLoader`
+) || { TransactionLoader: () => <>> };
+
+const { encodeToMarkdown, hasPermission, getRoleWiseData } = VM.require(
"${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/lib.common"
-);
+) || {
+ encodeToMarkdown: () => {},
+ hasPermission: () => {},
+};
const { instance } = props;
if (!instance) {
return <>>;
}
const { treasuryDaoID } = VM.require(`${instance}/widget/config.data`);
-const { getRoleWiseData, hasPermission } = VM.require(
- "${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/lib.common"
-) || { hasPermission: () => {} };
-const { href } = VM.require("${REPL_DEVHUB}/widget/core.lib.url") || {
- href: () => {},
-};
if (typeof getRoleWiseData !== "function") {
return <>>;
}
+
+const hasEditPermission = hasPermission(
+ treasuryDaoID,
+ context.accountId,
+ "ChangeConfig",
+ "AddProposal"
+);
+
const [selectedGroup, setSelectedGroup] = useState(null);
const [selectedVoteOption, setSelectedVoteOption] = useState(null);
const [selectedVoteValue, setSelectedVoteValue] = useState(null);
@@ -28,12 +40,13 @@ const [valueError, setValueError] = useState(null);
const [showConfirmModal, setConfirmModal] = useState(null);
const [rolesData, setRolesData] = useState(null);
const [refreshData, setRefreshData] = useState(false);
+const [showErrorToast, setShowErrorToast] = useState(false);
const hasCreatePermission = hasPermission(
treasuryDaoID,
context.accountId,
"policy",
- "vote"
+ "AddProposal"
);
useEffect(() => {
@@ -78,16 +91,33 @@ useEffect(() => {
useEffect(() => {
if (isTxnCreated) {
+ let checkTxnTimeout = null;
+ let errorTimeout = null;
+
const checkForNewProposal = () => {
getLastProposalId().then((id) => {
if (lastProposalId !== id) {
setToastStatus(true);
+ setTxnCreated(false);
+ clearTimeout(errorTimeout);
} else {
- setTimeout(() => checkForNewProposal(), 1000);
+ checkTxnTimeout = setTimeout(() => checkForNewProposal(), 1000);
}
});
};
checkForNewProposal();
+
+ // if in 20 seconds there is no change, show error condition
+ errorTimeout = setTimeout(() => {
+ setShowErrorToast(true);
+ setTxnCreated(false);
+ clearTimeout(checkTxnTimeout);
+ }, 20000);
+
+ return () => {
+ clearTimeout(checkTxnTimeout);
+ clearTimeout(errorTimeout);
+ };
}
}, [isTxnCreated]);
@@ -105,27 +135,20 @@ useEffect(() => {
const Container = styled.div`
font-size: 14px;
- .border-right {
- border-right: 1px solid rgba(226, 230, 236, 1);
- }
.card-title {
font-size: 18px;
font-weight: 600;
padding-block: 5px;
- border-bottom: 1px solid rgba(226, 230, 236, 1);
+ border-bottom: 1px solid var(--border-color);
}
.selected-role {
- background-color: rgba(244, 244, 244, 1);
- }
-
- .cursor-pointer {
- cursor: pointer;
+ background-color: var(--grey-04);
}
.tag {
- background-color: rgba(244, 244, 244, 1);
+ background-color: var(--grey-04);
font-size: 12px;
padding-block: 5px;
}
@@ -135,10 +158,6 @@ const Container = styled.div`
font-size: 12px;
}
- .fw-bold {
- font-weight: 500 !important;
- }
-
.p-0 {
padding: 0 !important;
}
@@ -147,14 +166,9 @@ const Container = styled.div`
font-size: 13px;
}
- .theme-btn {
- background: var(--theme-color) !important;
- color: white;
- }
-
.warning {
background-color: rgba(255, 158, 0, 0.1);
- color: rgba(177, 113, 8, 1);
+ color: var(--other-warning);
font-weight: 500;
}
@@ -162,38 +176,11 @@ const Container = styled.div`
font-size: 12px !important;
}
- .text-muted {
- color: rgba(153, 153, 153, 1);
- }
-
- .text-red {
- color: #d95c4a;
- }
-
- .toast {
- background: white !important;
- }
-
- .toast-header {
- background-color: #2c3e50 !important;
- color: white !important;
- }
-
.dropdown-toggle:after {
top: 20% !important;
}
`;
-const ToastContainer = styled.div`
- a {
- color: black !important;
- text-decoration: underline !important;
- &:hover {
- color: black !important;
- }
- }
-`;
-
const proposalKinds = [
"config",
"policy",
@@ -267,30 +254,35 @@ function onSubmitClick() {
const SubmitToast = () => {
return (
showToastStatus && (
-
+
Just Now
setToastStatus(null)}
>
-
+
)
);
};
@@ -313,6 +305,11 @@ const requiredVotes = selectedGroup
return (
+ setShowErrorToast(false)}
+ />
{Array.isArray(rolesData) && rolesData.length ? (
{isPercentageSelected && (
-
+
This is equivalent to
{requiredVotes} votes
@@ -480,17 +477,37 @@ return (
onClick: () => {
resetForm();
},
- disabled: isTxnCreated,
+ disabled: isTxnCreated || !hasCreatePermission,
}}
/>
setConfirmModal(true),
- loading: isTxnCreated,
+ ActionButton: () => (
+
+ ),
+ checkForDeposit: true,
+ disabled:
+ !selectedVoteValue ||
+ valueError ||
+ !hasCreatePermission ||
+ isTxnCreated,
+ treasuryDaoID,
+ callbackAction: () => {
+ setConfirmModal(true);
+ },
}}
/>
diff --git a/instances/treasury-devdao.near/widget/pages/settings/VotingDurationPage.jsx b/instances/treasury-devdao.near/widget/pages/settings/VotingDurationPage.jsx
index c20bc240..6415453d 100644
--- a/instances/treasury-devdao.near/widget/pages/settings/VotingDurationPage.jsx
+++ b/instances/treasury-devdao.near/widget/pages/settings/VotingDurationPage.jsx
@@ -1,6 +1,12 @@
-const { encodeToMarkdown } = VM.require(
+const { encodeToMarkdown, hasPermission, getRoleWiseData } = VM.require(
"${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/lib.common"
-);
+) || {
+ encodeToMarkdown: () => {},
+ hasPermission: () => {},
+};
+const { TransactionLoader } = VM.require(
+ `${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/components.TransactionLoader`
+) || { TransactionLoader: () => <>> };
const { href } = VM.require("${REPL_DEVHUB}/widget/core.lib.url") || {
href: () => {},
@@ -16,9 +22,19 @@ const { Modal, ModalContent, ModalHeader, ModalFooter } = VM.require(
"${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/lib.modal"
);
-const daoPolicy = Near.view(treasuryDaoID, "get_policy", {});
+const daoPolicy = treasuryDaoID
+ ? Near.view(treasuryDaoID, "get_policy", {})
+ : null;
+
const lastProposalId = Near.view(treasuryDaoID, "get_last_proposal_id");
+const hasCreatePermission = hasPermission(
+ treasuryDaoID,
+ context.accountId,
+ "policy",
+ "AddProposal"
+);
+
if (!daoPolicy || lastProposalId === null) {
return (
@@ -49,93 +65,23 @@ const [isSubmittingChangeRequest, setSubmittingChangeRequest] = useState(false);
const [showAffectedProposalsModal, setShowAffectedProposalsModal] =
useState(false);
+const [showErrorToast, setShowErrorToast] = useState(false);
const [showLoader, setLoading] = useState(false);
const Container = styled.div`
font-size: 14px;
- .border-right {
- border-right: 1px solid rgba(226, 230, 236, 1);
- }
.card-title {
font-size: 18px;
font-weight: 600;
padding-block: 5px;
- border-bottom: 1px solid rgba(226, 230, 236, 1);
- }
-
- .selected-role {
- background-color: rgba(244, 244, 244, 1);
- }
-
- .cursor-pointer {
- cursor: pointer;
- }
-
- .tag {
- background-color: rgba(244, 244, 244, 1);
- font-size: 12px;
- padding-block: 5px;
+ border-bottom: 1px solid var(--border-color);
}
label {
- color: rgba(153, 153, 153, 1);
+ color: var(--text-secondary);
font-size: 12px;
}
-
- .fw-bold {
- font-weight: 500 !important;
- }
-
- .p-0 {
- padding: 0 !important;
- }
-
- .text-md {
- font-size: 13px;
- }
-
- .theme-btn {
- background-color: var(--theme-color) !important;
- color: white;
- }
-
- .warning {
- background-color: rgba(255, 158, 0, 0.1);
- color: rgba(177, 113, 8, 1);
- font-weight: 500;
- }
-
- .text-sm {
- font-size: 12px !important;
- }
-
- .text-muted {
- color: rgba(153, 153, 153, 1);
- }
-
- .text-red {
- color: #d95c4a;
- }
-
- .toast {
- background: white !important;
- }
-
- .toast-header {
- background-color: #2c3e50 !important;
- color: white !important;
- }
-`;
-
-const ToastContainer = styled.div`
- a {
- color: black !important;
- text-decoration: underline !important;
- &:hover {
- color: black !important;
- }
- }
`;
const cancelChangeRequest = () => {
@@ -319,23 +265,34 @@ const submitChangeRequest = () => {
};
useEffect(() => {
- Near.asyncView(treasuryDaoID, "get_proposal", {
- id: lastProposalId - 1,
- }).then((proposal) => {
- const proposal_period =
- proposal?.kind?.ChangePolicyUpdateParameters?.parameters?.proposal_period;
+ if (isSubmittingChangeRequest) {
+ let errorTimeout = null;
+ Near.asyncView(treasuryDaoID, "get_proposal", {
+ id: lastProposalId - 1,
+ }).then((proposal) => {
+ const proposal_period =
+ proposal?.kind?.ChangePolicyUpdateParameters?.parameters
+ ?.proposal_period;
+
+ if (
+ proposal_period &&
+ isSubmittingChangeRequest &&
+ Number(proposal_period.substring(0, proposal_period.length - 9)) /
+ (24 * 60 * 60) ===
+ Number(durationDays)
+ ) {
+ setToastStatus(true);
+ setSubmittingChangeRequest(false);
+ clearTimeout(errorTimeout);
+ }
+ });
- if (
- proposal_period &&
- isSubmittingChangeRequest &&
- Number(proposal_period.substring(0, proposal_period.length - 9)) /
- (24 * 60 * 60) ===
- Number(durationDays)
- ) {
- setToastStatus(true);
+ // if in 20 seconds there is no change, show error condition
+ errorTimeout = setTimeout(() => {
+ setShowErrorToast(true);
setSubmittingChangeRequest(false);
- }
- });
+ }, 20000);
+ }
}, [isSubmittingChangeRequest, lastProposalId]);
const changeDurationDays = (newDurationDays) => {
@@ -347,6 +304,11 @@ const showImpactedRequests =
return (
+ setShowErrorToast(false)}
+ />
Voting Duration
@@ -365,6 +327,7 @@ return (
placeholder="Enter voting duration days"
value={durationDays}
onChange={(event) => changeDurationDays(event.target.value)}
+ disabled={!hasCreatePermission}
>
@@ -515,38 +478,66 @@ return (
}}
/>
(
+
+ ),
+ checkForDeposit: true,
+ disabled:
+ durationDays === currentDurationDays ||
+ showLoader ||
+ !hasCreatePermission ||
+ isSubmittingChangeRequest,
+ treasuryDaoID,
+ callbackAction: submitChangeRequest,
}}
/>
-
+
Just Now
- setToastStatus(null)}>
+ setToastStatus(null)}
+ >
-
Voting duration change request submitted.
-
- View it
-
+
+
+
+
Voting duration change request submitted.
+
+ View it
+
+
+
-
+
);
diff --git a/instances/treasury-devdao.near/widget/pages/settings/feed/History.jsx b/instances/treasury-devdao.near/widget/pages/settings/feed/History.jsx
index 90b28e75..52704f0d 100644
--- a/instances/treasury-devdao.near/widget/pages/settings/feed/History.jsx
+++ b/instances/treasury-devdao.near/widget/pages/settings/feed/History.jsx
@@ -70,7 +70,9 @@ useEffect(() => {
});
}, [currentPage, rowsPerPage]);
-const policy = Near.view(treasuryDaoID, "get_policy", {});
+const policy = treasuryDaoID
+ ? Near.view(treasuryDaoID, "get_policy", {})
+ : null;
const settingsApproverGroup = getApproversAndThreshold(treasuryDaoID, "policy");
diff --git a/instances/treasury-devdao.near/widget/pages/settings/feed/PendingRequests.jsx b/instances/treasury-devdao.near/widget/pages/settings/feed/PendingRequests.jsx
index 1e11bcca..131d7a57 100644
--- a/instances/treasury-devdao.near/widget/pages/settings/feed/PendingRequests.jsx
+++ b/instances/treasury-devdao.near/widget/pages/settings/feed/PendingRequests.jsx
@@ -54,7 +54,9 @@ useEffect(() => {
fetchProposals();
}, [currentPage, rowsPerPage]);
-const policy = Near.view(treasuryDaoID, "get_policy", {});
+const policy = treasuryDaoID
+ ? Near.view(treasuryDaoID, "get_policy", {})
+ : null;
const settingsApproverGroup = getApproversAndThreshold(treasuryDaoID, "policy");
diff --git a/instances/treasury-devdao.near/widget/pages/settings/feed/SettingsDropdown.jsx b/instances/treasury-devdao.near/widget/pages/settings/feed/SettingsDropdown.jsx
index a0483f58..0fb8046d 100644
--- a/instances/treasury-devdao.near/widget/pages/settings/feed/SettingsDropdown.jsx
+++ b/instances/treasury-devdao.near/widget/pages/settings/feed/SettingsDropdown.jsx
@@ -77,36 +77,16 @@ const Container = styled.div`
z-index: 9999;
}
- .dropdown-item.active,
- .dropdown-item:active {
- background-color: #f0f0f0 !important;
- color: black;
- }
-
.custom-select {
position: relative;
}
-
- .cursor-pointer {
- cursor: pointer;
- }
-
- .text-grey {
- color: #b3b3b3;
- }
-
- .text-sm {
- font-size: 13px;
- }
`;
const Item = ({ option }) => {
return (
{option.title}
@@ -133,7 +113,7 @@ return (
{isOpen && (
-
Shown in table
+
Shown in table
{settingsOptions.map((option) => {
if (
!isPendingPage &&
diff --git a/instances/treasury-devdao.near/widget/pages/settings/feed/Table.jsx b/instances/treasury-devdao.near/widget/pages/settings/feed/Table.jsx
index b1f794d3..8f720c14 100644
--- a/instances/treasury-devdao.near/widget/pages/settings/feed/Table.jsx
+++ b/instances/treasury-devdao.near/widget/pages/settings/feed/Table.jsx
@@ -30,6 +30,7 @@ const isPendingRequests = props.isPendingRequests;
const settingsApproverGroup = props.settingsApproverGroup;
const [showToastStatus, setToastStatus] = useState(false);
const [voteProposalId, setVoteProposalId] = useState(null);
+const [showRefreshPageText, setShowRefreshPageText] = useState(false);
const refreshTableData = props.refreshTableData;
const deleteGroup = props.deleteGroup;
@@ -53,18 +54,7 @@ const columnsVisibility = JSON.parse(
const Container = styled.div`
font-size: 13px;
min-height: 60vh;
- .text-grey {
- color: #b9b9b9 !important;
- }
- .text-size-2 {
- font-size: 15px;
- }
- .text-dark-grey {
- color: #687076;
- }
- .text-grey-100 {
- background-color: #f5f5f5;
- }
+
td {
padding: 0.5rem;
color: inherit;
@@ -72,66 +62,9 @@ const Container = styled.div`
background: inherit;
}
- .max-w-100 {
- max-width: 100%;
- }
-
table {
overflow-x: auto;
}
-
- .bold {
- font-weight: 500;
- }
-
- .custom-truncate {
- display: -webkit-box;
- -webkit-line-clamp: 3;
- -webkit-box-orient: vertical;
- overflow: hidden;
- text-overflow: ellipsis;
- line-height: 1.5;
- max-height: 4.5em;
- text-align: left;
- }
-
- .display-none {
- display: none;
- }
-
- .text-right {
- text-align: end;
- }
-
- .text-left {
- text-align: left;
- }
- .text-underline {
- text-decoration: underline !important;
- }
-
- .bg-highlight {
- background-color: rgb(185, 185, 185, 0.2);
- }
-
- .toast {
- background: white !important;
- }
-
- .toast-header {
- background-color: #2c3e50 !important;
- color: white !important;
- }
-`;
-
-const ToastContainer = styled.div`
- a {
- color: black !important;
- text-decoration: underline !important;
- &:hover {
- color: black !important;
- }
- }
`;
function checkProposalStatus(proposalId) {
@@ -140,6 +73,16 @@ function checkProposalStatus(proposalId) {
})
.then((result) => {
setToastStatus(result.status);
+ // if status is approved and it's a theme change request, tell user to refresh page to view changes
+ if (
+ result.status === "Approved" &&
+ Object.keys(result.kind ?? {})?.[0] &&
+ Object.keys(result.kind)[0] === "ChangeConfig"
+ ) {
+ setShowRefreshPageText(true);
+ } else {
+ setShowRefreshPageText(false);
+ }
setVoteProposalId(proposalId);
refreshTableData();
})
@@ -219,7 +162,9 @@ const ToastStatusContent = () => {
content = "Your vote is counted, the request is highlighted.";
break;
case "Approved":
- content = "The request has been successfully executed.";
+ content =
+ "The request has been successfully executed." +
+ (showRefreshPageText ? " Refresh the page to see the updates." : "");
break;
case "Rejected":
content = "The request has been rejected.";
@@ -233,25 +178,34 @@ const ToastStatusContent = () => {
}
return (
- {content}
-
- {showToastStatus !== "InProgress" && (
-
- View in History
-
- )}
+
+ {showToastStatus === "Approved" && (
+
+ )}
+
+ {content}
+
+ {showToastStatus !== "InProgress" &&
+ showToastStatus !== "Removed" && (
+
+ View in History
+
+ )}
+
+
);
};
@@ -260,15 +214,18 @@ const VoteSuccessToast = () => {
return showToastStatus &&
(typeof voteProposalId === "number" ||
typeof highlightProposalId === "number") ? (
-
+
Just Now
- setToastStatus(null)}>
+ setToastStatus(null)}
+ >
-
+
) : null;
};
@@ -290,7 +247,7 @@ const ProposalsComponent = () => {
: ""
}
>
- {item.id}
+ {item.id}
{
)}
-
+
{title ?? item.description}
-
+
+ ),
children: (
{
{item.proposer}
),
+ instance,
}}
/>
@@ -380,6 +346,7 @@ const ProposalsComponent = () => {
props={{
votes: item.votes,
approversGroup: settingsApproverGroup?.approverAccounts,
+ instance,
}}
/>
@@ -484,7 +451,7 @@ ${JSON.stringify(showDetailsProposalKind.transactionDetails, null, 2)}
) : (
-
+
#
Created Date
{!isPendingRequests && Status }
diff --git a/instances/treasury-devdao.near/widget/pages/settings/index.jsx b/instances/treasury-devdao.near/widget/pages/settings/index.jsx
index dfca14ba..4c272998 100644
--- a/instances/treasury-devdao.near/widget/pages/settings/index.jsx
+++ b/instances/treasury-devdao.near/widget/pages/settings/index.jsx
@@ -21,6 +21,11 @@ const [leftNavbarOptions, setLeftBarOptions] = useState([
href: `${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/pages.settings.VotingDurationPage`,
props: { instance },
},
+ {
+ title: "Theme & Logo",
+ href: `${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/pages.settings.Theme`,
+ props: { instance },
+ },
]);
return (
diff --git a/instances/treasury-devdao.near/widget/pages/stake-delegation/CreateButton.jsx b/instances/treasury-devdao.near/widget/pages/stake-delegation/CreateButton.jsx
index e2a865c9..9c101231 100644
--- a/instances/treasury-devdao.near/widget/pages/stake-delegation/CreateButton.jsx
+++ b/instances/treasury-devdao.near/widget/pages/stake-delegation/CreateButton.jsx
@@ -1,3 +1,11 @@
+const { StakeIcon, UnstakeIcon, WithdrawIcon } = VM.require(
+ "${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/components.Icons"
+) || {
+ StakeIcon: () => <>>,
+ UnstakeIcon: () => <>>,
+ WithdrawIcon: () => <>>,
+};
+
const { hasPermission } = VM.require(
"${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/lib.common"
) || {
@@ -44,11 +52,15 @@ function toggleWithdrawPage() {
setShowWithdrawRequest((prev) => !prev);
}
+const toggleDropdown = () => {
+ setCreateBtnOpen((prev) => !prev);
+};
+
const CreateBtn = () => {
const btnOptions = [
{
label: "Stake",
- icon: "${REPL_STAKE_ICON}",
+ icon: ,
value: createBtnOption.STAKE,
onClick: () => {
setShowUnStakeRequest(false);
@@ -58,7 +70,7 @@ const CreateBtn = () => {
},
{
label: "Unstake",
- icon: "${REPL_UNSTAKE_ICON}",
+ icon: ,
value: createBtnOption.UNSTAKE,
onClick: () => {
setShowStakeRequest(false);
@@ -68,7 +80,7 @@ const CreateBtn = () => {
},
{
label: "Withdraw",
- icon: "${REPL_WITHDRAW_ICON}",
+ icon: ,
value: createBtnOption.WITHDRAW,
onClick: () => {
setShowWithdrawRequest(true);
@@ -77,10 +89,6 @@ const CreateBtn = () => {
},
];
- const toggleDropdown = () => {
- setCreateBtnOpen((prev) => !prev);
- };
-
const DropdowntBtnContainer = styled.div`
font-size: 13px;
min-width: 150px;
@@ -90,11 +98,9 @@ const CreateBtn = () => {
}
.select-header {
- color:white;
display: flex;
justify-content: space-between;
cursor: pointer;
- background-color: var(--theme-color);
border-radius: 5px;
}
@@ -108,8 +114,9 @@ const CreateBtn = () => {
top: 100%;
left: 0;
width: 100%;
- border: 1px solid #ccc;
- background-color: #fff;
+ border: 1px solid var(--border-color);
+ background-color: var(--bg-page-color) !important;
+ color: var(--text-color) !important;
padding: 0.5rem;
z-index: 99;
font-size: 13px;
@@ -122,6 +129,7 @@ const CreateBtn = () => {
display: block;
opacity: 1;
transform: translateY(0);
+ z-index:1000;
}
}
@@ -138,16 +146,16 @@ const CreateBtn = () => {
}
.option {
+ color: var(--text-color) !important;
margin-block: 5px;
padding: 10px;
cursor: pointer;
- border-bottom: 1px solid #f0f0f0;
+ border-bottom: 1px solid var(--border-color);
transition: background-color 0.3s ease;
- border-radius: 0.375rem !important;
}
.option:hover {
- background-color: #f0f0f0; /* Custom hover effect color */
+ background-color: var(--bs-dropdown-link-hover-bg);
}
.option:last-child {
@@ -155,7 +163,7 @@ const CreateBtn = () => {
}
.selected {
- background-color: #f0f0f0;
+ background-color: var(--grey-04);
}
.disabled {
@@ -183,18 +191,20 @@ const CreateBtn = () => {
tabIndex="0"
onBlur={() => setCreateBtnOpen(false)}
>
-
+
Create Request
{
>
{btnOptions.map((option) => (
-
-
+
+ {option.icon}
{option.label}
@@ -277,7 +287,17 @@ return (
className="d-flex gap-2 align-items-center"
style={{ paddingBottom: "7px" }}
>
- {hasCreatePermission &&
}
+ {hasCreatePermission && (
+
+ )}
<>> };
+const { TransactionLoader } = VM.require(
+ `${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/components.TransactionLoader`
+) || { TransactionLoader: () => <>> };
const { encodeToMarkdown } = VM.require(
"${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/lib.common"
);
@@ -55,6 +60,7 @@ const [lockupStakedPoolsWithBalance, setLockupStakedPoolsWithBalance] =
useState(null);
const [lockupStakedPoolId, setLockupStakedPoolId] = useState(null);
const [lockupAlreadyStaked, setLockupAlreadyStaked] = useState(false);
+const [showErrorToast, setShowErrorToast] = useState(false);
function formatNearAmount(amount) {
return Big(amount ?? "0")
@@ -124,19 +130,35 @@ function cleanInputs() {
useEffect(() => {
if (isTxnCreated) {
+ let checkTxnTimeout = null;
+ let errorTimeout = null;
+
const checkForNewProposal = () => {
getLastProposalId().then((id) => {
if (lastProposalId !== id) {
cleanInputs();
onCloseCanvas();
refreshData();
+ clearTimeout(errorTimeout);
setTxnCreated(false);
} else {
- setTimeout(() => checkForNewProposal(), 1000);
+ checkTxnTimeout = setTimeout(() => checkForNewProposal(), 1000);
}
});
};
checkForNewProposal();
+
+ // if in 20 seconds there is no change, show error condition
+ errorTimeout = setTimeout(() => {
+ setShowErrorToast(true);
+ setTxnCreated(false);
+ clearTimeout(checkTxnTimeout);
+ }, 20000);
+
+ return () => {
+ clearTimeout(checkTxnTimeout);
+ clearTimeout(errorTimeout);
+ };
}
}, [isTxnCreated]);
@@ -152,7 +174,7 @@ const BalanceDisplay = ({ label, balance, tooltipInfo, noBorder }) => {
placement="top"
overlay={{tooltipInfo} }
>
-
+
@@ -335,59 +357,11 @@ function onSubmitClick() {
const Container = styled.div`
font-size: 14px;
- .text-grey {
- color: #b9b9b9 !important;
- }
-
- .text-grey a {
- color: inherit !important;
- }
-
label {
font-weight: 600;
margin-bottom: 3px;
font-size: 15px;
}
-
- .p-2 {
- padding: 0px !important;
- }
-
- .theme-btn {
- background: var(--theme-color) !important;
- color: white;
- }
-
- .primary-text-color a {
- color: var(--theme-color) !important;
- }
-
- .btn:hover {
- color: black !important;
- }
-
- .text-sm {
- font-size: 13px;
- }
-
- .use-max-bg {
- background-color: #ecf8fb;
- color: #1d62a8;
- cursor: pointer;
- }
-
- .text-dark-grey {
- color: rgba(85, 85, 85, 1) !important;
- }
-
- .bg-validator-info {
- background: rgba(0, 16, 61, 0.06);
- color: #1b1b18;
- padding-inline: 0.8rem;
- padding-block: 0.5rem;
- font-weight: 500;
- font-size: 13px;
- }
`;
useEffect(() => {
@@ -425,6 +399,11 @@ useEffect(() => {
return (
+ setShowErrorToast(false)}
+ />
- ),
+ prefix: ,
},
}}
/>
@@ -616,7 +590,8 @@ return (
src={`${REPL_DEVHUB}/widget/devhub.components.molecule.Button`}
props={{
classNames: { root: "theme-btn" },
- disabled: !validatorAccount || !amount || amountError,
+ disabled:
+ !validatorAccount || !amount || amountError || isTxnCreated,
label: "Submit",
onClick: onSubmitClick,
loading: isTxnCreated,
diff --git a/instances/treasury-devdao.near/widget/pages/stake-delegation/CreateUnstakeRequest.jsx b/instances/treasury-devdao.near/widget/pages/stake-delegation/CreateUnstakeRequest.jsx
index e7d90095..ff06a336 100644
--- a/instances/treasury-devdao.near/widget/pages/stake-delegation/CreateUnstakeRequest.jsx
+++ b/instances/treasury-devdao.near/widget/pages/stake-delegation/CreateUnstakeRequest.jsx
@@ -1,5 +1,12 @@
const { getNearBalances, LOCKUP_MIN_BALANCE_FOR_STORAGE, TooltipText } =
VM.require("${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/lib.common");
+const { TransactionLoader } = VM.require(
+ `${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/components.TransactionLoader`
+) || { TransactionLoader: () => <>> };
+
+const { NearToken } = VM.require(
+ "${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/components.Icons"
+) || { NearToken: () => <>> };
const { href } = VM.require("${REPL_DEVHUB}/widget/core.lib.url") || {
href: () => {},
@@ -59,6 +66,7 @@ const [lockupStakedPoolsWithBalance, setLockupStakedPoolsWithBalance] =
const [showWarning, setShowWarning] = useState(false);
const [lockupStakedPoolId, setLockupStakedPoolId] = useState(null);
+const [showErrorToast, setShowErrorToast] = useState(false);
function formatNearAmount(amount) {
return Big(amount ?? "0")
@@ -180,19 +188,35 @@ function cleanInputs() {
useEffect(() => {
if (isTxnCreated) {
+ let checkTxnTimeout = null;
+ let errorTimeout = null;
+
const checkForNewProposal = () => {
getLastProposalId().then((id) => {
if (lastProposalId !== id) {
cleanInputs();
onCloseCanvas();
refreshData();
+ clearTimeout(errorTimeout);
setTxnCreated(false);
} else {
- setTimeout(() => checkForNewProposal(), 1000);
+ checkTxnTimeout = setTimeout(() => checkForNewProposal(), 1000);
}
});
};
checkForNewProposal();
+
+ // if in 20 seconds there is no change, show error condition
+ errorTimeout = setTimeout(() => {
+ setShowErrorToast(true);
+ setTxnCreated(false);
+ clearTimeout(checkTxnTimeout);
+ }, 20000);
+
+ return () => {
+ clearTimeout(checkTxnTimeout);
+ clearTimeout(errorTimeout);
+ };
}
}, [isTxnCreated]);
@@ -208,7 +232,7 @@ const BalanceDisplay = ({ label, balance, tooltipInfo, noBorder }) => {
placement="top"
overlay={{tooltipInfo} }
>
-
+
@@ -342,14 +366,6 @@ function onSubmitClick() {
const Container = styled.div`
font-size: 14px;
- .text-grey {
- color: #b9b9b9 !important;
- }
-
- .text-grey a {
- color: inherit !important;
- }
-
label {
font-weight: 600;
margin-bottom: 3px;
@@ -359,51 +375,6 @@ const Container = styled.div`
.p-2 {
padding: 0px !important;
}
-
- .theme-btn {
- background: var(--theme-color) !important;
- color: white;
- }
-
- .primary-text-color a {
- color: var(--theme-color) !important;
- }
-
- .btn:hover {
- color: black !important;
- }
-
- .text-sm {
- font-size: 13px;
- }
-
- .use-max-bg {
- background-color: #ecf8fb;
- color: #1d62a8;
- cursor: pointer;
- }
-
- .text-dark-grey {
- color: rgba(85, 85, 85, 1) !important;
- }
-
- .bg-validator-info {
- background: rgba(0, 16, 61, 0.06);
- color: #1b1b18;
- padding-inline: 0.8rem;
- padding-block: 0.5rem;
- font-weight: 500;
- font-size: 13px;
- }
-
- .bg-validator-warning {
- background: rgba(255, 158, 0, 0.1);
- color: rgba(177, 113, 8, 1);
- padding-inline: 0.8rem;
- padding-block: 0.5rem;
- font-weight: 500;
- font-size: 13px;
- }
`;
useEffect(() => {
@@ -437,6 +408,11 @@ useEffect(() => {
return (
+ setShowErrorToast(false)}
+ />
- ),
+ prefix: ,
},
}}
/>
{validatorAccount && (
-
+
Available to unstake:
{formatNearAmount(
@@ -631,7 +602,8 @@ return (
src={`${REPL_DEVHUB}/widget/devhub.components.molecule.Button`}
props={{
classNames: { root: "theme-btn" },
- disabled: !validatorAccount || !amount || amountError,
+ disabled:
+ !validatorAccount || !amount || amountError || isTxnCreated,
label: "Submit",
onClick: onSubmitClick,
loading: isTxnCreated,
diff --git a/instances/treasury-devdao.near/widget/pages/stake-delegation/CreateWithdrawRequest.jsx b/instances/treasury-devdao.near/widget/pages/stake-delegation/CreateWithdrawRequest.jsx
index 5f7f7d59..1e6e14ff 100644
--- a/instances/treasury-devdao.near/widget/pages/stake-delegation/CreateWithdrawRequest.jsx
+++ b/instances/treasury-devdao.near/widget/pages/stake-delegation/CreateWithdrawRequest.jsx
@@ -1,5 +1,8 @@
const { getNearBalances, LOCKUP_MIN_BALANCE_FOR_STORAGE, TooltipText } =
VM.require("${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/lib.common");
+const { TransactionLoader } = VM.require(
+ `${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/components.TransactionLoader`
+) || { TransactionLoader: () => <>> };
const instance = props.instance;
const onCloseCanvas = props.onCloseCanvas ?? (() => {});
@@ -55,6 +58,7 @@ const [lockupStakedPoolsWithBalance, setLockupStakedPoolsWithBalance] =
const [hasUnstakedAssets, setHasUnstakedAssets] = useState(true);
const [isReadyToWithdraw, setIsReadyToWithdraw] = useState(true);
const [showLoader, setShowLoader] = useState(true);
+const [showErrorToast, setShowErrorToast] = useState(false);
function formatNearAmount(amount) {
return Big(amount ?? "0")
@@ -114,18 +118,35 @@ useEffect(() => {
useEffect(() => {
if (isTxnCreated) {
+ let checkTxnTimeout = null;
+ let errorTimeout = null;
+
const checkForNewProposal = () => {
getLastProposalId().then((id) => {
if (lastProposalId !== id) {
+ cleanInputs();
onCloseCanvas();
refreshData();
+ clearTimeout(errorTimeout);
setTxnCreated(false);
} else {
- setTimeout(() => checkForNewProposal(), 1000);
+ checkTxnTimeout = setTimeout(() => checkForNewProposal(), 1000);
}
});
};
checkForNewProposal();
+
+ // if in 20 seconds there is no change, show error condition
+ errorTimeout = setTimeout(() => {
+ setShowErrorToast(true);
+ setTxnCreated(false);
+ clearTimeout(checkTxnTimeout);
+ }, 20000);
+
+ return () => {
+ clearTimeout(checkTxnTimeout);
+ clearTimeout(errorTimeout);
+ };
}
}, [isTxnCreated]);
@@ -141,7 +162,7 @@ const BalanceDisplay = ({ label, balance, tooltipInfo, noBorder }) => {
placement="top"
overlay={{tooltipInfo} }
>
-
+
@@ -302,14 +323,14 @@ const Pools = () => {
}
>
- {fee}% Fee
+ {fee}% Fee
Active
{pool_id}
-
+
Available for withdrawal:{" "}
@@ -336,14 +357,6 @@ const Pools = () => {
const Container = styled.div`
font-size: 14px;
- .text-grey {
- color: #b9b9b9 !important;
- }
-
- .text-grey a {
- color: inherit !important;
- }
-
label {
font-weight: 600;
margin-bottom: 3px;
@@ -354,51 +367,6 @@ const Container = styled.div`
padding: 0px !important;
}
- .theme-btn {
- background: var(--theme-color) !important;
- color: white;
- }
-
- .primary-text-color a {
- color: var(--theme-color) !important;
- }
-
- .btn:hover {
- color: black !important;
- }
-
- .text-sm {
- font-size: 13px;
- }
-
- .use-max-bg {
- background-color: #ecf8fb;
- color: #1d62a8;
- cursor: pointer;
- }
-
- .text-dark-grey {
- color: rgba(85, 85, 85, 1) !important;
- }
-
- .bg-validator-info {
- background: rgba(0, 16, 61, 0.06);
- color: #1b1b18;
- padding-inline: 0.8rem;
- padding-block: 0.5rem;
- font-weight: 500;
- font-size: 13px;
- }
-
- .bg-withdraw-warning {
- background: rgba(255, 158, 0, 0.1);
- color: rgba(177, 113, 8, 1);
- padding-inline: 0.8rem;
- padding-block: 0.5rem;
- font-weight: 500;
- font-size: 13px;
- }
-
.text-green {
color: #34c759;
}
@@ -410,6 +378,11 @@ const Container = styled.div`
return (
+ setShowErrorToast(false)}
+ />
{
});
}, [currentPage, rowsPerPage]);
-const policy = Near.view(treasuryDaoID, "get_policy", {});
+const policy = treasuryDaoID
+ ? Near.view(treasuryDaoID, "get_policy", {})
+ : null;
const functionCallApproversGroup = getApproversAndThreshold(
treasuryDaoID,
diff --git a/instances/treasury-devdao.near/widget/pages/stake-delegation/PendingRequests.jsx b/instances/treasury-devdao.near/widget/pages/stake-delegation/PendingRequests.jsx
index 83637da2..277397f7 100644
--- a/instances/treasury-devdao.near/widget/pages/stake-delegation/PendingRequests.jsx
+++ b/instances/treasury-devdao.near/widget/pages/stake-delegation/PendingRequests.jsx
@@ -64,7 +64,9 @@ useEffect(() => {
fetchProposals();
}, [refreshTableData]);
-const policy = Near.view(treasuryDaoID, "get_policy", {});
+const policy = treasuryDaoID
+ ? Near.view(treasuryDaoID, "get_policy", {})
+ : null;
const functionCallApproversGroup = getApproversAndThreshold(
treasuryDaoID,
diff --git a/instances/treasury-devdao.near/widget/pages/stake-delegation/SettingsDropdown.jsx b/instances/treasury-devdao.near/widget/pages/stake-delegation/SettingsDropdown.jsx
index 986874b2..cc4be62b 100644
--- a/instances/treasury-devdao.near/widget/pages/stake-delegation/SettingsDropdown.jsx
+++ b/instances/treasury-devdao.near/widget/pages/stake-delegation/SettingsDropdown.jsx
@@ -88,36 +88,16 @@ const Container = styled.div`
z-index: 9999;
}
- .dropdown-item.active,
- .dropdown-item:active {
- background-color: #f0f0f0 !important;
- color: black;
- }
-
.custom-select {
position: relative;
}
-
- .cursor-pointer {
- cursor: pointer;
- }
-
- .text-grey {
- color: #b3b3b3;
- }
-
- .text-sm {
- font-size: 13px;
- }
`;
const Item = ({ option }) => {
return (
{option.title}
@@ -144,7 +124,7 @@ return (
{isOpen && (
-
Shown in table
+
Shown in table
{settingsOptions.map((option) => {
if (
!isPendingPage &&
diff --git a/instances/treasury-devdao.near/widget/pages/stake-delegation/Table.jsx b/instances/treasury-devdao.near/widget/pages/stake-delegation/Table.jsx
index 7b69ef77..50e2266a 100644
--- a/instances/treasury-devdao.near/widget/pages/stake-delegation/Table.jsx
+++ b/instances/treasury-devdao.near/widget/pages/stake-delegation/Table.jsx
@@ -81,18 +81,7 @@ const hasDeletePermission = (deleteGroup?.approverAccounts ?? []).includes(
const Container = styled.div`
font-size: 13px;
min-height: 60vh;
- .text-grey {
- color: #b9b9b9 !important;
- }
- .text-size-2 {
- font-size: 15px;
- }
- .text-dark-grey {
- color: #687076;
- }
- .text-grey-100 {
- background-color: #f5f5f5;
- }
+
td {
padding: 0.5rem;
color: inherit;
@@ -100,59 +89,12 @@ const Container = styled.div`
background: inherit;
}
- .max-w-100 {
- max-width: 100%;
- }
-
table {
overflow-x: auto;
}
- .bold {
- font-weight: 500;
- }
-
- .custom-truncate {
- display: -webkit-box;
- -webkit-line-clamp: 3;
- -webkit-box-orient: vertical;
- overflow: hidden;
- text-overflow: ellipsis;
- line-height: 1.5;
- max-height: 4.5em;
- text-align: left;
- }
-
- .display-none {
- display: none;
- }
-
- .text-right {
- text-align: end;
- }
-
- .text-left {
- text-align: left;
- }
- .text-underline {
- text-decoration: underline !important;
- }
-
- .bg-highlight {
- background-color: rgb(185, 185, 185, 0.2);
- }
-
- .toast {
- background: white !important;
- }
-
- .toast-header {
- background-color: #2c3e50 !important;
- color: white !important;
- }
-
.text-warning {
- color: rgba(177, 113, 8, 1) !important;
+ color: var(--other-warning) !important;
}
.markdown-href p {
@@ -162,22 +104,8 @@ const Container = styled.div`
.markdown-href a {
color: inherit !important;
}
-
- .fw-semi-bold {
- font-weight: 500;
- }
`;
-
-const ToastContainer = styled.div`
- a {
- color: black !important;
- text-decoration: underline !important;
- &:hover {
- color: black !important;
- }
- }
-`;
-
+div;
function checkProposalStatus(proposalId) {
Near.asyncView(treasuryDaoID, "get_proposal", {
id: proposalId,
@@ -275,25 +203,34 @@ const ToastStatusContent = () => {
}
return (
- {content}
-
- {showToastStatus !== "InProgress" && showToastStatus !== "Removed" && (
-
- View in History
-
- )}
+
+ {showToastStatus === "Approved" && (
+
+ )}
+
+ {content}
+
+ {showToastStatus !== "InProgress" &&
+ showToastStatus !== "Removed" && (
+
+ View in History
+
+ )}
+
+
);
};
@@ -302,15 +239,18 @@ const VoteSuccessToast = () => {
return showToastStatus &&
(typeof voteProposalId === "number" ||
typeof highlightProposalId === "number") ? (
-
+
Just Now
- setToastStatus(null)}>
+ setToastStatus(null)}
+ >
-
+
) : null;
};
@@ -368,7 +308,7 @@ const ProposalsComponent = () => {
: ""
}
>
- {item.id}
+ {item.id}
{
/>
)}
-
+
{
{lockupContract && (
-
+
{" "}
{isLockup ? "Lockup" : "Sputnik DAO"}
+ ),
children: (
{
{treasuryWallet}
),
+ instance,
}}
/>
@@ -435,14 +381,20 @@ const ProposalsComponent = () => {
src={`${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/pages.stake-delegation.Validator`}
props={{
validatorId: validatorAccount,
+ instance,
}}
/>
-
+
+ ),
children: (
{
{item.proposer}
),
+ instance,
}}
/>
@@ -501,6 +454,7 @@ const ProposalsComponent = () => {
props={{
votes: item.votes,
approversGroup: functionCallApproversGroup?.approverAccounts,
+ instance,
}}
/>
@@ -579,7 +533,7 @@ return (
) : (
-
+
#
Created Date
{!isPendingRequests && Status }
diff --git a/instances/treasury-devdao.near/widget/pages/stake-delegation/Type.jsx b/instances/treasury-devdao.near/widget/pages/stake-delegation/Type.jsx
index a4d5df84..95bf0a17 100644
--- a/instances/treasury-devdao.near/widget/pages/stake-delegation/Type.jsx
+++ b/instances/treasury-devdao.near/widget/pages/stake-delegation/Type.jsx
@@ -1,3 +1,12 @@
+const { StakeIcon, UnstakeIcon, WithdrawIcon, Whitelist } = VM.require(
+ "${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/components.Icons"
+) || {
+ StakeIcon: () => <>>,
+ UnstakeIcon: () => <>>,
+ WithdrawIcon: () => <>>,
+ Whitelist: () => <>>,
+};
+
const type = props.type;
const classes =
@@ -8,7 +17,7 @@ const Badge = () => {
case "unstake": {
return (
-
+
Unstake
);
@@ -17,7 +26,7 @@ const Badge = () => {
case "withdraw_all_from_staking_pool": {
return (
-
+
Withdraw
);
@@ -25,7 +34,7 @@ const Badge = () => {
case "select_staking_pool": {
return (
-
+
Whitelist
);
@@ -33,7 +42,7 @@ const Badge = () => {
default: {
return (
-
+
Stake
);
diff --git a/instances/treasury-devdao.near/widget/pages/stake-delegation/Validator.jsx b/instances/treasury-devdao.near/widget/pages/stake-delegation/Validator.jsx
index 81a9d5cf..fde57b6e 100644
--- a/instances/treasury-devdao.near/widget/pages/stake-delegation/Validator.jsx
+++ b/instances/treasury-devdao.near/widget/pages/stake-delegation/Validator.jsx
@@ -10,14 +10,20 @@ const pikespeakKey = isBosGateway()
if (!pikespeakKey) {
return (
+ ),
children: (
{validatorId}
),
+ instance: props.instance,
}}
/>
);
@@ -44,10 +50,6 @@ const Container = styled.div`
color: #34c759;
}
- .text-grey {
- color: #b9b9b9 !important;
- }
-
.text-sm {
font-size: 12px;
}
@@ -57,7 +59,7 @@ return (
{validatorId}
-
{fee}% Fee
+
{fee}% Fee
{isActive &&
Active
}
diff --git a/instances/treasury-devdao.near/widget/pages/stake-delegation/WalletDropdown.jsx b/instances/treasury-devdao.near/widget/pages/stake-delegation/WalletDropdown.jsx
index 71e6d792..f6f8f396 100644
--- a/instances/treasury-devdao.near/widget/pages/stake-delegation/WalletDropdown.jsx
+++ b/instances/treasury-devdao.near/widget/pages/stake-delegation/WalletDropdown.jsx
@@ -91,7 +91,9 @@ return (
/>
{item.label}
- {item.balance && ${item.balance}
}
+ {item.balance && (
+ ${item.balance}
+ )}
);
},
diff --git a/instances/treasury-infinex.near/widget/app.jsx b/instances/treasury-infinex.near/widget/app.jsx
index 2f617073..4859aafb 100644
--- a/instances/treasury-infinex.near/widget/app.jsx
+++ b/instances/treasury-infinex.near/widget/app.jsx
@@ -11,6 +11,7 @@ const { AppLayout } = VM.require(
) || { AppLayout: () => <>> };
const instance = "${REPL_INSTANCE}";
+const treasuryDaoID = "${REPL_TREASURY}";
const { Theme } = VM.require(`${instance}/widget/config.css`) || {
Theme: () => <>>,
@@ -95,7 +96,12 @@ function Page() {
return (
-
+
diff --git a/instances/treasury-infinex.near/widget/config/css.jsx b/instances/treasury-infinex.near/widget/config/css.jsx
index 77e83bdb..f7a131f3 100644
--- a/instances/treasury-infinex.near/widget/config/css.jsx
+++ b/instances/treasury-infinex.near/widget/config/css.jsx
@@ -1,70 +1,5 @@
const Theme = styled.div`
- --theme-color: rgb(21 22 25);
- --theme-bg-color: #f4f4f4;
- --text-color: white;
- --link-inactive-color: white;
- --link-active-color: white;
- --border-color: rgba(226, 230, 236, 1);
- --light-grey-color: rgba(185, 185, 185, 1);
- --dark-grey-color: rgba(103, 103, 103, 1);
-
- a {
- text-decoration: none;
- color: var(--link-inactive-color) !important;
- &.active {
- color: var(--link-active-color) !important;
- font-weight: 700 !important;
- }
-
- &:hover {
- text-decoration: none;
- color: var(--link-active-color) !important;
- font-weight: 700 !important;
- }
- }
-
- button {
- &.primary {
- background: var(--theme-color);
- color: white;
- border: none !important;
- padding-block: 0.7rem !important;
- }
- }
-
- .text-light-grey {
- color: var(--light-grey-color);
- }
-
- .text-muted {
- color: var(--dark-grey-color);
- }
-
- .text-md {
- font-size: 15px;
- }
-
- .primary-text-color {
- color: var(--theme-color);
- }
-
- .btn-outline-plain {
- padding-block: 8px;
- padding-inline: 10px;
- border-radius: 0.375rem;
- border: 1.5px solid #e2e6ec;
- background-color: white;
- color: black;
-
- &:hover {
- background-color: white;
- color: black;
- }
- }
-
- .fill-accent {
- fill: #fe6f39;
- }
+ --theme-color: #f76218;
`;
return { Theme };
diff --git a/instances/treasury-templar.near/widget/app.jsx b/instances/treasury-templar.near/widget/app.jsx
index a9233012..d2fcc990 100644
--- a/instances/treasury-templar.near/widget/app.jsx
+++ b/instances/treasury-templar.near/widget/app.jsx
@@ -11,6 +11,7 @@ const { AppLayout } = VM.require(
) || { AppLayout: () => <>> };
const instance = "${REPL_INSTANCE}";
+const treasuryDaoID = "${REPL_TREASURY}";
const { Theme } = VM.require(`${instance}/widget/config.css`) || {
Theme: () => <>>,
@@ -84,7 +85,12 @@ function Page() {
return (
-
+
diff --git a/instances/treasury-templar.near/widget/config/css.jsx b/instances/treasury-templar.near/widget/config/css.jsx
index 75e8c00f..42f7499e 100644
--- a/instances/treasury-templar.near/widget/config/css.jsx
+++ b/instances/treasury-templar.near/widget/config/css.jsx
@@ -1,70 +1,5 @@
const Theme = styled.div`
--theme-color: #8942d9;
- --theme-bg-color: #f4f4f4;
- --text-color: white;
- --link-inactive-color: white;
- --link-active-color: white;
- --border-color: rgba(226, 230, 236, 1);
- --light-grey-color: rgba(185, 185, 185, 1);
- --dark-grey-color: rgba(103, 103, 103, 1);
-
- a {
- text-decoration: none;
- color: var(--link-inactive-color) !important;
- &.active {
- color: var(--link-active-color) !important;
- font-weight: 700 !important;
- }
-
- &:hover {
- text-decoration: none;
- color: var(--link-active-color) !important;
- font-weight: 700 !important;
- }
- }
-
- button {
- &.primary {
- background: var(--theme-color);
- color: white;
- border: none !important;
- padding-block: 0.7rem !important;
- }
- }
-
- .text-light-grey {
- color: var(--light-grey-color);
- }
-
- .text-muted {
- color: var(--dark-grey-color);
- }
-
- .text-md {
- font-size: 15px;
- }
-
- .primary-text-color {
- color: var(--theme-color);
- }
-
- .btn-outline-plain {
- padding-block: 8px;
- padding-inline: 10px;
- border-radius: 0.375rem;
- border: 1.5px solid #e2e6ec;
- background-color: white;
- color: black;
-
- &:hover {
- background-color: white;
- color: black;
- }
- }
-
- .fill-accent {
- fill: #fe6f39;
- }
`;
return { Theme };
diff --git a/instances/treasury-testing-infinex.near/widget/app.jsx b/instances/treasury-testing-infinex.near/widget/app.jsx
index 2f617073..4859aafb 100644
--- a/instances/treasury-testing-infinex.near/widget/app.jsx
+++ b/instances/treasury-testing-infinex.near/widget/app.jsx
@@ -11,6 +11,7 @@ const { AppLayout } = VM.require(
) || { AppLayout: () => <>> };
const instance = "${REPL_INSTANCE}";
+const treasuryDaoID = "${REPL_TREASURY}";
const { Theme } = VM.require(`${instance}/widget/config.css`) || {
Theme: () => <>>,
@@ -95,7 +96,12 @@ function Page() {
return (
-
+
diff --git a/instances/treasury-testing-infinex.near/widget/config/css.jsx b/instances/treasury-testing-infinex.near/widget/config/css.jsx
index 77e83bdb..f7a131f3 100644
--- a/instances/treasury-testing-infinex.near/widget/config/css.jsx
+++ b/instances/treasury-testing-infinex.near/widget/config/css.jsx
@@ -1,70 +1,5 @@
const Theme = styled.div`
- --theme-color: rgb(21 22 25);
- --theme-bg-color: #f4f4f4;
- --text-color: white;
- --link-inactive-color: white;
- --link-active-color: white;
- --border-color: rgba(226, 230, 236, 1);
- --light-grey-color: rgba(185, 185, 185, 1);
- --dark-grey-color: rgba(103, 103, 103, 1);
-
- a {
- text-decoration: none;
- color: var(--link-inactive-color) !important;
- &.active {
- color: var(--link-active-color) !important;
- font-weight: 700 !important;
- }
-
- &:hover {
- text-decoration: none;
- color: var(--link-active-color) !important;
- font-weight: 700 !important;
- }
- }
-
- button {
- &.primary {
- background: var(--theme-color);
- color: white;
- border: none !important;
- padding-block: 0.7rem !important;
- }
- }
-
- .text-light-grey {
- color: var(--light-grey-color);
- }
-
- .text-muted {
- color: var(--dark-grey-color);
- }
-
- .text-md {
- font-size: 15px;
- }
-
- .primary-text-color {
- color: var(--theme-color);
- }
-
- .btn-outline-plain {
- padding-block: 8px;
- padding-inline: 10px;
- border-radius: 0.375rem;
- border: 1.5px solid #e2e6ec;
- background-color: white;
- color: black;
-
- &:hover {
- background-color: white;
- color: black;
- }
- }
-
- .fill-accent {
- fill: #fe6f39;
- }
+ --theme-color: #f76218;
`;
return { Theme };
diff --git a/instances/treasury-testing.near/widget/app.jsx b/instances/treasury-testing.near/widget/app.jsx
index a9233012..d2fcc990 100644
--- a/instances/treasury-testing.near/widget/app.jsx
+++ b/instances/treasury-testing.near/widget/app.jsx
@@ -11,6 +11,7 @@ const { AppLayout } = VM.require(
) || { AppLayout: () => <>> };
const instance = "${REPL_INSTANCE}";
+const treasuryDaoID = "${REPL_TREASURY}";
const { Theme } = VM.require(`${instance}/widget/config.css`) || {
Theme: () => <>>,
@@ -84,7 +85,12 @@ function Page() {
return (
-
+
diff --git a/instances/treasury-testing.near/widget/config/css.jsx b/instances/treasury-testing.near/widget/config/css.jsx
index 5446ba50..aae9f279 100644
--- a/instances/treasury-testing.near/widget/config/css.jsx
+++ b/instances/treasury-testing.near/widget/config/css.jsx
@@ -1,66 +1,5 @@
const Theme = styled.div`
--theme-color: #05a36e;
- --theme-bg-color: #f4f4f4;
- --text-color: white;
- --link-inactive-color: white;
- --link-active-color: white;
- --border-color: rgba(226, 230, 236, 1);
- --light-grey-color: rgba(185, 185, 185, 1);
- --dark-grey-color: rgba(103, 103, 103, 1);
-
- a {
- text-decoration: none;
- color: var(--link-inactive-color) !important;
- &.active {
- color: var(--link-active-color) !important;
- font-weight: 700 !important;
- }
-
- &:hover {
- text-decoration: none;
- color: var(--link-active-color) !important;
- font-weight: 700 !important;
- }
- }
-
- button {
- &.primary {
- background: var(--theme-color);
- color: var(--text-color);
- border: none !important;
- padding-block: 0.7rem !important;
- }
- }
-
- .text-light-grey {
- color: var(--light-grey-color);
- }
-
- .text-muted {
- color: var(--dark-grey-color);
- }
-
- .text-md {
- font-size: 15px;
- }
-
- .primary-text-color {
- color: var(--theme-color);
- }
-
- .btn-outline-plain {
- padding-block: 8px;
- padding-inline: 10px;
- border-radius: 0.375rem;
- border: 1.5px solid #e2e6ec;
- background-color: white;
- color: black;
-
- &:hover {
- background-color: white;
- color: black;
- }
- }
`;
return { Theme };
diff --git a/instances/treasury-testing.near/widget/config/data.jsx b/instances/treasury-testing.near/widget/config/data.jsx
index ce1fcc33..186bc703 100644
--- a/instances/treasury-testing.near/widget/config/data.jsx
+++ b/instances/treasury-testing.near/widget/config/data.jsx
@@ -28,7 +28,6 @@ return {
showKYC: true,
showReferenceProposal: true,
isTesting: true,
-
logo: (
{
await page.unrouteAll({ behavior: "ignoreErrors" });
});
-test.describe("admin connected", function () {
+test.describe("User is not logged in", function () {
+ test("should not see 'Create Request' action", async ({
+ page,
+ instanceAccount,
+ }) => {
+ await page.goto(`/${instanceAccount}/widget/app?page=payments`);
+ await expect(page.getByText("Pending Requests")).toBeVisible();
+ await expect(
+ page.getByRole("button", {
+ name: "Create Request",
+ })
+ ).toBeHidden();
+ });
+});
+
+test.describe("User is logged in", function () {
+ const signedUser = "theori.near";
test.use({
contextOptions: {
permissions: ["clipboard-read", "clipboard-write"],
@@ -128,6 +146,55 @@ test.describe("admin connected", function () {
storageState: "playwright-tests/storage-states/wallet-connected-admin.json",
});
+ test("low account balance should show warning modal, and allow action ", async ({
+ page,
+ instanceAccount,
+ }) => {
+ test.setTimeout(60_000);
+ await mockNearBalances({
+ page,
+ accountId: signedUser,
+ balance: BigInt(0.6 * 10 ** 24).toString(),
+ storage: 8,
+ });
+ await page.goto(`/${instanceAccount}/widget/app?page=payments`);
+ await expect(
+ page.getByText(
+ "Please add more NEAR to your account soon to avoid any issues completing actions on your treasury"
+ )
+ ).toBeVisible();
+ });
+
+ test("insufficient account balance should show warning modal, disallow action ", async ({
+ page,
+ instanceAccount,
+ }) => {
+ test.setTimeout(60_000);
+ await updateDaoPolicyMembers({ page });
+ await mockNearBalances({
+ page,
+ accountId: signedUser,
+ balance: InsufficientBalance,
+ storage: 8,
+ });
+ await page.goto(`/${instanceAccount}/widget/app?page=payments`);
+ await expect(
+ page.getByText(
+ "Hey Ori, you don't have enough NEAR to complete actions on your treasury."
+ )
+ ).toBeVisible();
+ await page
+ .getByRole("button", {
+ name: "Create Request",
+ })
+ .click();
+ await expect(
+ page
+ .getByText("Please add more funds to your account and try again")
+ .nth(1)
+ ).toBeVisible();
+ });
+
test("different amount values should not throw any error", async ({
page,
instanceAccount,
@@ -594,6 +661,7 @@ test.describe("admin with function access keys", function () {
expectedTransactionModalObject
);
+ await expect(page.getByText("Processing your request ...")).toBeVisible();
let isTransactionCompleted = false;
let retryCountAfterComplete = 0;
let newProposalId;
diff --git a/playwright-tests/tests/payments/vote-on-request.spec.js b/playwright-tests/tests/payments/vote-on-request.spec.js
index 22bbf5d5..77a649ff 100644
--- a/playwright-tests/tests/payments/vote-on-request.spec.js
+++ b/playwright-tests/tests/payments/vote-on-request.spec.js
@@ -2,13 +2,18 @@ import { expect } from "@playwright/test";
import { test } from "../../util/test.js";
import { mockTransactionSubmitRPCResponses } from "../../util/transaction";
-import { mockRpcRequest, updateDaoPolicyMembers } from "../../util/rpcmock";
+import {
+ mockNearBalances,
+ mockRpcRequest,
+ updateDaoPolicyMembers,
+} from "../../util/rpcmock";
import { setDontAskAgainCacheValues } from "../../util/cache";
import { mockPikespeakFTTokensResponse } from "../../util/pikespeak.js";
import {
CurrentTimestampInNanoseconds,
TransferProposalData,
} from "../../util/inventory.js";
+import { InsufficientBalance } from "../../util/lib.js";
async function mockWithFTBalance({ page, daoAccount, isSufficient }) {
await page.route(
@@ -153,7 +158,41 @@ test.describe("don't ask again", function () {
storageState:
"playwright-tests/storage-states/wallet-connected-admin-with-accesskey.json",
});
- test("should throw insufficient balance error", async ({
+
+ test("should throw insufficient signed in account balance error", async ({
+ page,
+ instanceAccount,
+ daoAccount,
+ }) => {
+ test.setTimeout(60_000);
+ await updateDaoPolicyMembers({ page });
+ await mockNearBalances({
+ page,
+ accountId: "theori.near",
+ balance: InsufficientBalance,
+ storage: 8,
+ });
+ await voteOnProposal({
+ page,
+ daoAccount,
+ instanceAccount,
+ voteStatus: "Approved",
+ vote: "Approve",
+ });
+ const approveButton = page
+ .getByRole("button", {
+ name: "Approve",
+ })
+ .first();
+ await expect(approveButton).toBeEnabled({ timeout: 30_000 });
+ await approveButton.click();
+ await expect(
+ page
+ .getByText("Please add more funds to your account and try again")
+ .nth(1)
+ ).toBeVisible();
+ });
+ test("should throw insufficient dao account balance error", async ({
page,
instanceAccount,
daoAccount,
@@ -211,6 +250,7 @@ test.describe("don't ask again", function () {
await expect(approveButton).toBeEnabled({ timeout: 30_000 });
await approveButton.click();
await page.getByRole("button", { name: "Confirm" }).click();
+ await expect(page.getByText("Processing your request ...")).toBeVisible();
await expect(approveButton).toBeDisabled();
const transaction_toast = page.getByText(
@@ -266,6 +306,7 @@ test.describe("don't ask again", function () {
await expect(rejectButton).toBeEnabled({ timeout: 10000 });
await rejectButton.click();
await page.getByRole("button", { name: "Confirm" }).click();
+ await expect(page.getByText("Processing your request ...")).toBeVisible();
await expect(rejectButton).toBeDisabled();
const transaction_toast = page.getByText(
@@ -321,6 +362,8 @@ test.describe("don't ask again", function () {
page.getByText("Do you really want to delete this request?")
).toBeVisible();
await page.getByRole("button", { name: "Confirm" }).click();
+ await expect(page.getByText("Processing your request ...")).toBeVisible();
+
await expect(deleteButton).toBeDisabled();
const transaction_toast = page.getByText(
diff --git a/playwright-tests/tests/settings/assets/invalid.png b/playwright-tests/tests/settings/assets/invalid.png
new file mode 100644
index 00000000..7b9ab526
Binary files /dev/null and b/playwright-tests/tests/settings/assets/invalid.png differ
diff --git a/playwright-tests/tests/settings/assets/valid.jpg b/playwright-tests/tests/settings/assets/valid.jpg
new file mode 100644
index 00000000..dec00ae5
Binary files /dev/null and b/playwright-tests/tests/settings/assets/valid.jpg differ
diff --git a/playwright-tests/tests/settings/create-member-request.spec.js b/playwright-tests/tests/settings/create-member-request.spec.js
index a97de507..e2256b87 100644
--- a/playwright-tests/tests/settings/create-member-request.spec.js
+++ b/playwright-tests/tests/settings/create-member-request.spec.js
@@ -5,9 +5,13 @@ import {
getTransactionModalObject,
mockTransactionSubmitRPCResponses,
} from "../../util/transaction.js";
-import { mockRpcRequest, updateDaoPolicyMembers } from "../../util/rpcmock.js";
+import {
+ mockNearBalances,
+ mockRpcRequest,
+ updateDaoPolicyMembers,
+} from "../../util/rpcmock.js";
import { mockInventory } from "../../util/inventory.js";
-import { encodeToMarkdown } from "../../util/lib.js";
+import { InsufficientBalance, encodeToMarkdown } from "../../util/lib.js";
const lastProposalId = 3;
@@ -32,7 +36,9 @@ async function updateLastProposalId(page) {
async function navigateToMembersPage({ page, instanceAccount }) {
await page.goto(`/${instanceAccount}/widget/app?page=settings`);
+ await page.waitForTimeout(5_000);
await page.getByText("Members").click();
+ await expect(page.getByText("All Members")).toBeVisible({ timeout: 10_000 });
}
async function openAddMemberForm({ page }) {
@@ -52,23 +58,59 @@ test.afterEach(async ({ page }, testInfo) => {
await page.unrouteAll({ behavior: "ignoreErrors" });
});
-test.describe("admin connected", function () {
+test.describe("User is not logged in", function () {
+ test.beforeEach(async ({ page, instanceAccount }) => {
+ await navigateToMembersPage({ page, instanceAccount });
+ });
+
+ test("should not be able to add member or see actions", async ({ page }) => {
+ await expect(
+ page.getByRole("button", {
+ name: "New Member",
+ })
+ ).toBeHidden();
+ await expect(page.getByText("Actions", { exact: true })).toBeHidden();
+ });
+});
+
+test.describe("User is logged in", function () {
test.use({
storageState: "playwright-tests/storage-states/wallet-connected-admin.json",
});
- test.beforeEach(async ({ page, daoAccount, instanceAccount }) => {
+ test.beforeEach(async ({ page, daoAccount, instanceAccount }, testInfo) => {
await mockInventory({ page, account: daoAccount });
await updateDaoPolicyMembers({ page });
await updateLastProposalId(page);
+ if (testInfo.title.includes("insufficient account balance")) {
+ await mockNearBalances({
+ page,
+ accountId: "theori.near",
+ balance: InsufficientBalance,
+ storage: 8,
+ });
+ }
await navigateToMembersPage({ page, instanceAccount });
});
- test("should show members of the DAO", async ({
+ test("insufficient account balance should show warning modal, disallow action ", async ({
page,
- instanceAccount,
- daoAccount,
}) => {
+ test.setTimeout(60_000);
+ await expect(
+ page.getByText(
+ "Hey Ori, you don't have enough NEAR to complete actions on your treasury."
+ )
+ ).toBeVisible();
+ await page.getByRole("button", { name: " New Member" }).click();
+ await expect(
+ page
+ .getByText("Please add more funds to your account and try again")
+ .nth(1)
+ ).toBeVisible();
+ });
+
+ test("should show members of the DAO", async ({ page }) => {
test.setTimeout(60_000);
await expect(page.getByText("Megha", { exact: true })).toBeVisible();
});
@@ -148,7 +190,7 @@ test.describe("admin connected", function () {
const submitBtn = await page.locator("button", { hasText: "Submit" });
await submitBtn.scrollIntoViewIfNeeded({ timeout: 10_000 });
await submitBtn.click();
-
+ await expect(page.getByText("Processing your request ...")).toBeVisible();
const description = {
title: "Update policy - Members Permissions",
summary: `theori.near requested to add "${account}" to "${permission}".`,
@@ -425,6 +467,7 @@ test.describe("admin connected", function () {
await expect(submitBtn).toBeAttached({ timeout: 10_000 });
await submitBtn.scrollIntoViewIfNeeded({ timeout: 10_000 });
await submitBtn.click();
+ await expect(page.getByText("Processing your request ...")).toBeVisible();
const description = {
title: "Update policy - Members Permissions",
summary: `theori.near requested to remove "${account}" from "${permission}".`,
@@ -568,6 +611,8 @@ test.describe("admin connected", function () {
page.getByRole("heading", { name: "Are you sure?" })
).toBeVisible();
await page.getByRole("button", { name: "Remove" }).click();
+ await expect(page.getByText("Processing your request ...")).toBeVisible();
+
const description = {
title: "Update policy - Members Permissions",
summary: `theori.near requested to requested to revoke all permissions of "megha19.near".`,
diff --git a/playwright-tests/tests/settings/create-threshold-request.spec.js b/playwright-tests/tests/settings/create-threshold-request.spec.js
index 56130a1b..8a00f76c 100644
--- a/playwright-tests/tests/settings/create-threshold-request.spec.js
+++ b/playwright-tests/tests/settings/create-threshold-request.spec.js
@@ -4,10 +4,11 @@ import { test } from "../../util/test.js";
import { getTransactionModalObject } from "../../util/transaction.js";
import {
getMockedPolicy,
+ mockNearBalances,
mockRpcRequest,
updateDaoPolicyMembers,
} from "../../util/rpcmock.js";
-import { encodeToMarkdown } from "../../util/lib.js";
+import { InsufficientBalance, encodeToMarkdown } from "../../util/lib.js";
const lastProposalId = 3;
@@ -37,7 +38,8 @@ async function checkVotingDropdownChange({ page }) {
)
).toBeVisible();
await expect(page.getByText("Enter Percentage")).toBeVisible();
- await page.getByRole("list").getByText("Number of votes").click();
+ await page.getByTestId("dropdown-btn").click();
+ await page.getByText("Number of votes").click();
await expect(
page.getByText(
"A fixed number of votes is required for a decision to pass."
@@ -51,11 +53,19 @@ test.afterEach(async ({ page }, testInfo) => {
await page.unrouteAll({ behavior: "ignoreErrors" });
});
-test.describe("without login", function () {
+async function navigateToThresholdPage({ page, instanceAccount }) {
+ await page.goto(`/${instanceAccount}/widget/app?page=settings`);
+ await page.waitForTimeout(5_000);
+ await page.getByText("Voting Threshold").click();
+ await expect(page.getByText("Permission Groups")).toBeVisible({
+ timeout: 10_000,
+ });
+}
+
+test.describe("User is not logged in", function () {
test.beforeEach(async ({ page, instanceAccount }) => {
await updateDaoPolicyMembers({ page });
- await page.goto(`/${instanceAccount}/widget/app?page=settings`);
- await page.getByText("Voting Thresholds").click({ timeout: 20_000 });
+ await navigateToThresholdPage({ page, instanceAccount });
});
test("should show members of different roles", async ({ page }) => {
@@ -87,36 +97,63 @@ test.describe("without login", function () {
timeout: 20_000,
});
await expect(page.getByTestId("dropdown-btn")).toBeDisabled();
- await expect(page.getByText("Submit")).toBeHidden();
+ await expect(page.getByText("Submit Request")).toBeHidden();
await expect(page.getByTestId("threshold-input")).toBeDisabled();
});
});
async function fillInput(page, value) {
- const submitBtn = page.getByText("Submit");
+ const submitBtn = page.getByText("Submit Request");
const thresholdInput = page.getByTestId("threshold-input");
await thresholdInput.type(value);
expect(submitBtn).toBeVisible();
}
-test.describe("admin connected", function () {
+test.describe("User is logged in", function () {
test.use({
storageState: "playwright-tests/storage-states/wallet-connected-admin.json",
});
- test.beforeEach(async ({ page, instanceAccount }) => {
+ test.beforeEach(async ({ page, instanceAccount }, testInfo) => {
await updateLastProposalId(page);
await updateDaoPolicyMembers({ page });
- await page.goto(`/${instanceAccount}/widget/app?page=settings`);
- await page.getByText("Voting Thresholds").click({ timeout: 20_000 });
- await expect(page.getByText("Submit")).toBeVisible({
- timeout: 20_000,
- });
- await page.getByTestId("dropdown-btn").click();
+ if (testInfo.title.includes("insufficient account balance")) {
+ await mockNearBalances({
+ page,
+ accountId: "theori.near",
+ balance: InsufficientBalance,
+ storage: 8,
+ });
+ }
+ await navigateToThresholdPage({ page, instanceAccount });
+ });
+
+ test("insufficient account balance should show warning modal, disallow action ", async ({
+ page,
+ }) => {
+ test.setTimeout(60_000);
+
+ await expect(
+ page.getByText(
+ "Hey Ori, you don't have enough NEAR to complete actions on your treasury."
+ )
+ ).toBeVisible();
+ await page.getByTestId("threshold-input").fill("1");
+ await page
+ .getByText("Submit Request", {
+ exact: true,
+ })
+ .click();
+ await expect(
+ page
+ .getByText("Please add more funds to your account and try again")
+ .nth(1)
+ ).toBeVisible();
});
test("should allow only valid input for threshold", async ({ page }) => {
test.setTimeout(60_000);
+ await page.getByTestId("dropdown-btn").click();
await page.getByRole("list").getByText("Number of votes").click();
await fillInput(page, "20097292");
await fillInput(page, "10.323");
@@ -134,7 +171,8 @@ test.describe("admin connected", function () {
page,
}) => {
test.setTimeout(150_000);
- const submitBtn = page.getByText("Submit");
+ const submitBtn = page.getByText("Submit Request");
+ await page.getByTestId("dropdown-btn").click();
await page.getByRole("list").getByText("Number of votes").click();
const thresholdInput = page.getByTestId("threshold-input");
await thresholdInput.fill("20");
@@ -148,6 +186,7 @@ test.describe("admin connected", function () {
)
).toBeVisible();
await page.getByRole("button", { name: "Confirm" }).click();
+ await expect(page.getByText("Processing your request ...")).toBeVisible();
const updatedPolicy = {
weight_kind: "RoleWeight",
quorum: "0",
@@ -172,7 +211,8 @@ test.describe("admin connected", function () {
test("should be able to update policy by percentage", async ({ page }) => {
test.setTimeout(150_000);
- const submitBtn = page.getByText("Submit");
+ const submitBtn = page.getByText("Submit Request");
+ await page.getByTestId("dropdown-btn").click();
await page.getByRole("list").getByText("Percentage of members").click();
const thresholdInput = page.getByTestId("threshold-input");
await thresholdInput.fill("101");
@@ -189,6 +229,8 @@ test.describe("admin connected", function () {
)
).toBeVisible();
await page.getByRole("button", { name: "Confirm" }).click();
+ await expect(page.getByText("Processing your request ...")).toBeVisible();
+
const updatedPolicy = {
weight_kind: "RoleWeight",
quorum: "0",
diff --git a/playwright-tests/tests/settings/theme.spec.js b/playwright-tests/tests/settings/theme.spec.js
new file mode 100644
index 00000000..622e9592
--- /dev/null
+++ b/playwright-tests/tests/settings/theme.spec.js
@@ -0,0 +1,202 @@
+import { expect } from "@playwright/test";
+import { test } from "../../util/test.js";
+import { getTransactionModalObject } from "../../util/transaction.js";
+import {
+ mockRpcRequest,
+ updateDaoPolicyMembers,
+ mockNearBalances,
+} from "../../util/rpcmock.js";
+import path from "path";
+import { fileURLToPath } from "url";
+import { InsufficientBalance } from "../../util/lib.js";
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+
+function toBase64(json) {
+ return Buffer.from(JSON.stringify(json)).toString("base64");
+}
+
+test.afterEach(async ({ page }, testInfo) => {
+ console.log(`Finished ${testInfo.title} with status ${testInfo.status}`);
+ await page.unrouteAll({ behavior: "ignoreErrors" });
+});
+
+const metadata = {
+ flagLogo:
+ "https://ipfs.near.social/ipfs/bafkreiboarigt5w26y5jyxyl4au7r2dl76o5lrm2jqjgqpooakck5xsojq",
+ displayName: "testing-astradao",
+ primaryColor: "#2f5483",
+ theme: "dark",
+};
+
+const config = {
+ name: "testing-astradao",
+ metadata: toBase64(metadata),
+};
+
+async function updateDaoConfig({ page }) {
+ await mockRpcRequest({
+ page,
+ filterParams: {
+ method_name: "get_config",
+ },
+ modifyOriginalResultFunction: () => {
+ return config;
+ },
+ });
+}
+
+async function navigateToThemePage({ page, instanceAccount }) {
+ await page.goto(`/${instanceAccount}/widget/app?page=settings`);
+ await updateDaoPolicyMembers({ page });
+ await updateDaoConfig({ page });
+ await page.waitForTimeout(5_000);
+ await page.getByText("Theme & Logo", { exact: true }).click();
+ await expect(page.getByText("Theme & Logo").nth(1)).toBeVisible();
+}
+
+test.describe("User is not logged in", function () {
+ test("should not be able to change config", async ({
+ page,
+ instanceAccount,
+ }) => {
+ test.setTimeout(60_000);
+ await navigateToThemePage({ page, instanceAccount });
+ await expect(page.locator("input[type='color']")).toBeDisabled();
+ await expect(
+ page.getByRole("button", { name: "Submit Request" })
+ ).toBeDisabled({ timeout: 20_000 });
+ });
+});
+
+test.describe("User is logged in", function () {
+ test.use({
+ storageState: "playwright-tests/storage-states/wallet-connected-admin.json",
+ });
+
+ test.beforeEach(async ({ page, instanceAccount }, testInfo) => {
+ if (testInfo.title.includes("insufficient account balance")) {
+ await mockNearBalances({
+ page,
+ accountId: "theori.near",
+ balance: InsufficientBalance,
+ storage: 8,
+ });
+ }
+ await navigateToThemePage({ page, instanceAccount });
+ });
+
+ test("insufficient account balance should show warning modal, disallow action ", async ({
+ page,
+ }) => {
+ test.setTimeout(60_000);
+ await expect(
+ page.getByText(
+ "Hey Ori, you don't have enough NEAR to complete actions on your treasury."
+ )
+ ).toBeVisible();
+ await page
+ .getByText("Submit Request", {
+ exact: true,
+ })
+ .click();
+ await expect(
+ page
+ .getByText("Please add more funds to your account and try again")
+ .nth(1)
+ ).toBeVisible();
+ });
+
+ test("should be able to upload image, should show error with incorrect width and allow correct one", async ({
+ page,
+ }) => {
+ test.setTimeout(150_000);
+ await expect(
+ page.frameLocator("iframe").getByRole("button", { name: "Upload Logo" })
+ ).toBeVisible();
+ await page.route("https://ipfs.near.social/add", async (route) => {
+ await route.fulfill({
+ status: 200,
+ contentType: "application/json",
+ body: JSON.stringify({ cid: "simple_cid_1" }),
+ });
+ });
+
+ const logoInput = await page
+ .frameLocator("iframe")
+ .locator("input[type=file]");
+ const submitBtn = await page.getByRole("button", {
+ name: "Submit Request",
+ });
+ // invalid image
+ await logoInput.setInputFiles(path.join(__dirname, "./assets/invalid.png"));
+ await expect(
+ page.getByText(
+ "Invalid logo. Please upload a PNG, JPG, or SVG file for your logo that is exactly 256x256 px"
+ )
+ ).toBeVisible();
+ await expect(submitBtn).toBeDisabled(submitBtn);
+ await logoInput.setInputFiles(path.join(__dirname, "./assets/valid.jpg"));
+ await submitBtn.click();
+ await expect(page.getByText("Processing your request ...")).toBeVisible();
+
+ await expect(await getTransactionModalObject(page)).toEqual({
+ proposal: {
+ description: "* Title: Update Config - Theme & logo",
+ kind: {
+ ChangeConfig: {
+ config: {
+ ...config,
+ metadata: toBase64({
+ ...metadata,
+ flagLogo: "https://ipfs.near.social/ipfs/simple_cid_1",
+ }),
+ },
+ },
+ },
+ },
+ });
+ });
+
+ test("should be able to change color and theme", async ({ page }) => {
+ test.setTimeout(150_000);
+ const newColor = "#0000";
+ await page.getByRole("textbox").nth(1).fill(newColor);
+ await page.getByTestId("dropdown-btn").click();
+ await page.getByText("Light").click();
+ await page.getByRole("button", { name: "Submit Request" }).click();
+ await expect(page.getByText("Processing your request ...")).toBeVisible();
+ await expect(await getTransactionModalObject(page)).toEqual({
+ proposal: {
+ description: "* Title: Update Config - Theme & logo",
+ kind: {
+ ChangeConfig: {
+ config: {
+ ...config,
+ metadata: toBase64({
+ ...metadata,
+ primaryColor: newColor,
+ theme: "light",
+ }),
+ },
+ },
+ },
+ },
+ });
+ });
+
+ test("should display a transaction error toast when the transaction confirmation modal is canceled", async ({
+ page,
+ }) => {
+ test.setTimeout(150_000);
+ await page.getByRole("button", { name: "Submit Request" }).click();
+ await expect(page.getByText("Processing your request ...")).toBeVisible();
+ await page.getByRole("button", { name: "Close" }).nth(1).click();
+ await expect(
+ page.getByText(
+ "Something went wrong. Please try resubmitting the request"
+ )
+ ).toBeVisible({ timeout: 30_000 });
+ });
+});
diff --git a/playwright-tests/tests/settings/voting-duration.spec.js b/playwright-tests/tests/settings/voting-duration.spec.js
index 23cb7994..962c4c82 100644
--- a/playwright-tests/tests/settings/voting-duration.spec.js
+++ b/playwright-tests/tests/settings/voting-duration.spec.js
@@ -2,29 +2,89 @@ import { expect } from "@playwright/test";
import { test } from "../../util/test.js";
import { getTransactionModalObject } from "../../util/transaction.js";
import { SandboxRPC } from "../../util/sandboxrpc.js";
-import { mockRpcRequest } from "../../util/rpcmock.js";
-import { encodeToMarkdown } from "../../util/lib.js";
+import {
+ mockNearBalances,
+ mockRpcRequest,
+ updateDaoPolicyMembers,
+} from "../../util/rpcmock.js";
+import { InsufficientBalance, encodeToMarkdown } from "../../util/lib.js";
test.afterEach(async ({ page }, testInfo) => {
console.log(`Finished ${testInfo.title} with status ${testInfo.status}`);
await page.unrouteAll({ behavior: "ignoreErrors" });
});
-test.describe("admin connected", function () {
+async function navigateToVotingDurationPage({ page, instanceAccount }) {
+ await page.goto(`/${instanceAccount}/widget/app?page=settings`);
+ await page.waitForTimeout(5_000);
+ await page.getByText("Voting Duration").click();
+ await expect(
+ page.getByText("Set the number of days a vote is active.")
+ ).toBeVisible({
+ timeout: 10_000,
+ });
+}
+
+test.describe("User is not logged in", function () {
+ test("should not be able to change voting duration", async ({
+ page,
+ instanceAccount,
+ }) => {
+ await navigateToVotingDurationPage({ page, instanceAccount });
+ await expect(
+ page.getByPlaceholder("Enter voting duration days")
+ ).toBeDisabled();
+
+ await expect(
+ page.getByRole("button", { name: "Submit Request" })
+ ).toBeDisabled();
+ });
+});
+
+test.describe("User is logged in", function () {
test.use({
storageState: "playwright-tests/storage-states/wallet-connected-admin.json",
});
+ test("insufficient account balance should show warning modal, disallow action ", async ({
+ page,
+ instanceAccount,
+ }) => {
+ test.setTimeout(60_000);
+ await updateDaoPolicyMembers({ page });
+ await mockNearBalances({
+ page,
+ accountId: "theori.near",
+ balance: InsufficientBalance,
+ storage: 8,
+ });
+ await navigateToVotingDurationPage({ page, instanceAccount });
+ await expect(
+ page.getByText(
+ "Hey Ori, you don't have enough NEAR to complete actions on your treasury."
+ )
+ ).toBeVisible();
+ await page.getByPlaceholder("Enter voting duration days").fill("2");
+ await page
+ .getByText("Submit Request", {
+ exact: true,
+ })
+ .click();
+ await expect(
+ page
+ .getByText("Please add more funds to your account and try again")
+ .nth(1)
+ ).toBeVisible();
+ });
+
test("should set voting duration", async ({
page,
instanceAccount,
daoAccount,
}) => {
test.setTimeout(150_000);
- await page.goto(`/${instanceAccount}/widget/app?page=settings`);
- await page.getByText("Voting Duration").first().click();
-
- await page.waitForTimeout(500);
+ await updateDaoPolicyMembers({ page });
+ await navigateToVotingDurationPage({ page, instanceAccount });
const currentDurationDays = await page
.getByPlaceholder("Enter voting duration days")
.inputValue();
@@ -36,12 +96,6 @@ test.describe("admin connected", function () {
await page.waitForTimeout(500);
await page.locator("button", { hasText: "Submit" }).click();
- if (daoAccount === "testing-astradao.sputnik-dao.near") {
- await page
- .locator(".modalfooter button", { hasText: "Yes, proceed" })
- .click();
- }
-
const description = {
title: "Update policy - Voting Duration",
summary: `theori.near requested to change voting duration from ${currentDurationDays} to ${newDurationDays}.`,
@@ -73,10 +127,8 @@ test.describe("admin connected", function () {
daoAccount,
}) => {
test.setTimeout(150_000);
- await page.goto(`/${instanceAccount}/widget/app?page=settings`);
- await page.getByText("Voting Duration").first().click();
-
- await page.waitForTimeout(500);
+ await updateDaoPolicyMembers({ page });
+ await navigateToVotingDurationPage({ page, instanceAccount });
const currentDurationDays = await page
.getByPlaceholder("Enter voting duration days")
.inputValue();
@@ -111,10 +163,9 @@ test.describe("admin connected", function () {
receiver_id: "webassemblymusic.near",
daoName,
});
- await page.goto(`/${instanceAccount}/widget/app?page=settings`);
- await page.getByText("Voting Duration").first().click();
+ await updateDaoPolicyMembers({ page });
+ await navigateToVotingDurationPage({ page, instanceAccount });
- await page.waitForTimeout(500);
const currentDurationDays = await page
.getByPlaceholder("Enter voting duration days")
.inputValue();
@@ -137,8 +188,8 @@ test.describe("admin connected", function () {
const wallet = await selector.wallet();
return new Promise((resolve) => {
- wallet.signAndSendTransactions = async (transactions) => {
- resolve(transactions.transactions[0]);
+ wallet.signAndSendTransaction = async (transaction) => {
+ resolve(transaction);
return await new Promise(
(transactionSentPromiseResolve) =>
(window.transactionSentPromiseResolve =
@@ -162,7 +213,7 @@ test.describe("admin connected", function () {
await page.evaluate((transactionResult) => {
window.transactionSentPromiseResolve(transactionResult);
}, transactionResult);
- await expect(page.locator(".toast-header")).toBeVisible();
+ await expect(page.getByText("Processing your request ...")).toBeVisible();
await expect(
page.getByText("Voting duration change request submitted")
).toBeVisible();
@@ -222,8 +273,8 @@ test.describe("admin connected", function () {
}
},
});
- await page.goto(`/${instanceAccount}/widget/app?page=settings`);
- await page.getByText("Voting Duration").first().click();
+ await updateDaoPolicyMembers({ page });
+ await navigateToVotingDurationPage({ page, instanceAccount });
await page.waitForTimeout(500);
const currentDurationDays = await page
diff --git a/playwright-tests/tests/stake-delegation/stake-delegation.spec.js b/playwright-tests/tests/stake-delegation/stake-delegation.spec.js
index cb7f43cd..e11730d0 100644
--- a/playwright-tests/tests/stake-delegation/stake-delegation.spec.js
+++ b/playwright-tests/tests/stake-delegation/stake-delegation.spec.js
@@ -6,7 +6,11 @@ import {
mockTransactionSubmitRPCResponses,
} from "../../util/transaction";
import { utils } from "near-api-js";
-import { mockRpcRequest, updateDaoPolicyMembers } from "../../util/rpcmock.js";
+import {
+ mockNearBalances,
+ mockRpcRequest,
+ updateDaoPolicyMembers,
+} from "../../util/rpcmock.js";
import {
CurrentTimestampInNanoseconds,
OldJsonProposalData,
@@ -16,6 +20,7 @@ import {
} from "../../util/inventory.js";
import { setDontAskAgainCacheValues } from "../../util/cache.js";
import Big from "big.js";
+import { InsufficientBalance } from "../../util/lib.js";
test.afterEach(async ({ page }, testInfo) => {
console.log(`Finished ${testInfo.title} with status ${testInfo.status}`);
@@ -160,27 +165,6 @@ async function mockStakedPoolBalances({ page }) {
});
}
-async function mockNearBalances({ page, daoAccount, balance }) {
- await page.route(
- `https://api.fastnear.com/v1/account/${daoAccount}/full`,
- async (route) => {
- const json = {
- account_id: daoAccount,
- nfts: [],
- pools: [],
- state: {
- balance: balance,
- locked: "0",
- storage_bytes: 677278,
- },
- tokens: [],
- };
-
- await route.fulfill({ json });
- }
- );
-}
-
export async function mockStakedPools({
daoAccount,
page,
@@ -412,7 +396,7 @@ async function voteOnProposal({
}
test.describe("Have valid staked requests and sufficient token balance", function () {
- test.beforeEach(async ({ page, instanceAccount, daoAccount }) => {
+ test.beforeEach(async ({ page, instanceAccount, daoAccount }, testInfo) => {
const instanceConfig = await getInstanceConfig({ page, instanceAccount });
if (
!instanceConfig.navbarLinks.find(
@@ -422,20 +406,37 @@ test.describe("Have valid staked requests and sufficient token balance", functio
console.log("no stake delegation page configured for instance");
return test.skip();
}
- await mockStakeProposals({ page });
+ if (
+ testInfo.title.includes("Should successfully parse old JSON description")
+ ) {
+ await mockOldJSONStakeProposals({ page });
+ } else {
+ await mockStakeProposals({ page });
+ }
await updateDaoPolicyMembers({ page });
await mockStakedPools({ page, daoAccount });
- await mockNearBalances({
- page,
- daoAccount,
- balance: sufficientAvailableBalance,
- });
+ if (testInfo.title.includes("insufficient account balance")) {
+ await mockNearBalances({
+ page,
+ accountId: "theori.near",
+ balance: InsufficientBalance,
+ storage: 8,
+ });
+ } else {
+ await mockNearBalances({
+ page,
+ accountId: daoAccount,
+ balance: sufficientAvailableBalance,
+ storage: 2323,
+ });
+ }
await mockStakedPoolBalances({ page });
await page.goto(`/${instanceAccount}/widget/app?page=stake-delegation`);
await expect(
await page.locator("div").filter({ hasText: /^Stake Delegation$/ })
).toBeVisible();
+ await page.waitForTimeout(5_000);
});
test.describe("User not logged in", function () {
@@ -452,8 +453,6 @@ test.describe("Have valid staked requests and sufficient token balance", functio
test("Should successfully parse old JSON description", async ({ page }) => {
test.setTimeout(80_000);
- await mockOldJSONStakeProposals({ page });
-
await expect(
page.getByRole("cell", { name: "this is notes", exact: true })
).toBeVisible({ timeout: 40_000 });
@@ -480,6 +479,28 @@ test.describe("Have valid staked requests and sufficient token balance", functio
});
});
+ test("insufficient account balance should show warning modal, disallow action ", async ({
+ page,
+ }) => {
+ test.setTimeout(60_000);
+
+ await expect(
+ page.getByText(
+ "Hey Ori, you don't have enough NEAR to complete actions on your treasury."
+ )
+ ).toBeVisible();
+ await page
+ .getByText("Create Request", {
+ exact: true,
+ })
+ .click();
+ await expect(
+ page
+ .getByText("Please add more funds to your account and try again")
+ .nth(1)
+ ).toBeVisible();
+ });
+
test("Should create stake delegation request, should throw error when invalid data is provided", async ({
page,
}) => {
@@ -488,7 +509,7 @@ test.describe("Have valid staked requests and sufficient token balance", functio
exact: true,
});
await createRequestButton.click();
- await page.getByText("Stake", { exact: true }).click();
+ await page.locator(".option").first().click();
await page.waitForTimeout(10_000);
await fillValidatorAccount({
@@ -505,6 +526,7 @@ test.describe("Have valid staked requests and sufficient token balance", functio
.first()
.inputValue();
await page.getByRole("button", { name: "Submit" }).click();
+ await expect(page.getByText("Processing your request ...")).toBeVisible();
await expect(await getTransactionModalObject(page)).toEqual({
proposal: {
description: "* Proposal Action: stake",
@@ -540,6 +562,8 @@ test.describe("Have valid staked requests and sufficient token balance", functio
errorText: "The amount exceeds the balance you have staked.",
});
await page.getByRole("button", { name: "Submit" }).click();
+ await expect(page.getByText("Processing your request ...")).toBeVisible();
+
await expect(await getTransactionModalObject(page)).toEqual({
proposal: {
description: "* Proposal Action: unstake",
@@ -655,7 +679,8 @@ test.describe("Withdraw request", function () {
});
const submitBtn = page.getByRole("button", { name: "Submit" });
await expect(submitBtn).toBeEnabled();
- await submitBtn.click();
+ await submitBtn.dblclick();
+ await expect(page.getByText("Processing your request ...")).toBeVisible();
await expect(await getTransactionModalObject(page)).toEqual({
proposal: {
description: `* Proposal Action: withdraw`,
@@ -701,9 +726,6 @@ test.describe("Withdraw request", function () {
await expect(page.getByText(stakedPoolAccount)).toBeVisible({
timeout: 10_000,
});
- const submitBtn = page.getByRole("button", { name: "Submit" });
- await expect(submitBtn).toBeEnabled();
- await submitBtn.click();
await expect(
page.getByText(
"By submitting, you request to withdraw all available funds. A separate withdrawal request will be created for each validator"
@@ -711,6 +733,10 @@ test.describe("Withdraw request", function () {
).toBeVisible({
timeout: 10_000,
});
+ const submitBtn = page.getByRole("button", { name: "Submit" });
+ await expect(submitBtn).toBeEnabled();
+ await submitBtn.dblclick();
+ await expect(page.getByText("Processing your request ...")).toBeVisible();
// proposals for both the pools
await expect(await getTransactionModalObject(page)).toEqual({
proposal: {
@@ -837,9 +863,11 @@ test.describe("Lockup staking", function () {
await updateDaoPolicyMembers({ page });
await mockNearBalances({
page,
- daoAccount,
+ accountId: daoAccount,
balance: sufficientAvailableBalance,
+ storage: 2323,
});
+
await mockLockupNearBalances({
page,
balance: sufficientAvailableBalance,
@@ -888,6 +916,8 @@ test.describe("Lockup staking", function () {
errorText: "Your account doesn't have sufficient balance.",
});
await page.getByRole("button", { name: "Submit" }).click();
+ await expect(page.getByText("Processing your request ...")).toBeVisible();
+
await expect(await getTransactionModalObject(page)).toEqual({
proposal: {
description:
@@ -1002,6 +1032,8 @@ test.describe("Lockup staking", function () {
errorText: "Your account doesn't have sufficient balance.",
});
await page.getByRole("button", { name: "Submit" }).click();
+ await expect(page.getByText("Processing your request ...")).toBeVisible();
+
await expect(await getTransactionModalObject(page)).toEqual({
proposal: {
description: "* Proposal Action: stake",
@@ -1049,6 +1081,8 @@ test.describe("Lockup staking", function () {
errorText: "The amount exceeds the balance you have staked.",
});
await page.getByRole("button", { name: "Submit" }).click();
+ await expect(page.getByText("Processing your request ...")).toBeVisible();
+
await expect(await getTransactionModalObject(page)).toEqual({
proposal: {
description: "* Proposal Action: unstake",
@@ -1306,9 +1340,11 @@ test.describe("Insufficient balance ", function () {
await mockStakedPoolBalances({ page });
await mockNearBalances({
page,
- daoAccount,
+ accountId: daoAccount,
balance: inSufficientAvailableBalance,
+ storage: 2323,
});
+
await page.goto(`/${instanceAccount}/widget/app?page=stake-delegation`);
await expect(
await page.locator("div").filter({ hasText: /^Stake Delegation$/ })
diff --git a/playwright-tests/util/lib.js b/playwright-tests/util/lib.js
index 35b81046..e6f7134a 100644
--- a/playwright-tests/util/lib.js
+++ b/playwright-tests/util/lib.js
@@ -20,3 +20,5 @@ export const encodeToMarkdown = (data) => {
})
.join(" ");
};
+
+export const InsufficientBalance = BigInt(0.05 * 10 ** 24).toString();
diff --git a/playwright-tests/util/rpcmock.js b/playwright-tests/util/rpcmock.js
index 72b74c13..1ea4781a 100644
--- a/playwright-tests/util/rpcmock.js
+++ b/playwright-tests/util/rpcmock.js
@@ -190,3 +190,24 @@ export async function updateDaoPolicyMembers({ page, isMultiVote = false }) {
},
});
}
+
+export async function mockNearBalances({ page, accountId, balance, storage }) {
+ await page.route(
+ `https://api.fastnear.com/v1/account/${accountId}/full`,
+ async (route) => {
+ const json = {
+ account_id: accountId,
+ nfts: [],
+ pools: [],
+ state: {
+ balance: balance,
+ locked: "0",
+ storage: storage,
+ },
+ tokens: [],
+ };
+
+ await route.fulfill({ json });
+ }
+ );
+}
diff --git a/playwright-tests/util/sandboxrpc.js b/playwright-tests/util/sandboxrpc.js
index 9e22f652..f97bad2b 100644
--- a/playwright-tests/util/sandboxrpc.js
+++ b/playwright-tests/util/sandboxrpc.js
@@ -6,7 +6,8 @@ import { KeyPairEd25519 } from "near-api-js/lib/utils/key_pair.js";
import { getLocalWidgetSource } from "./bos-workspace.js";
export const SPUTNIK_DAO_CONTRACT_ID = "sputnik-dao.near";
-export const PROPOSAL_BOND = "100000000000000000000000";
+// we don't have proposal bond for any instance (in this repo)
+export const PROPOSAL_BOND = "0";
export class SandboxRPC {
async init() {
@@ -237,7 +238,7 @@ export class SandboxRPC {
quorum: "0",
threshold: [1, 2],
},
- proposal_bond: "100000000000000000000000",
+ proposal_bond: PROPOSAL_BOND,
proposal_period: "604800000000000",
bounty_bond: "100000000000000000000000",
bounty_forgiveness_period: "604800000000000",