Merge remote-tracking branch 'origin/create-contract' into bet
This commit is contained in:
		
						commit
						b8b5868eb8
					
				
							
								
								
									
										32
									
								
								web/components/button.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								web/components/button.tsx
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,32 @@ | |||
| export function Button(props: { | ||||
|   className?: string | ||||
|   onClick?: () => void | ||||
|   color: 'green' | 'red' | 'deemphasized' | ||||
|   hideFocusRing?: boolean | ||||
|   children?: any | ||||
| }) { | ||||
|   const { className, onClick, children, color, hideFocusRing } = props | ||||
| 
 | ||||
|   return ( | ||||
|     <button | ||||
|       type="button" | ||||
|       className={classNames( | ||||
|         'inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white', | ||||
|         !hideFocusRing && 'focus:outline-none focus:ring-2 focus:ring-offset-2', | ||||
|         color === 'green' && | ||||
|           'bg-green-500 hover:bg-green-600 focus:ring-green-500', | ||||
|         color === 'red' && 'bg-red-500 hover:bg-red-600 focus:ring-red-500', | ||||
|         color === 'deemphasized' && | ||||
|           'bg-transparent hover:bg-gray-500 focus:ring-gray-400', | ||||
|         className | ||||
|       )} | ||||
|       onClick={onClick} | ||||
|     > | ||||
|       {children} | ||||
|     </button> | ||||
|   ) | ||||
| } | ||||
| 
 | ||||
