waitlist mockup

This commit is contained in:
Paul Miller
2023-02-11 16:25:35 -06:00
parent 16431b247e
commit a8d8157b58
9 changed files with 272 additions and 110 deletions

View File

@@ -9,10 +9,12 @@
"preview": "vite preview"
},
"dependencies": {
"class-variance-authority": "^0.4.0",
"framer-motion": "^8.5.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-modal-sheet": "^1.10.0"
"react-modal-sheet": "^1.10.0",
"react-router-dom": "^6.8.0"
},
"devDependencies": {
"@types/react": "^18.0.26",

43
pnpm-lock.yaml generated
View File

@@ -5,21 +5,25 @@ specifiers:
'@types/react-dom': ^18.0.9
'@vitejs/plugin-react': ^3.0.0
autoprefixer: ^10.4.13
class-variance-authority: ^0.4.0
framer-motion: ^8.5.4
postcss: ^8.4.21
react: ^18.2.0
react-dom: ^18.2.0
react-modal-sheet: ^1.10.0
react-router-dom: ^6.8.0
tailwindcss: ^3.2.4
typescript: ^4.9.3
vite: ^4.0.0
vite-plugin-pwa: ^0.14.1
dependencies:
class-variance-authority: 0.4.0_typescript@4.9.4
framer-motion: 8.5.4_react-dom@18.2.0+react@18.2.0
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
react-modal-sheet: 1.10.0_framer-motion@8.5.4+react@18.2.0
react-router-dom: 6.8.0_react-dom@18.2.0+react@18.2.0
devDependencies:
'@types/react': 18.0.27
@@ -1576,6 +1580,11 @@ packages:
react: 18.2.0
dev: false
/@remix-run/router/1.3.1:
resolution: {integrity: sha512-+eun1Wtf72RNRSqgU7qM2AMX/oHp+dnx7BHk1qhK5ZHzdHTUU4LA1mGG1vT+jMc8sbhG3orvsfOmryjzx2PzQw==}
engines: {node: '>=14'}
dev: false
/@rollup/plugin-babel/5.3.1_d8e457a9eec5694be0a6185ede2794cb:
resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==}
engines: {node: '>= 10.0.0'}
@@ -1957,6 +1966,17 @@ packages:
fsevents: 2.3.2
dev: true
/class-variance-authority/0.4.0_typescript@4.9.4:
resolution: {integrity: sha512-74enNN8O9ZNieycac/y8FxqgyzZhZbxmCitAtAeUrLPlxjSd5zA7LfpprmxEcOmQBnaGs5hYhiSGnJ0mqrtBLQ==}
peerDependencies:
typescript: '>= 4.5.5 < 5'
peerDependenciesMeta:
typescript:
optional: true
dependencies:
typescript: 4.9.4
dev: false
/clsx/1.2.1:
resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==}
engines: {node: '>=6'}
@@ -2929,6 +2949,29 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
/react-router-dom/6.8.0_react-dom@18.2.0+react@18.2.0:
resolution: {integrity: sha512-hQouduSTywGJndE86CXJ2h7YEy4HYC6C/uh19etM+79FfQ6cFFFHnHyDlzO4Pq0eBUI96E4qVE5yUjA00yJZGQ==}
engines: {node: '>=14'}
peerDependencies:
react: '>=16.8'
react-dom: '>=16.8'
dependencies:
'@remix-run/router': 1.3.1
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
react-router: 6.8.0_react@18.2.0
dev: false
/react-router/6.8.0_react@18.2.0:
resolution: {integrity: sha512-760bk7y3QwabduExtudhWbd88IBbuD1YfwzpuDUAlJUJ7laIIcqhMvdhSVh1Fur1PE8cGl84L0dxhR3/gvHF7A==}
engines: {node: '>=14'}
peerDependencies:
react: '>=16.8'
dependencies:
'@remix-run/router': 1.3.1
react: 18.2.0
dev: false
/react/18.2.0:
resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
engines: {node: '>=0.10.0'}

View File

