Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Commit

Permalink
feat(wallet-lifecycle): scaffold example UI (#35)
Browse files Browse the repository at this point in the history
* feat(wallet-lifecycle): scaffold example UI

* fix react types

* sync
  • Loading branch information
eli-lim authored Oct 10, 2023
1 parent 8d3a842 commit 1a27427
Show file tree
Hide file tree
Showing 23 changed files with 661 additions and 13 deletions.
33 changes: 33 additions & 0 deletions examples/wallet-lifecycle/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
5 changes: 5 additions & 0 deletions examples/wallet-lifecycle/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.next
node_modules
vercel.json
next-env.d.ts
!.gitignore
60 changes: 60 additions & 0 deletions examples/wallet-lifecycle/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
title: Wallet Lifecycle
description: Create wallets and execute transactions.
framework: Next.js
publisher: Levain
template:
prompts: []
messages:
postInstall: To start the app, run `npm run dev` and open http://localhost:3000 with your browser.
---

This is a [Next.js](https://nextjs.org/) app bootstrapped
with [`create-levain-app`](https://github.com/levaintech/levain-examples/tree/main/packages/create-levain-app).

## Getting Started

First, run the development server:

```bash
npm run dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

## Learn More

### Levain

To learn more about [Levain](https://developer.levain.tech/), take a look at the following resources:

- [Levain GraphQL API](https://developer.levain.tech/products/graph/docs/about-graphql) - learn about Levain GraphQL
API.
- [Viewer Queries](https://developer.levain.tech/products/graph/docs/user-guides/query-viewer) - learn about Viewer
Queries.
- [Organization Queries](https://developer.levain.tech/products/graph/docs/user-guides/query-organization) - learn about
Organization Queries.
- [Wallet API](https://developer.levain.tech/products/graph/docs/features/wallets) - learn about Wallet API.
- [Deposit Address API](https://developer.levain.tech/products/graph/docs/features/deposit-address) - learn about
Deposit Address API.
- [Transaction API](https://developer.levain.tech/products/graph/docs/features/transactions) - learn about Transaction
API.

### Next.js

To learn more about [Next.js](https://nextjs.org/), take a look at the following resources:

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.

## Going Live

### Going Live on Levain

If you need help with going live on Levain, feel free to reach out to our team at https://support.levain.tech

### Deploying the App

The easiest way to deploy a Next.js app is to use the [Vercel Platform](https://vercel.com/new) from the creators of Next.js.

Check out their [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
37 changes: 37 additions & 0 deletions examples/wallet-lifecycle/app/RootHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { ReactElement } from 'react';
import { ThemeSelector } from '@/components/ThemeSelector';
import { ActiveLink } from '@/components/ActiveLink';

export function RootHeader(): ReactElement {
const links = [
{
href: '/',
label: 'Wallet Overview',
},
{
href: '/transactions',
label: 'Transactions',
},
];

return (
<div className="mx-auto my-6 w-full max-w-screen-xl px-6 lg:my-10 lg:px-10">
<div className="flex justify-between">
<h1 className="text-mono-50 text-3xl font-bold">Levain Wallet</h1>
<ThemeSelector />
</div>
<div className="border-b-mono-800 mt-5 flex gap-4 border-b">
{links.map((link) => (
<ActiveLink
key={link.href}
href={link.href}
className="text-mono-500 block px-2 pb-2.5 pt-3 text-sm font-bold"
activeClassName="!text-mono-50 border-b-2 border-b-mono-50"
>
{link.label}
</ActiveLink>
))}
</div>
</div>
);
}
Binary file added examples/wallet-lifecycle/app/favicon.ico
Binary file not shown.
57 changes: 57 additions & 0 deletions examples/wallet-lifecycle/app/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

.--tokens-dark {
color-scheme: dark;
--color-invert: 255 255 255;

--color-mono-50: 250 250 250;
--color-mono-100: 245 245 245;
--color-mono-200: 229 229 229;
--color-mono-300: 212 212 212;
--color-mono-400: 163 163 163;
--color-mono-500: 115 115 115;
--color-mono-600: 82 82 82;
--color-mono-700: 64 64 64;
--color-mono-800: 38 38 38;
--color-mono-900: 23 23 23;
--color-mono-950: 10 10 10;
}

.--tokens-light {
color-scheme: light;
--color-invert: 0 0 0;

--color-mono-50: 10 10 10;
--color-mono-100: 23 23 23;
--color-mono-200: 38 38 38;
--color-mono-300: 64 64 64;
--color-mono-400: 82 82 82;
--color-mono-500: 115 115 115;
--color-mono-600: 163 163 163;
--color-mono-700: 212 212 212;
--color-mono-800: 229 229 229;
--color-mono-900: 245 245 245;
--color-mono-950: 250 250 250;
}

@layer base {
:root {
@apply --tokens-dark;
}

@media (prefers-color-scheme: light) {
:root {
@apply --tokens-light;
}
}

html[data-theme='light'] {
@apply --tokens-light;
}

html[data-theme='dark'] {
@apply --tokens-dark;
}
}
34 changes: 34 additions & 0 deletions examples/wallet-lifecycle/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import './globals.css';
import { ReactElement, ReactNode } from 'react';
import { Metadata } from 'next';
import { ThemeScript, ThemeSelector } from '@/components/ThemeSelector';
import { MadeWithLevain } from '@/components/MadeWithLevain';

export const metadata: Metadata = {
title: 'Wallet Lifecycle — Levain Examples',
description:
'A demo app built with Levain, Next.js and Tailwind CSS. ' +
'Start, run and grow your crypto business with enterprise-grade security and self-custody wallet infrastructure.',
};

export default function RootLayout(props: { children: ReactNode }): ReactElement {
return (
<html lang="en">
<head>
<ThemeScript />
</head>
<body className="bg-mono-950 text-mono-200">
<div className="mx-auto mt-6 w-full max-w-screen-xl px-6 lg:mt-10 lg:px-10">
<div className="flex justify-between">
<h1 className="text-mono-50 text-3xl font-bold">Levain NeoBank</h1>
<ThemeSelector />
</div>
</div>

{props.children}

<MadeWithLevain />
</body>
</html>
);
}
52 changes: 52 additions & 0 deletions examples/wallet-lifecycle/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { ReactElement } from 'react';
import Link from 'next/link';
import { StyledTable } from '@/components/StyledTable';

export default function WalletPage(): ReactElement {
return (
<main className="mx-auto my-6 w-full max-w-screen-xl px-6 lg:my-10 lg:px-10">
<div className="border-mono-50 border">
<div className="p-5">
<h2>Omnibus Wallets</h2>
</div>
<StyledTable>
<thead>
<tr>
<th>Name</th>
<th>Blockchain CAIP19 Identifier</th>
<th>Link to Scan</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>Wallet #1</td>
<td>caip2:eip155:421613</td>
<td>0xd54c050540bb15c7be6e16d8f65dbd42a890414e2b6a0b96b7312a5bbcb8ae94</td>
<td>
<div className="flex">
<Link href="" className="border-mono-50 block border px-4 py-1 font-bold">
Send
</Link>
</div>
</td>
</tr>
<tr>
<td>Wallet #1</td>
<td>caip2:eip155:421613</td>
<td>0xd54c050540bb15c7be6e16d8f65dbd42a890414e2b6a0b96b7312a5bbcb8ae94</td>
<td>
<div className="flex">
<Link href="" className="border-mono-50 block border px-4 py-1 font-bold">
Send
</Link>
</div>
</td>
</tr>
</tbody>
</StyledTable>
<div className="border-mono-50 border-t px-5 py-2.5 text-sm">Total Records:</div>
</div>
</main>
);
}
9 changes: 9 additions & 0 deletions examples/wallet-lifecycle/app/wallets/[walletId]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ReactElement } from 'react';

export default function WalletPage(): ReactElement {
return (
<div className="mt-5">
<h1>Wallet Page</h1>
</div>
);
}
37 changes: 37 additions & 0 deletions examples/wallet-lifecycle/app/wallets/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { ReactElement, ReactNode } from 'react';
import { ActiveLink } from '@/components/ActiveLink';

export default function WalletLayout(props: { children: ReactNode }): ReactElement {
const links = [
{
href: '/',
label: 'Wallet Overview',
},
{
href: '/deposit-addresses',
label: 'Deposit Addresses',
},
{
href: '/events',
label: 'Events',
},
];

return (
<div className="mx-auto w-full max-w-screen-xl px-6 lg:px-10">
<div className="border-b-mono-800 mt-5 flex gap-4 border-b">
{links.map((link) => (
<ActiveLink
key={link.href}
href={link.href}
className="text-mono-500 block px-2 pb-2.5 pt-3 text-sm font-bold"
activeClassName="!text-mono-50 border-b-2 border-b-mono-50"
>
{link.label}
</ActiveLink>
))}
</div>
{props.children}
</div>
);
}
29 changes: 29 additions & 0 deletions examples/wallet-lifecycle/components/ActiveLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use client';
import { clsx } from 'clsx';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { ComponentProps, ReactElement, ReactNode } from 'react';

/**
* ActiveLink is a wrapper around Next.js Link that adds an `activeClassName` prop.
* This prop is used to add a class to the link when the current path matches the link's href.
* This is useful for styling active links.
*/
export function ActiveLink(
props: Omit<ComponentProps<typeof Link>, 'href'> & {
children: ReactNode;
activeClassName: string;
href: string;
mode?: 'prefix' | 'exact';
},
): ReactElement {
const pathname = usePathname();
const { children, className, mode, activeClassName, ...rest } = props;
const isActive = mode === 'prefix' ? pathname.startsWith(props.href) : pathname === props.href;

return (
<Link {...rest} className={clsx(className, { [activeClassName]: isActive })}>
{children}
</Link>
);
}
9 changes: 9 additions & 0 deletions examples/wallet-lifecycle/components/LevainLogo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ReactElement, SVGProps } from 'react';

export function LevainLogo(props: SVGProps<any>): ReactElement {
return (
<svg viewBox="0 0 40 40" width="40" height="40" {...props}>
<path d="M5.30223046,5.97241118 C9.50701377,1.74864452 15.5988601,-0.638164373 22.2353483,0.149106738 C31.4428521,1.23628896 38.8455436,8.60914452 39.8714274,17.7190001 C41.2265849,29.7842223 31.6898074,40 19.7340388,40 C14.0790657,40 8.96874574,37.706889 5.29589047,34.0204445 C4.77029115,33.4894445 4.97293416,32.6021112 5.68217906,32.3584445 L5.68217906,32.3584445 L9.55425753,30.2914445 C10.1938414,30.0664445 10.8967576,30.2164445 11.4286857,30.6350001 C14.0186874,32.6656667 17.5458554,33.6666667 20,33.6666667 C28.3333333,33.6666667 33.6666667,26.6666667 33.6666667,20 C33.6666667,13.3333333 28.3333333,6.33333333 20,6.33333333 C17.32261,6.33333333 14.1073121,7.23972229 11.3843621,9.38910007 C10.8524228,9.8077334 10.1305204,9.90145563 9.4846078,9.67651118 L9.4846078,9.67651118 L5.68217906,7.63442229 C4.97293416,7.39074452 4.77663113,6.49725563 5.30223046,5.97241118 Z M0.8722501,14.0538302 C1.23874378,12.8250123 2.48375611,12.2077005 3.58863506,12.6351108 L3.58863506,12.6351108 L16.8902835,17.7579123 C18.8143499,18.4998899 18.8143499,21.497568 16.8902835,22.2395456 L16.8902835,22.2395456 L16.8956508,22.2395456 L3.59402149,27.3682584 C2.4568048,27.8015801 1.22257491,27.1307496 0.850690972,25.8723751 C-0.329642057,21.8774766 -0.243407461,17.7579123 0.8722501,14.0538302 Z" />
</svg>
);
}
18 changes: 18 additions & 0 deletions examples/wallet-lifecycle/components/MadeWithLevain.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { ReactElement } from 'react';
import { LevainLogo } from '@/components/LevainLogo';
import Link from 'next/link';

export function MadeWithLevain(): ReactElement {
return (
<div className="fixed bottom-4 right-4">
<Link
href="https://levain.tech"
target="_blank"
className="border-mono-800 hover:bg-invert/2.5 flex items-center gap-1.5 rounded border px-2.5 py-1.5"
>
<LevainLogo className="h-3.5 w-3.5 fill-current" />
<div className="text-mono-300 text-xs font-bold">Build with Levain</div>
</Link>
</div>
);
}
Loading

1 comment on commit 1a27427

@vercel
Copy link

@vercel vercel bot commented on 1a27427 Oct 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.