| function classNames(...classes: any[]) { | ||||
|   return classes.filter(Boolean).join(' ') | ||||
| } | ||||
|  | @ -9,8 +9,8 @@ const convertkitHTML = { | |||
|    <div data-style="clean"> | ||||
|       <ul class="formkit-alert formkit-alert-error" data-element="errors" data-group="alert"></ul> | ||||
|       <div data-element="fields" data-stacked="false" class="seva-fields formkit-fields"> | ||||
|          <div class="formkit-field" style="max-width:400px;margin-right:12px;"><input class="formkit-input" name="email_address" aria-label="Email Address" placeholder="Email Address" required="" type="email" style="color: rgb(0, 0, 0); border-color: rgb(227, 227, 227); border-radius: 4px; font-weight: 400;"></div> | ||||
|          <button data-element="submit" class="formkit-submit formkit-submit" style="color: rgb(255, 255, 255); background-color: rgb(17, 185, 129); border-radius: 4px; font-weight: 400; max-width: 175px"> | ||||
|          <div class="formkit-field" style="max-width:350px;margin-right:12px;"><input class="formkit-input" name="email_address" aria-label="Email Address" placeholder="Email Address" required="" type="email" style="color: rgb(0, 0, 0); border-color: rgb(227, 227, 227); border-radius: 4px; font-weight: 400;"></div> | ||||
|          <button data-element="submit" class="formkit-submit formkit-submit" style="color: rgb(255, 255, 255); background-color: rgb(17, 185, 129); border-radius: 4px; font-weight: 400; max-width: 200px"> | ||||
|             <div class="formkit-spinner"> | ||||
|                <div></div> | ||||
|                <div></div> | ||||
|  |  | |||
|  | @ -17,11 +17,19 @@ function SignInLink() { | |||
|   return ( | ||||
|     <> | ||||
|       {user ? ( | ||||
|         <> | ||||
|           <Link href="/contract"> | ||||
|             <a className="text-base font-medium text-white hover:text-gray-300"> | ||||
|               Create a market | ||||
|             </a> | ||||
|           </Link> | ||||
| 
 | ||||
|           <Link href="/account"> | ||||
|             <a className="text-base font-medium text-green-400 hover:text-gray-300"> | ||||
|               {user.name} | ||||
|             </a> | ||||
|           </Link> | ||||
|         </> | ||||
|       ) : ( | ||||
|         <button | ||||
|           className="text-base font-medium text-green-400  hover:text-gray-300" | ||||
|  |  | |||
|  | @ -11,9 +11,9 @@ export const Hero = () => { | |||
|             <div className="lg:grid lg:grid-cols-2 lg:gap-8"> | ||||
|               <div className="mx-auto max-w-md px-4 sm:max-w-2xl sm:px-6 sm:text-center lg:px-0 lg:text-left lg:flex lg:items-center"> | ||||
|                 <div className="lg:py-24"> | ||||
|                   <h1 className="mt-4 text-4xl tracking-tight font-extrabold text-white sm:mt-5 sm:text-6xl lg:mt-6 xl:text-6xl"> | ||||
|                   <h1 className="mt-4 text-4xl text-white sm:mt-5 sm:text-6xl lg:mt-6 xl:text-6xl"> | ||||
|                     <span className="block">Create your own</span> | ||||
|                     <span className="block text-green-400"> | ||||
|                     <span className="block text-green-400 font-bold"> | ||||
|                       prediction markets | ||||
|                     </span> | ||||
|                   </h1> | ||||
|  |  | |||
|  | @ -1,23 +1,88 @@ | |||
| import { collection, onSnapshot, doc } from '@firebase/firestore' | ||||
| import { db } from './init' | ||||
| import { app } from './init' | ||||
| import { | ||||
|   getFirestore, | ||||
|   doc, | ||||
|   setDoc, | ||||
|   deleteDoc, | ||||
|   where, | ||||
|   collection, | ||||
|   query, | ||||
|   getDocs, | ||||
|   onSnapshot, | ||||
|   orderBy, | ||||
| } from 'firebase/firestore' | ||||
| 
 | ||||
| export type Contract = { | ||||
|   id: string | ||||
|   id: string // Chosen by creator; must be unique
 | ||||
|   creatorId: string | ||||
|   creatorName: string | ||||
| 
 | ||||
|   question: string | ||||
|   description: string | ||||
|   description: string // More info about what the contract is about
 | ||||
|   outcomeType: 'BINARY' // | 'MULTI' | 'interval' | 'date'
 | ||||
|   // outcomes: ['YES', 'NO']
 | ||||
|   seedAmounts: { YES: number; NO: number } // seedBets: [number, number]
 | ||||
| 
 | ||||
|   createdTime: number // Milliseconds since epoch
 | ||||
|   lastUpdatedTime: number // If the question or description was changed
 | ||||
|   closeTime?: number // When no more trading is allowed
 | ||||
| 
 | ||||
|   // isResolved: boolean
 | ||||
|   resolutionTime?: 10293849 // When the contract creator resolved the market; 0 if unresolved
 | ||||
|   resolution?: 'YES' | 'NO' | 'CANCEL' // Chosen by creator; must be one of outcomes
 | ||||
| } | ||||
| 
 | ||||
| export type Bet = { | ||||
|   id: string | ||||
|   userId: string | ||||
|   contractId: string | ||||
| 
 | ||||
|   size: number // Amount of USD bid
 | ||||
|   outcome: 'YES' | 'NO' // Chosen outcome
 | ||||
|   createdTime: number | ||||
| 
 | ||||
|   dpmWeight: number // Dynamic Parimutuel weight
 | ||||
| } | ||||
| 
 | ||||
| const db = getFirestore(app) | ||||
| const contractCollection = collection(db, 'contracts') | ||||
| 
 | ||||
| // Push contract to Firestore
 | ||||
| export async function setContract(contract: Contract) { | ||||
|   const docRef = doc(db, 'contracts', contract.id) | ||||
|   await setDoc(docRef, contract) | ||||
| } | ||||
| 
 | ||||
| export async function deleteContract(contractId: string) { | ||||
|   const docRef = doc(db, 'contracts', contractId) | ||||
|   await deleteDoc(docRef) | ||||
| } | ||||
| 
 | ||||
| export async function listContracts(creatorId: string): Promise<Contract[]> { | ||||
|   const q = query( | ||||
|     contractCollection, | ||||
|     where('creatorId', '==', creatorId), | ||||
|     orderBy('createdTime', 'desc') | ||||
|   ) | ||||
|   const snapshot = await getDocs(q) | ||||
|   const contracts: Contract[] = [] | ||||
|   snapshot.forEach((doc) => contracts.push(doc.data() as Contract)) | ||||
|   return contracts | ||||
| } | ||||
| 
 | ||||
| export function listenForContract( | ||||
|   contractId: string, | ||||
|   setContract: (contract: Contract) => void | ||||
| ) { | ||||
|   const contractRef = doc(contractCollection, contractId) | ||||
| 
 | ||||
|   return onSnapshot(contractRef, (contractSnap) => { | ||||
|     setContract(contractSnap.data() as Contract) | ||||
|   }) | ||||
| } | ||||
| 
 | ||||
| // Push bet to Firestore
 | ||||
| // TODO: Should bets be subcollections under its contract?
 | ||||
| export async function setBet(bet: Bet) { | ||||
|   const docRef = doc(db, 'bets', bet.id) | ||||
|   await setDoc(docRef, bet) | ||||
| } | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ import { Html, Head, Main, NextScript } from 'next/document' | |||
| 
 | ||||
| export default function Document() { | ||||
|   return ( | ||||
|     <Html data-theme="dark"> | ||||
|     <Html data-theme="dark" className="h-full bg-gray-900"> | ||||
|       <Head> | ||||
|         <title>Mantic Markets</title> | ||||
| 
 | ||||
|  | @ -38,12 +38,7 @@ export default function Document() { | |||
|           crossOrigin="true" | ||||
|         /> | ||||
|         <link | ||||
|           href="https://fonts.googleapis.com/css2?family=Major+Mono+Display&display=swap" | ||||
|           rel="stylesheet" | ||||
|         /> | ||||
| 
 | ||||
|         <link | ||||
|           href="https://fonts.googleapis.com/css2?family=Inter&display=optional" | ||||
|           href="https://fonts.googleapis.com/css2?family=Major+Mono+Display&family=Readex+Pro:wght@400;700&display=swap" | ||||
|           rel="stylesheet" | ||||
|         /> | ||||
| 
 | ||||
|  | @ -62,7 +57,7 @@ export default function Document() { | |||
|           }} | ||||
|         /> | ||||
|       </Head> | ||||
|       <body> | ||||
|       <body className="h-full font-readex-pro"> | ||||
|         <Main /> | ||||
|         <NextScript /> | ||||
|       </body> | ||||
|  |  | |||
|  | @ -2,21 +2,31 @@ import { useRouter } from 'next/router' | |||
| import { firebaseLogout } from '../lib/firebase/users' | ||||
| import { Header } from '../components/header' | ||||
| import { useUser } from '../hooks/use-user' | ||||
| import { useState, useEffect } from 'react' | ||||
| import { Contract, listContracts } from '../lib/firebase/contracts' | ||||
| import { ContractList } from './contract' | ||||
| 
 | ||||
| export default function Account() { | ||||
|   const user = useUser() | ||||
|   const router = useRouter() | ||||
|   const [contracts, setContracts] = useState<Contract[]>([]) | ||||
|   useEffect(() => { | ||||
|     if (user?.id) { | ||||
|       listContracts(user?.id).then(setContracts) | ||||
|     } | ||||
|   }, [user?.id]) | ||||
| 
 | ||||
|   return ( | ||||
|     <div className="relative overflow-hidden h-screen bg-cover bg-gray-900"> | ||||
|       <Header /> | ||||
|       <div className="flex items-center w-full h-max px-4 py-10 bg-cover card"> | ||||
|         <div className="card glass lg:card-side text-neutral-content bg-gray-800 m-10 transition-all"> | ||||
|       <div className="max-w-4xl my-20 mx-auto"> | ||||
|         <div> | ||||
|           <div className="card glass lg:card-side text-neutral-content bg-gray-800 transition-all max-w-sm mx-auto my-20"> | ||||
|             <figure className="p-6"> | ||||
|               <img src={user?.avatarUrl} className="rounded-lg shadow-lg" /> | ||||
|             </figure> | ||||
|             <div className="max-w-md card-body"> | ||||
|             <h2 className="card-title">{user?.name}</h2> | ||||
|               <h2 className="card-title font-major-mono">{user?.name}</h2> | ||||
|               <p>{user?.email}</p> | ||||
|               <p>${user?.balanceUsd} USD</p> | ||||
|               <div className="card-actions"> | ||||
|  | @ -32,82 +42,12 @@ export default function Account() { | |||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
| 
 | ||||
|         {/* Lorem ipsum table. TODO: fill in user's bets and markets */} | ||||
|         <h1 className="text-4xl text-neutral-content m-4"> | ||||
|           {user?.username}'s Bets | ||||
|         </h1> | ||||
|         <div className="overflow-x-auto"> | ||||
|           <table className="table table-compact"> | ||||
|             <thead> | ||||
|               <tr> | ||||
|                 <th></th> | ||||
|                 <th>Name</th> | ||||
|                 <th>Job</th> | ||||
|                 <th>company</th> | ||||
|                 <th>location</th> | ||||
|                 <th>Last Login</th> | ||||
|                 <th>Favorite Color</th> | ||||
|               </tr> | ||||
|             </thead> | ||||
|             <tbody> | ||||
|               <tr> | ||||
|                 <th>1</th> | ||||
|                 <td>Cy Ganderton</td> | ||||
|                 <td>Quality Control Specialist</td> | ||||
|                 <td>Littel, Schaden and Vandervort</td> | ||||
|                 <td>Canada</td> | ||||
|                 <td>12/16/2020</td> | ||||
|                 <td>Blue</td> | ||||
|               </tr> | ||||
|               <tr> | ||||
|                 <th>2</th> | ||||
|                 <td>Hart Hagerty</td> | ||||
|                 <td>Desktop Support Technician</td> | ||||
|                 <td>Zemlak, Daniel and Leannon</td> | ||||
|                 <td>United States</td> | ||||
|                 <td>12/5/2020</td> | ||||
|                 <td>Purple</td> | ||||
|               </tr> | ||||
|               <tr> | ||||
|                 <th>3</th> | ||||
|                 <td>Brice Swyre</td> | ||||
|                 <td>Tax Accountant</td> | ||||
|                 <td>Carroll Group</td> | ||||
|                 <td>China</td> | ||||
|                 <td>8/15/2020</td> | ||||
|                 <td>Red</td> | ||||
|               </tr> | ||||
|               <tr> | ||||
|                 <th>4</th> | ||||
|                 <td>Marjy Ferencz</td> | ||||
|                 <td>Office Assistant I</td> | ||||
|                 <td>Rowe-Schoen</td> | ||||
|                 <td>Russia</td> | ||||
|                 <td>3/25/2021</td> | ||||
|                 <td>Crimson</td> | ||||
|               </tr> | ||||
|               <tr> | ||||
|                 <th>5</th> | ||||
|                 <td>Yancy Tear</td> | ||||
|                 <td>Community Outreach Specialist</td> | ||||
|                 <td>Wyman-Ledner</td> | ||||
|                 <td>Brazil</td> | ||||
|                 <td>5/22/2020</td> | ||||
|                 <td>Indigo</td> | ||||
|               </tr> | ||||
|               <tr> | ||||
|                 <th>6</th> | ||||
|                 <td>Irma Vasilik</td> | ||||
|                 <td>Editor</td> | ||||
|                 <td>Wiza, Bins and Emard</td> | ||||
|                 <td>Venezuela</td> | ||||
|                 <td>12/8/2020</td> | ||||
|                 <td>Purple</td> | ||||
|               </tr> | ||||
|             </tbody> | ||||
|           </table> | ||||
|         </div> | ||||
| 
 | ||||
|         <h1 className="text-2xl font-major-mono text-indigo-300 font-bold mt-6 mb-4"> | ||||
|           Your markets | ||||
|         </h1> | ||||
|         <ContractList contracts={contracts} /> | ||||
|       </div> | ||||
|     </div> | ||||
|   ) | ||||
|  |  | |||
							
								
								
									
										237
									
								
								web/pages/contract/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										237
									
								
								web/pages/contract/index.tsx
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,237 @@ | |||
| import Link from 'next/link' | ||||
| import { useEffect, useState } from 'react' | ||||
| import { Header } from '../../components/header' | ||||
| import { useUser } from '../../hooks/use-user' | ||||
| import { | ||||
|   Contract, | ||||
|   deleteContract, | ||||
|   listContracts, | ||||
|   setContract as pushContract, | ||||
| } from '../../lib/firebase/contracts' | ||||
| 
 | ||||
| function ContractCard(props: { contract: Contract }) { | ||||
|   const { contract } = props | ||||
|   return ( | ||||
|     <li> | ||||
|       <Link href={`/contract/${contract.id}`}> | ||||
|         <a className="block hover:bg-gray-600"> | ||||
|           <div className="px-4 py-4 sm:px-6"> | ||||
|             <div className="flex items-center justify-between"> | ||||
|               <p className="text-sm font-medium text-indigo-300 truncate"> | ||||
|                 {contract.question} | ||||
|               </p> | ||||
|               <div className="ml-2 flex-shrink-0 flex"> | ||||
|                 <p className="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800"> | ||||
|                   {contract.outcomeType} | ||||
|                 </p> | ||||
|               </div> | ||||
|             </div> | ||||
|             <div className="mt-2 sm:flex sm:justify-between"> | ||||
|               <div className="sm:flex"> | ||||
|                 <p className="flex items-center text-sm">{contract.id}</p> | ||||
|                 <p className="mt-2 flex items-center text-sm sm:mt-0 sm:ml-6"> | ||||
|                   {contract.description} | ||||
|                 </p> | ||||
|               </div> | ||||
|               <div className="mt-2 flex items-center text-sm sm:mt-0"> | ||||
|                 <p> | ||||
|                   Created on{' '} | ||||
|                   <time dateTime={`${contract.createdTime}`}> | ||||
|                     {new Date(contract.createdTime).toLocaleString()} | ||||
|                   </time> | ||||
|                 </p> | ||||
|                 <button | ||||
|                   className="btn btn-sm btn-error ml-2" | ||||
|                   onClick={() => { | ||||
|                     deleteContract(contract.id) | ||||
|                   }} | ||||
|                 > | ||||
|                   Delete | ||||
|                 </button> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         </a> | ||||
|       </Link> | ||||
|     </li> | ||||
|   ) | ||||
| } | ||||
| 
 | ||||
| export function ContractList(props: { contracts: Contract[] }) { | ||||
|   const { contracts } = props | ||||
|   return ( | ||||
|     <div className="bg-gray-500 shadow overflow-hidden sm:rounded-md max-w-4xl w-full"> | ||||
|       <ul role="list" className="divide-y divide-gray-200"> | ||||
|         {contracts.map((contract) => ( | ||||
|           <ContractCard contract={contract} key={contract.id} /> | ||||
|         ))} | ||||
|       </ul> | ||||
|     </div> | ||||
|   ) | ||||
| } | ||||
| 
 | ||||
| // Allow user to create a new contract
 | ||||
| // TODO: Extract to a reusable UI, for listing contracts too?
 | ||||
| export default function NewContract() { | ||||
|   const creator = useUser() | ||||
|   const [contract, setContract] = useState<Contract>({ | ||||
|     id: '', | ||||
|     creatorId: '', | ||||
|     question: '', | ||||
|     description: '', | ||||
|     seedAmounts: { YES: 100, NO: 100 }, | ||||
| 
 | ||||
|     // TODO: Set create time to Firestore timestamp
 | ||||
|     createdTime: Date.now(), | ||||
|     lastUpdatedTime: Date.now(), | ||||
|   } as Contract) | ||||
| 
 | ||||
|   const [contracts, setContracts] = useState<Contract[]>([]) | ||||
|   useEffect(() => { | ||||
|     if (creator?.id) { | ||||
|       setContract((contract) => ({ | ||||
|         ...contract, | ||||
|         creatorId: creator.id, | ||||
|         creatorName: creator.name, | ||||
|       })) | ||||
|       listContracts(creator?.id).then(setContracts) | ||||
|     } | ||||
|   }, [creator?.id]) | ||||
| 
 | ||||
|   async function saveContract() { | ||||
|     await pushContract(contract) | ||||
|     // Update local contract list
 | ||||
|     setContracts([{ ...contract }, ...contracts]) | ||||
|   } | ||||
| 
 | ||||
|   function saveField(field: keyof Contract) { | ||||
|     return (changeEvent: React.ChangeEvent<any>) => | ||||
|       setContract({ ...contract, [field]: changeEvent.target.value }) | ||||
|   } | ||||
| 
 | ||||
|   const descriptionPlaceholder = `e.g. This market will resolve to “Yes” if, by June 2, 2021, 11:59:59 PM ET, Paxlovid (also known under PF-07321332)...` | ||||
| 
 | ||||
|   return ( | ||||
|     <div> | ||||
|       <Header /> | ||||
|       <div className="max-w-4xl my-20 lg:mx-auto mx-4"> | ||||
|         <h1 className="text-2xl font-major-mono text-indigo-300 font-bold mt-6 mb-4"> | ||||
|           Create a new prediction market | ||||
|         </h1> | ||||
|         <div className="w-full bg-gray-500 rounded-lg shadow-xl p-6"> | ||||
|           {/* Create a Tailwind form that takes in all the fields needed for a new contract */} | ||||
|           {/* When the form is submitted, create a new contract in the database */} | ||||
|           <form> | ||||
|             <div className="form-control"> | ||||
|               <label className="label"> | ||||
|                 <span className="label-text">Contract ID</span> | ||||
|               </label> | ||||
|               <input | ||||
|                 type="text" | ||||
|                 placeholder="e.g. COVID-123" | ||||
|                 className="input" | ||||
|                 value={contract.id} | ||||
|                 onChange={saveField('id')} | ||||
|               /> | ||||
|             </div> | ||||
| 
 | ||||
|             <div className="form-control"> | ||||
|               <label className="label"> | ||||
|                 <span className="label-text">Question</span> | ||||
|               </label> | ||||
|               <input | ||||
|                 type="text" | ||||
|                 placeholder="e.g. Will the FDA approve Paxlovid before Jun 2nd, 2022?" | ||||
|                 className="input" | ||||
|                 value={contract.question} | ||||
|                 onChange={saveField('question')} | ||||
|               /> | ||||
|             </div> | ||||
| 
 | ||||
|             <div className="form-control"> | ||||
|               <label className="label"> | ||||
|                 <span className="label-text">Description</span> | ||||
|               </label> | ||||
|               <textarea | ||||
|                 className="textarea h-24 textarea-bordered" | ||||
|                 placeholder={descriptionPlaceholder} | ||||
|                 value={contract.description} | ||||
|                 onChange={saveField('description')} | ||||
|               ></textarea> | ||||
|             </div> | ||||
| 
 | ||||
|             <div className="mt-6 grid grid-cols-1 gap-y-6 gap-x-4 sm:grid-cols-6"> | ||||
|               <div className="sm:col-span-3"> | ||||
|                 <div className="form-control"> | ||||
|                   <label className="label"> | ||||
|                     <span className="label-text">Yes Seed</span> | ||||
|                   </label> | ||||
|                   <input | ||||
|                     type="number" | ||||
|                     placeholder="100" | ||||
|                     className="input" | ||||
|                     value={contract.seedAmounts.YES} | ||||
|                     onChange={(e) => { | ||||
|                       setContract({ | ||||
|                         ...contract, | ||||
|                         seedAmounts: { | ||||
|                           ...contract.seedAmounts, | ||||
|                           YES: parseInt(e.target.value), | ||||
|                         }, | ||||
|                       }) | ||||
|                     }} | ||||
|                   /> | ||||
|                 </div> | ||||
|               </div> | ||||
| 
 | ||||
|               <div className="sm:col-span-3"> | ||||
|                 <div className="form-control"> | ||||
|                   <label className="label"> | ||||
|                     <span className="label-text">No Seed</span> | ||||
|                   </label> | ||||
|                   <input | ||||
|                     type="number" | ||||
|                     placeholder="100" | ||||
|                     className="input" | ||||
|                     value={contract.seedAmounts.NO} | ||||
|                     onChange={(e) => { | ||||
|                       setContract({ | ||||
|                         ...contract, | ||||
|                         seedAmounts: { | ||||
|                           ...contract.seedAmounts, | ||||
|                           NO: parseInt(e.target.value), | ||||
|                         }, | ||||
|                       }) | ||||
|                     }} | ||||
|                   /> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
| 
 | ||||
|             {/* TODO: Show a preview of the created market here? */} | ||||
| 
 | ||||
|             <div className="flex justify-end mt-6"> | ||||
|               <button | ||||
|                 type="submit" | ||||
|                 className="btn btn-primary" | ||||
|                 onClick={(e) => { | ||||
|                   e.preventDefault() | ||||
|                   saveContract() | ||||
|                 }} | ||||
|               > | ||||
|                 Create market | ||||
|               </button> | ||||
|             </div> | ||||
|           </form> | ||||
|         </div> | ||||
| 
 | ||||
|         {/* Show a separate card for each contract */} | ||||
|         <h1 className="text-2xl  font-major-mono text-indigo-300 font-bold mt-6 mb-4"> | ||||
|           Your markets | ||||
|         </h1> | ||||
| 
 | ||||
|         <ContractList contracts={contracts} /> | ||||
|       </div> | ||||
|     </div> | ||||
|   ) | ||||
| } | ||||
|  | @ -249,7 +249,7 @@ export default function Simulator() { | |||
|   } | ||||
| 
 | ||||
|   return ( | ||||
|     <div className="relative overflow-hidden h-screen bg-gray-900"> | ||||
|     <div> | ||||
|       <Header /> | ||||
|       <div className="grid grid-cols-1 xl:grid-cols-2 gap-4 mt-8 max-w-7xl mx-auto text-center"> | ||||
|         {/* Left column */} | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ module.exports = { | |||
|   theme: { | ||||
|     fontFamily: { | ||||
|       'major-mono': ['Major Mono Display', 'monospace'], | ||||
|       'readex-pro': ['Readex Pro', 'sans-serif'], | ||||
|     }, | ||||
|     extend: { | ||||
|       backgroundImage: { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user