Skip to content

Commit

Permalink
Treasury self creation flow (#102)
Browse files Browse the repository at this point in the history
  • Loading branch information
rubycop authored Nov 18, 2024
1 parent d8f8314 commit 8aa0b43
Show file tree
Hide file tree
Showing 21 changed files with 1,445 additions and 0 deletions.
Binary file added instances/treasury-factory.near/.DS_Store
Binary file not shown.
2 changes: 2 additions & 0 deletions instances/treasury-factory.near/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
build
dist
12 changes: 12 additions & 0 deletions instances/treasury-factory.near/aliases.mainnet.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"REPL_DEVHUB": "devhub.near",
"REPL_DEVHUB_CONTRACT": "devhub.near",
"REPL_BASE_DEPLOYMENT_ACCOUNT": "treasury-factory.near",
"REPL_DEVDAO_ACCOUNT": "treasury-devdao.near",
"REPL_SPUTNIK_FACTORY_ACCOUNT": "sputnik-dao.near",
"REPL_NEAR": "near",
"REPL_MOB": "mob.near",
"REPL_SOCIAL_CONTRACT": "social.near",
"REPL_RPC_URL": "https://rpc.mainnet.near.org",
"REPL_PIKESPEAK_KEY": "36f2b87a-7ee6-40d8-80b9-5e68e587a5b5"
}
6 changes: 6 additions & 0 deletions instances/treasury-factory.near/bos.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"account": "treasury-factory.near",
"aliasPrefix": "REPL",
"aliasesContainsPrefix": true,
"aliases": ["./aliases.mainnet.json"]
}
3 changes: 3 additions & 0 deletions instances/treasury-factory.near/data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"treasury-factory.near": {}
}
1 change: 1 addition & 0 deletions instances/treasury-factory.near/src
29 changes: 29 additions & 0 deletions instances/treasury-factory.near/widget/app.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* This is the main entry point for the Treasury application.
* Page route gets passed in through params, along with all other page props.
*/

const { page, ...passProps } = props;

// Import our modules
const { AppLayout } = VM.require(
"${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/components.templates.AppLayout"
) || { AppLayout: () => <></> };
const { Theme } = VM.require(
`${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/config.css`
) || {
Theme: () => <></>,
};

const propsToSend = { ...passProps };

return (
<Theme>
<AppLayout>
<Widget
src="${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/pages.treasury.Create"
props={propsToSend}
/>
</AppLayout>
</Theme>
);
32 changes: 32 additions & 0 deletions instances/treasury-factory.near/widget/components/Info.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const { type, text } = props;

const typeMapper = {
info: { icon: "bi-info-circle", color: "#555555", bg: "#F4F4F4" },
alert: { icon: "bi-exclamation-triangle", color: "#B17108", bg: "#FF9E001A" },
};

const Containet = styled.div`
display: flex;
padding: 10px 15px;
gap: 10px;
border-radius: 15px;
align-items: center;
background: ${typeMapper[type].bg};
color: ${typeMapper[type].color};
i {
font-size: 20px;
}
small {
font-size: 12px;
line-height: 15px;
}
`;

return (
<Containet>
<i className={`bi ${typeMapper[type].icon}`} />
<small>{text}</small>
</Containet>
);
97 changes: 97 additions & 0 deletions instances/treasury-factory.near/widget/components/Modal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
const { onClose, isOpen, heading, content } = props;

const Modal = styled.div`
display: ${({ hidden }) => (hidden ? "none" : "flex")};
position: fixed;
inset: 0;
justify-content: center;
align-items: center;
opacity: 1;
z-index: 999;
.black-btn {
background-color: #000 !important;
border: none;
color: white;
&:active {
color: white;
}
}
@media screen and (max-width: 768px) {
h5 {
font-size: 16px !important;
}
}
.btn {
font-size: 14px;
}
.theme-btn {
background-color: var(--theme-color) !important;
color: white;
}
`;

const ModalBackdrop = styled.div`
position: absolute;
inset: 0;
background-color: rgba(0, 0, 0, 0.5);
opacity: 0.4;
`;

