mirror of
https://github.com/aljazceru/mutiny-web.git
synced 2026-01-31 12:04:23 +01:00
waitlist mockup
This commit is contained in:
@@ -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
43
pnpm-lock.yaml
generated
@@ -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'}
|
||||
|
||||
122
src/App.tsx
122
src/App.tsx
@@ -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
115
src/Home.tsx
Normal 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
42
src/Join.tsx
Normal 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
19
src/Receive.tsx
Normal 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
23
src/components/button.ts
Normal 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
|
||||
11
src/main.tsx
11
src/main.tsx
@@ -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>,
|
||||
)
|
||||
|
||||
@@ -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%)'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user