@@ -1,115 +1,21 @@
import Sheet from 'react-modal-sheet';
import { useState } from 'react';
import logo from './assets/mutiny-logo.svg';
import mutiny_m from './assets/m.svg';
import scan from './assets/scan.svg';
import settings from './assets/settings.svg';
import send from './assets/send.svg';
function ActivityItem() {
return (
<div className="flex flex-row border-b border-gray-500 gap-4 py-2">
<img src={send} className="App-logo" alt="logo" />
<div className='flex flex-col flex-1'>
<h1>Bitcoin Beefsteak</h1>
<h2>-1,441,851 SAT</h2>
<h3 className='text-sm text-gray-500'>Jul 24</h3>
</div>
<div className='text-sm font-semibold uppercase text-[#E23A5E]'>SEND</div>
</div>
)
}
import { Routes, Route } from "react-router-dom";
import { AnimatePresence } from "framer-motion";
import Home from "./Home";
import Receive from "./Receive";
import Join from "./Join";
function App() {
const [isOpen, setOpen] = useState(false);
return (
<div className="safe-top safe-left safe-right safe-bottom">
<div className="disable-scrollbars max-h-screen h-full overflow-y-scroll mx-4">
<main className='flex flex-col gap-4 py-8'>
<header>
<img src={logo} className="App-logo" alt="logo" />
</header>
<div className='border border-white rounded-xl border-b-4 p-4 flex flex-col gap-2'>
<header className='text-sm font-semibold uppercase'>
Balance
</header>
<div>
<h1 className='text-4xl font-light'>
69,420 <span className='text-xl'>SAT</span>
</h1>
</div>
<div className="flex gap-2 py-4">
<button onClick={() => setOpen(true)} className='bg-[#1EA67F] p-4 flex-1 rounded-xl text-xl font-semibold '><span className="drop-shadow-sm shadow-black">Send</span></button>
<button className='bg-[#3B6CCC] p-4 flex-1 rounded-xl text-xl font-semibold '><span className="drop-shadow-sm shadow-black">Receive</span></button>
</div>
</div>
<div className='rounded-xl p-4 flex flex-col gap-2 bg-[rgba(0,0,0,0.5)]'>
<header className='text-sm font-semibold uppercase'>
Activity
</header>
<ActivityItem />
<ActivityItem />
<ActivityItem />
<ActivityItem />
<ActivityItem />
<ActivityItem />
<ActivityItem />
<div className='flex justify-end py-4'>
<a href="#" className='underline text-sm'>
MORE
</a>
</div>
</div>
{/* safety div */}
<div className="h-32" />
</main>
<div className="App">
{/* globals such as header will go here */}
</div>
<Sheet isOpen={isOpen} onClose={() => setOpen(false)}>
<Sheet.Container>
<Sheet.Header />
<Sheet.Content>
<div className='p-4 flex flex-col gap-2'>
<header className='text-sm font-semibold uppercase'>
Activity
</header>
<ActivityItem />
<h1 className='text-4xl font-light'>
It's a sheet! Like a modal, but a sheet.
</h1>
</div>
</Sheet.Content>
</Sheet.Container>
<Sheet.Backdrop />
</Sheet>
<nav className='bg-black fixed bottom-0 shadow-lg z-40 w-full safe-bottom'>
<ul className='h-16 flex justify-between px-16 items-center'>
<li className='h-full border-t-2 border-b-2 border-b-black flex flex-col justify-center'>
<img src={mutiny_m} className="App-logo" alt="logo" />
</li>
<li>
<img src={scan} className="App-logo" alt="logo" />
</li>
<li>
<img src={settings} className="App-logo" alt="logo" />
</li>
{/* <li>home</li> */}
{/* <li>scan</li> */}
{/* <li>settings</li> */}
</ul>
</nav>
</div >
)
<Routes>
{/* <Route path="/" element={<Home />} /> */}
<Route path="/" element={<Join />} />
</Routes>
</div>
);
}
export default App
export default App;

115
src/Home.tsx Normal file
View File

@@ -0,0 +1,115 @@
import Sheet from 'react-modal-sheet';
import { useState } from 'react';
import logo from './assets/mutiny-logo.svg';
import mutiny_m from './assets/m.svg';
import scan from './assets/scan.svg';
import settings from './assets/settings.svg';
import send from './assets/send.svg';
function ActivityItem() {
return (
<div className="flex flex-row border-b border-gray-500 gap-4 py-2">
<img src={send} className="App-logo" alt="logo" />
<div className='flex flex-col flex-1'>
<h1>Bitcoin Beefsteak</h1>
<h2>-1,441,851 SAT</h2>
<h3 className='text-sm text-gray-500'>Jul 24</h3>
</div>
<div className='text-sm font-semibold uppercase text-[#E23A5E]'>SEND</div>
</div>
)
}
function App() {
const [isOpen, setOpen] = useState(false);
return (
<div className="safe-top safe-left safe-right safe-bottom">
<div className="disable-scrollbars max-h-screen h-full overflow-y-scroll mx-4">
<main className='flex flex-col gap-4 py-8'>
<header>
<img src={logo} className="App-logo" alt="logo" />
</header>
<div className='border border-white rounded-xl border-b-4 p-4 flex flex-col gap-2'>
<header className='text-sm font-semibold uppercase'>
Balance
</header>
<div>
<h1 className='text-4xl font-light'>
69,420 <span className='text-xl'>SAT</span>
</h1>
</div>
<div className="flex gap-2 py-4">
<button onClick={() => setOpen(true)} className='bg-[#1EA67F] p-4 flex-1 rounded-xl text-xl font-semibold '><span className="drop-shadow-sm shadow-black">Send</span></button>
<button className='bg-[#3B6CCC] p-4 flex-1 rounded-xl text-xl font-semibold '><span className="drop-shadow-sm shadow-black">Receive</span></button>
</div>
</div>
<div className='rounded-xl p-4 flex flex-col gap-2 bg-[rgba(0,0,0,0.5)]'>
<header className='text-sm font-semibold uppercase'>
Activity
</header>
<ActivityItem />
<ActivityItem />
<ActivityItem />
<ActivityItem />
<ActivityItem />
<ActivityItem />
<ActivityItem />
<div className='flex justify-end py-4'>
<a href="#" className='underline text-sm'>
MORE
</a>
</div>
</div>
{/* safety div */}
<div className="h-32" />
</main>
</div>
<Sheet isOpen={isOpen} onClose={() => setOpen(false)}>
<Sheet.Container>
<Sheet.Header />
<Sheet.Content>
<div className='p-4 flex flex-col gap-2'>
<header className='text-sm font-semibold uppercase'>
Activity
</header>
<ActivityItem />
<h1 className='text-4xl font-light'>
It's a sheet! Like a modal, but a sheet.
</h1>
</div>
</Sheet.Content>
</Sheet.Container>
<Sheet.Backdrop />
</Sheet>
<nav className='bg-black fixed bottom-0 shadow-lg z-40 w-full safe-bottom'>
<ul className='h-16 flex justify-between px-16 items-center'>
<li className='h-full border-t-2 border-b-2 border-b-black flex flex-col justify-center'>
<img src={mutiny_m} className="App-logo" alt="logo" />
</li>
<li>
<img src={scan} className="App-logo" alt="logo" />
</li>
<li>
<img src={settings} className="App-logo" alt="logo" />
</li>
{/* <li>home</li> */}
{/* <li>scan</li> */}
{/* <li>settings</li> */}
</ul>
</nav>
</div >
)
}
export default App