const ModalDialog = styled.div`
padding: 1.5em;
z-index: 999;
overflow-y: auto;
max-height: 85%;
margin-top: 5%;
width: 35%;
@media screen and (max-width: 768px) {
margin: 2rem;
width: 100%;
}
`;

const ModalHeader = styled.div`
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
h5 {
font-size: 24px;
font-weight: 600;
line-height: 29.05px;
letter-spacing: -0.02em;
text-align: left;
}
`;

const ModalContent = styled.div`
flex: 1;
font-size: 14px;
margin-top: 4px;
margin-bottom: 4px;
overflow-y: auto;
max-height: 50%;
text-align: left !important;
@media screen and (max-width: 768px) {
font-size: 12px !important;
}
`;

return (
<Modal hidden={!isOpen}>
<ModalBackdrop />
<ModalDialog className="card d-flex flex-column gap-2">
<ModalHeader className="d-flex justify-content-between align-items-center">
<h5 className="m-0">{heading}</h5>
<i role="button" className="bi bi-x-lg" onClick={onClose} />
</ModalHeader>
<ModalContent>{content}</ModalContent>
</ModalDialog>
</Modal>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
const { alertMsg, setAlertMsg, onChange, defaultValue, postfix } = props;

const [value, setValue] = useState(defaultValue ?? "");

const checkAccountAvailable = async (accountId) => {
if (accountId.length === 0) return;

asyncFetch(`${REPL_RPC_URL}`, {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({
jsonrpc: "2.0",
id: "dontcare",
method: "query",
params: {
request_type: "view_account",
finality: "final",
account_id: `${accountId}${postfix}`,
},
}),
}).then((resp) => {
if (!resp) return;

const err = resp.body?.error?.cause;
let errMsg = null;

if (!err) errMsg = `Account ${accountId}${postfix} already been taken`;
else if (err.name !== "UNKNOWN_ACCOUNT") errMsg = err?.info?.error_message;

setAlertMsg(errMsg);
});
};

return (
<div className="position-relative d-flex align-items-center">
<input
type="text"
placeholder="app-account"
value={value}
onChange={(e) => {
const v = e.target.value;
checkAccountAvailable(v);
setValue(v);
onChange(v);
}}
/>
<div
style={{
position: "absolute",
right: "0px",
borderLeft: "1px solid var(--bs-border-color)",
}}
className="py-2 px-3"
>
{postfix}
</div>
</div>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
const { fields, setFields, onClose, onSubmit } = props;

const FormFields = styled.div`
.rbt-token {
background: transparent;
color: inherit;
border: 1px solid #e2e6ec;
border-radius: 32px;
padding: 2px 4px;
}
.rbt-menu > .dropdown-item:hover {
text-decoration: none;
}
`;

const PERMISSIONS = {
create: "Create",
edit: "Edit",
vote: "Vote",
};

const [open, setOpen] = useState(false);

return (
<FormFields className="d-flex flex-column gap-3">
<div className="d-flex flex-column gap-2">
<label>Account</label>
<Widget
src="${REPL_DEVHUB}/widget/devhub.entity.proposal.AccountInput"
props={{
value: fields.accountId ?? "",
onUpdate: (value) => {
setFields({
...fields,
accountId: value ?? fields.accountId,
});
},
maxWidth: "100%",
}}
/>
</div>
<div className="d-flex flex-column gap-2">
<label>Permissions</label>
<Typeahead
selected={fields.permissions ?? []}
onChange={(value) => {
setFields({
...fields,
permissions: value ?? fields.permissions,
});
}}
options={Object.values(PERMISSIONS)}
positionFixed
multiple
/>
</div>
<div className="d-flex flex-row justify-content-end gap-2">
<div className={`btn`} onClick={onClose}>
Close
</div>
<div
className={`btn btn-primary ${
fields.accountId?.length > 0 && fields.permissions?.length > 0
? ""
: "disabled"
}`}
onClick={onSubmit}
>
Submit
</div>
</div>
</FormFields>
);
Loading

0 comments on commit 8aa0b43

Please sign in to comment.