diff --git a/dapp/package.json b/dapp/package.json
index a324df5..dc6e8b1 100644
--- a/dapp/package.json
+++ b/dapp/package.json
@@ -5,6 +5,7 @@
"@astrojs/tailwind": "5.1.2",
"@creit.tech/sorobandomains-sdk": "^0.1.4",
"@creit.tech/stellar-wallets-kit": "^1.2.5",
+ "@mdxeditor/editor": "^3.19.2",
"@nanostores/react": "^0.8.0",
"@stellar/stellar-sdk": "^13.0.0",
"astro": "4.16.13",
diff --git a/dapp/src/components/page/governance/GovernancePageTitle.astro b/dapp/src/components/page/governance/GovernancePageTitle.astro
index 85833b8..cfda972 100644
--- a/dapp/src/components/page/governance/GovernancePageTitle.astro
+++ b/dapp/src/components/page/governance/GovernancePageTitle.astro
@@ -18,6 +18,7 @@ import Topic from "../../utils/Topic.astro";
diff --git a/dapp/src/components/page/governance/NewProposalPage.astro b/dapp/src/components/page/governance/NewProposalPage.astro
new file mode 100644
index 0000000..c8aa770
--- /dev/null
+++ b/dapp/src/components/page/governance/NewProposalPage.astro
@@ -0,0 +1,13 @@
+---
+import ProposalForm from "./ProposalForm";
+---
+
+
diff --git a/dapp/src/components/page/governance/NewProposalPageTitle.astro b/dapp/src/components/page/governance/NewProposalPageTitle.astro
new file mode 100644
index 0000000..7ccfaf4
--- /dev/null
+++ b/dapp/src/components/page/governance/NewProposalPageTitle.astro
@@ -0,0 +1,9 @@
+---
+import Topic from "components/utils/Topic.astro";
+---
+
+
+
+
diff --git a/dapp/src/components/page/governance/ProposalForm.tsx b/dapp/src/components/page/governance/ProposalForm.tsx
new file mode 100644
index 0000000..06a03f0
--- /dev/null
+++ b/dapp/src/components/page/governance/ProposalForm.tsx
@@ -0,0 +1,291 @@
+import React from "react";
+import { useState, useEffect } from "react";
+import {
+ MDXEditor,
+ headingsPlugin,
+ listsPlugin,
+ quotePlugin,
+ thematicBreakPlugin,
+ markdownShortcutPlugin,
+ linkPlugin,
+ linkDialogPlugin,
+ imagePlugin,
+ tablePlugin,
+ codeBlockPlugin,
+ codeMirrorPlugin,
+ diffSourcePlugin,
+ frontmatterPlugin,
+ toolbarPlugin,
+ Separator,
+ UndoRedo,
+ BoldItalicUnderlineToggles,
+ BlockTypeSelect,
+ CodeToggle,
+ CreateLink,
+ DiffSourceToggleWrapper,
+ InsertAdmonition,
+ InsertImage,
+ InsertTable,
+ InsertFrontmatter,
+ InsertThematicBreak,
+ InsertCodeBlock,
+ ListsToggle,
+ type CodeBlockEditorDescriptor,
+ useCodeBlockEditorContext,
+} from "@mdxeditor/editor";
+import "@mdxeditor/editor/style.css";
+import { capitalizeFirstLetter } from "utils/utils";
+import type { ProposalOutcome } from "types/proposal";
+
+const ProposalForm: React.FC = () => {
+ const [mdText, setMdText] = useState("");
+ const [isClient, setIsClient] = useState(false);
+ const [approveDescription, setApproveDescription] = useState("");
+ const [rejectDescription, setRejectDescription] = useState("");
+ const [cancelledDescription, setCancelledDescription] = useState("");
+ const [approveXdr, setApproveXdr] = useState("");
+ const [rejectXdr, setRejectXdr] = useState("");
+ const [cancelledXdr, setCancelledXdr] = useState("");
+ const [imageFiles, setImageFiles] = useState<
+ { localUrl: string; publicUrl: string; source: File }[]
+ >([]);
+
+ useEffect(() => {
+ setIsClient(true);
+ }, []);
+
+ if (!isClient) {
+ return null;
+ }
+
+ const imageUploadHandler = async (image: File) => {
+ const url = URL.createObjectURL(image);
+ setImageFiles([
+ ...imageFiles,
+ { localUrl: url, publicUrl: image.name, source: image },
+ ]);
+ console.log("imageUrl:", imageFiles);
+ return url;
+ };
+
+ const PlainTextCodeEditorDescriptor: CodeBlockEditorDescriptor = {
+ match: (language, meta) => true,
+ priority: 0,
+ Editor: (props) => {
+ const cb = useCodeBlockEditorContext();
+ return (
+ e.nativeEvent.stopImmediatePropagation()}>
+
+ );
+ },
+ };
+
+ const submitProposal = () => {
+ const proposalOutcome: ProposalOutcome = {
+ approved: {
+ description: approveDescription,
+ xdr: approveXdr,
+ },
+ rejected: {
+ description: rejectDescription,
+ xdr: rejectXdr,
+ },
+ cancelled: {
+ description: cancelledDescription,
+ xdr: cancelledXdr,
+ },
+ };
+ const outcome = new Blob([JSON.stringify(proposalOutcome)], {
+ type: "application/json",
+ });
+
+ let files = [new File([outcome], "outcomes.json")];
+ let description = mdText;
+
+ imageFiles.forEach((image) => {
+ if (description.includes(image.localUrl)) {
+ description = description.replace(
+ new RegExp(image.localUrl, "g"),
+ image.publicUrl,
+ );
+ files.push(new File([image.source], image.publicUrl));
+ }
+ });
+
+ files.push(new File([description], "proposal.md"));
+ alert("Proposal submitted successfully!");
+ };
+
+ return (
+
+
+ Proposal Description
+
+
+
(
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ ),
+ }),
+ ]}
+ markdown={mdText}
+ onChange={(value) => setMdText(value || "")}
+ placeholder="Input your proposal description here..."
+ />
+
+
+ Proposal Outcome
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default ProposalForm;
+
+interface OutcomeInputProps {
+ type: string;
+ description: string;
+ setDescription: React.Dispatch>;
+ xdr: string;
+ setXdr: React.Dispatch>;
+}
+
+const OutcomeInput = ({
+ type,
+ description,
+ setDescription,
+ xdr,
+ setXdr,
+}: OutcomeInputProps) => {
+ return (
+
+
+
+ {capitalizeFirstLetter(type)}
+
+
+
+
+ );
+};
diff --git a/dapp/src/pages/proposal/new.astro b/dapp/src/pages/proposal/new.astro
new file mode 100644
index 0000000..354977d
--- /dev/null
+++ b/dapp/src/pages/proposal/new.astro
@@ -0,0 +1,23 @@
+---
+import Layout from "../../layouts/Layout.astro";
+import Container from "../../components/layout/Container.astro";
+import NewProposalPageTitle from "components/page/governance/NewProposalPageTitle.astro";
+import NewProposalPage from "components/page/governance/NewProposalPage.astro";
+---
+
+
+
+
+
+
+
+
+
+
+
+
+