42
src/Join.tsx Normal file
View File

@@ -0,0 +1,42 @@
import { useState } from "react";
import button from "./components/button"
const INPUT = "w-full mb-4 p-2 rounded-lg text-black"
export default function Join() {
let [nostr, setNostr] = useState(true);
return (
<div className="safe-top safe-left safe-right safe-bottom">
<div className="disable-scrollbars max-h-screen h-full overflow-y-scroll mx-4">
<main className='flex flex-col gap-4 py-8 max-w-xl mx-auto'>
<h1 className='text-4xl font-bold'>Join Waitlist</h1>
{/* HTML form with three inputs: nostr pubkey (text), email (text), and a textarea for comments */}
<div className="p-8 rounded-xl bg-half-black">
<div className="flex gap-4 mb-6">
<button className={button({ intent: nostr ? "active" : "inactive" })} onClick={() => setNostr(true)}><span className="drop-shadow-sm shadow-black">Nostr</span></button>
<button className={button({ intent: nostr ? "inactive" : "active" })} onClick={() => setNostr(false)}><span className="drop-shadow-sm shadow-black">Email</span></button>
</div>
<form className="flex flex-col items-start gap-2">
{nostr &&
<>
<label className="font-semibold" htmlFor="pubkey">Nostr npub or NIP-05</label>
<input className={INPUT} type="text" id="pubkey" name="pubkey" placeholder="npub..." />
</>
}
{
!nostr &&
<>
<label className="font-semibold" htmlFor="email">Email</label>
<input className={INPUT} type="text" id="email" name="email" placeholder="email@mutinywallet.com" />
</>
}
<label className="font-semibold" htmlFor="comments">Comments</label>
<textarea className={INPUT} id="comments" name="comments" rows={4} placeholder="I want a lightning wallet that does..." />
<button className={button({ intent: "red", layout: "pad" })}><span className="drop-shadow-sm shadow-black">Submit</span></button>
</form>
</div>
</main>
</div>
</div >
)
}

19
src/Receive.tsx Normal file
View File

@@ -0,0 +1,19 @@
import { Link } from "react-router-dom";
import { motion } from "framer-motion";
function Receive() {
const pageMotion = {
initial: { opacity: 0, x: 0 },
animate: { opacity: 1, x: 50, transition: { duration: 2 } },
exit: { opacity: 0, x: 0, transition: { duration: 2 } }
};
return (
<div className="about">
<motion.div initial="initial" animate="animate" exit="exit" variants={pageMotion}>about page</motion.div>
<Link to="/">Go to home page</Link>
</div>
)
}
export default Receive

23
src/components/button.ts Normal file
View File

@@ -0,0 +1,23 @@
import { cva } from "class-variance-authority";
const button = cva(["p-4", "rounded-xl", "text-xl", "font-semibold"], {
variants: {
intent: {
active: "bg-white text-black",
inactive: "bg-black text-white border border-white",
blue: "bg-[#3B6CCC] text-white",
red: "bg-[#F61D5B] text-white"
},
layout: {
flex: "flex-1",
pad: "px-8"
},
},
defaultVariants: {
intent: "inactive",
layout: "flex"
},
});
export default button

View File

@@ -1,10 +1,19 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
import App from './App'
import './index.css'
const router = createBrowserRouter([
{
path: "/",
element: <App />,
},
]);
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<App />
<RouterProvider router={router} />
{/* <App /> */}
</React.StrictMode>,
)

View File

@@ -8,6 +8,9 @@ module.exports = {
],
theme: {
extend: {
colors: {
"half-black": "rgba(0, 0, 0, 0.5)",
},
backgroundImage: {
'fade-to-blue': 'linear-gradient(1.63deg, #0B215B 32.05%, rgba(11, 33, 91, 0) 84.78%)'
}