aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2022-10-26 00:54:55 -0700
committerAdam Soltys <[email protected]>2022-10-26 00:55:58 -0700
commit9c95c7889ff6901bee13e77860172e03500cd9cd (patch)
tree0b673c6b0bb3c17cd577345a5787b543d593c219 /src
parent2091cf76cfa67d37dc8f058cc62d0aa1050eef3c (diff)
downloadcoinos-ui-9c95c7889ff6901bee13e77860172e03500cd9cd.tar.gz
coinos-ui-9c95c7889ff6901bee13e77860172e03500cd9cd.zip
transaction filters
Diffstat (limited to 'src')
-rw-r--r--src/components/AppHeader.svelte50
-rw-r--r--src/components/Avatar.svelte4
-rw-r--r--src/locales/en.json3
-rw-r--r--src/routes/[username]/transactions/[...page]/+page.server.js18
-rw-r--r--src/routes/[username]/transactions/[...page]/+page.svelte71
-rw-r--r--src/routes/send/[...text]/+page.svelte25
-rw-r--r--src/routes/send/user/[username]/[...amount]/+page.svelte11
7 files changed, 122 insertions, 60 deletions
diff --git a/src/components/AppHeader.svelte b/src/components/AppHeader.svelte
index 302a9c7a..fb57f27a 100644
--- a/src/components/AppHeader.svelte
+++ b/src/components/AppHeader.svelte
@@ -13,23 +13,14 @@
let update = (data) => ({ subject, user } = data);
const menuButtons = [
- { stringID: 'nav.dashboard', icon: 'dash', goto: 'dashboard' },
- { stringID: 'nav.settings', icon: 'settings', goto: 'settings' },
- { stringID: 'nav.support', icon: 'support', goto: 'support' },
- { stringID: 'nav.signOut', icon: 'logout', goto: 'logout' }
+ { key: 'nav.profile', icon: 'profile', href: `/${user.username}` },
+ { key: 'nav.settings', icon: 'settings', href: `/${user.username}/settings` },
+ { key: 'nav.support', icon: 'support', href: `/${user.username}/support` },
+ { key: 'nav.signOut', icon: 'logout', href: `/${user.username}/logout` }
];
let showMenu = false;
- const handleMenuItemClick = (item) => {
- if (item === 'logout') {
- goto('/logout');
- } else {
- showMenu = false;
- goto(`/${user.username + '/' + item}`);
- }
- };
-
$: bg =
$tempProfileFiles && $tempProfileFiles.banner
? `url(${$tempProfileFiles.banner})`
@@ -44,6 +35,15 @@
>
<nav class="flex justify-end items-center space-x-4 p-5">
{#if user}
+ <a href={`/${user.username}/dashboard`}>
+ <button
+ class="bg-white p-2 rounded-full w-12 h-12 drop-shadow-xl border border-black/10 {$page
+ .url.pathname === `/${user.username}/dashboard`
+ ? 'opacity-100'
+ : 'opacity-70 hover:opacity-80'}"
+ ><Icon icon="home" style="mx-auto w-6" />
+ </button>
+ </a>
<a href={`/${user.username}/receive`}>
<button
class="bg-white p-2 rounded-full w-12 h-12 drop-shadow-xl border border-black/10 {$page
@@ -72,20 +72,11 @@
{/if}
</button>
</a>
- <a href={`/${user.username}`}>
- <button
- class="bg-white p-2 rounded-full w-12 h-12 drop-shadow-xl border border-black/10 {$page
- .url.pathname === `/${user.username}`
- ? 'opacity-100'
- : 'opacity-70 hover:opacity-80'}"
- ><Icon icon="profile" style="mx-auto" />
- </button>
- </a>
<div class="relative">
<OutClick on:outclick={() => (showMenu = false)}>
<button
class="bg-white p-2 rounded-full w-12 h-12 drop-shadow-xl border border-black/10 {$page
- .url.pathname === `/${user.username}/dashboard` ||
+ .url.pathname === `/${user.username}` ||
$page.url.pathname === `/${user.username}/settings` ||
$page.url.pathname === `/${user.username}/support`
? 'opacity-100'
@@ -100,13 +91,14 @@
: 'hidden'} absolute top-14 right-0 bg-white rounded-3xl p-8 shadow-xl z"
>
<ul class="space-y-5 w-32">
- {#each menuButtons as button}
+ {#each menuButtons as { href, icon, key }}
<li>
- <button
- class="flex justify-center items-center font-semibold text-sm hover:opacity-80"
- on:click={() => handleMenuItemClick(button.goto)}
- ><Icon icon={button.icon} style="mr-1" /> {$t(button.stringID)}
- </button>
+ <a {href}>
+ <button
+ class="flex justify-center items-center font-semibold text-sm hover:opacity-80"
+ ><Icon {icon} style="mr-1" /> {$t(key)}
+ </button>
+ </a>
</li>
{/each}
</ul>
diff --git a/src/components/Avatar.svelte b/src/components/Avatar.svelte
index c7339bf5..092af740 100644
--- a/src/components/Avatar.svelte
+++ b/src/components/Avatar.svelte
@@ -3,11 +3,13 @@
import { Icon } from '$comp';
export let user;
+ export let size = 32;
+ $: s = size.toString();
</script>
<a href={`/${user.username}`}>
<div
- class="rounded-full border-4 border-white overflow-hidden w-32 h-32 bg-gradient-to-r {$colorTheme} flex justify-center items-center"
+ class="w-{s} h-{s} rounded-full border-4 border-white overflow-hidden bg-gradient-to-r {$colorTheme} flex justify-center items-center"
>
{#if user?.profile}
<img
diff --git a/src/locales/en.json b/src/locales/en.json
index b5aef47a..8e71e3a8 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -144,6 +144,7 @@
"failed": "Login failed"
},
"nav": {
+ "profile": "Profile",
"startInSeconds": "Start in Seconds",
"account": "Account",
"signIn": "Sign In",
@@ -221,7 +222,7 @@
"send": "Send"
},
"transactions": {
- "header": "Transaction History",
+ "header": "Transactions",
"AMOUNT": "AMOUNT",
"TYPE": "TYPE",
"TIME": "TIME",
diff --git a/src/routes/[username]/transactions/[...page]/+page.server.js b/src/routes/[username]/transactions/[...page]/+page.server.js
index faa8a1ab..caa2e155 100644
--- a/src/routes/[username]/transactions/[...page]/+page.server.js
+++ b/src/routes/[username]/transactions/[...page]/+page.server.js
@@ -3,12 +3,24 @@ import { get, auth } from '$lib/utils';
let offset = 25;
export async function load({ cookies, params, request }) {
- let page = parseInt(params.page || 1);
- let transactions = await get('/payments', auth(cookies));
+ let parts = params.page.split('/');
+
+ let start, end, page;
+ if (parts.length === 1) [page] = parts;
+ if (parts.length === 2) [start, page] = parts;
+ if (parts.length === 3) [start, end, page] = parts;
+ if (!parseInt(page)) page = 1;
+
+ let url = '/payments';
+ if (start && end) url += `?start=${start}&end=${end}`;
+ else if (start) url += `?start=${start}`;
+ else if (end) url += `?end=${end}`;
+
+ let transactions = await get(url, auth(cookies));
let total = transactions.length;
let pages = new Array(Math.ceil(total / offset));
let i = (page - 1) * offset;
transactions = transactions.slice(i, i + offset);
- return { total, transactions, page, pages };
+ return { total, transactions, page, pages, start, end };
}
diff --git a/src/routes/[username]/transactions/[...page]/+page.svelte b/src/routes/[username]/transactions/[...page]/+page.svelte
index b957caf8..cf809445 100644
--- a/src/routes/[username]/transactions/[...page]/+page.svelte
+++ b/src/routes/[username]/transactions/[...page]/+page.svelte
@@ -1,23 +1,61 @@
<script>
+ import { Icon } from '$comp';
import { onMount } from 'svelte';
import { formatDistance, parseISO } from 'date-fns';
import { newPayment } from '$lib/store';
import { t } from '$lib/translations';
import { f, s, sats } from '$lib/utils';
import { page } from '$app/stores';
+ import { differenceInDays, getUnixTime, sub } from 'date-fns';
export let data;
- let { user } = data;
+ let { start, end, user } = data;
+
+ let presets = [
+ { title: 'Today', start: sub(new Date(), { days: 1 }), end: null },
+ { title: '7 days', start: sub(new Date(), { days: 7 }), end: null },
+ { title: 'Last month', start: sub(new Date(), { months: 1 }), end: null },
+ { title: 'All', start: null, end: null }
+ ];
+
+ $: selection = start
+ ? presets.findIndex((p) => Math.abs(differenceInDays(new Date(start * 1000), p.start)) < 1)
+ : presets.length - 1;
let p,
total,
transactions = [],
pages = [];
- $: data && ({ page: p, pages, total, transactions } = data);
+ $: data && ({ page: p, pages, start, end, total, transactions } = data);
$: $page && ($newPayment = false);
$: $newPayment && invalidate(`/users/${user.username}`);
+
+ let csv = () => {
+ const keys = ['hash', 'updatedAt', 'rate', 'currency', 'amount', 'fee', 'tip'];
+ const csv =
+ keys.map((k) => `"${k}"`).join(',') +
+ '\n' +
+ transactions.map((r) => keys.map((k) => `"${r[k]}"`).join(',')).join('\n');
+
+ const filename = 'transactions.csv';
+ let blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
+ if (navigator.msSaveBlob) {
+ navigator.msSaveBlob(blob, filename);
+ } else {
+ let link = document.createElement('a');
+ if (link.download !== undefined) {
+ let url = URL.createObjectURL(blob);
+ link.setAttribute('href', url);
+ link.setAttribute('download', filename);
+ link.style.visibility = 'hidden';
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ }
+ }
+ };
</script>
<div class="mt-24 mb-20">
@@ -26,13 +64,32 @@
</h1>
<div class="container w-full mx-auto md:text-lg px-10 lg:px-72">
- <div class="flex flex-wrap justify-end mb-8">
+ <div class="flex text-md text-secondary flex-wrap mb-4">
+ <div class="flex flex-wrap gap-2">
+ {#each presets as { start, end, title }, i}
+ <a
+ class:active={selection === i}
+ href={`/${user.username}/transactions/${start ? getUnixTime(start) + '/' : ''}1`}
+ >
+ <button class="rounded-full border py-2 px-5 hover:opacity-80 mb-2">
+ <div class="my-auto">{title}</div>
+ </button>
+ </a>
+ {/each}
+ </div>
+ <button class="ml-auto rounded-xl border py-2 px-5 hover:opacity-80 mb-2 flex" on:click={csv}>
+ <Icon icon="save" style="opacity-50 mr-2" />
+ <div class="my-auto">Export</div>
+ </button>
+ </div>
+
+ <div class="flex flex-wrap justify-center mb-8">
{#if pages.length > 1}
{#each pages as _, i}
<a
class="mr-1 last:mr-0"
href={`/${user.username}/transactions/${i + 1}`}
- class:active={p === i + 1}
+ class:active={parseInt(p) === i + 1}
>
<div class="border py-2 rounded-full border-2 w-12 h-12 hover:opacity-80 text-center">
{i + 1}
@@ -44,7 +101,7 @@
<div class="text-secondary grid grid-cols-3 mb-5">
<h2>{$t('user.transactions.AMOUNT')}</h2>
- <h2 class="text-center">{$t('user.transactions.TYPE')}</h2>
+ <h2 class="text-center" />
<h2 class="text-right">{$t('user.transactions.TIME')}</h2>
</div>
@@ -85,7 +142,7 @@
</div>
<style>
- .active div {
- @apply bg-gray-100;
+ .active * {
+ @apply bg-black text-white border-black;
}
</style>
diff --git a/src/routes/send/[...text]/+page.svelte b/src/routes/send/[...text]/+page.svelte
index fe949617..1311422e 100644
--- a/src/routes/send/[...text]/+page.svelte
+++ b/src/routes/send/[...text]/+page.svelte
@@ -10,8 +10,8 @@
export let form;
let el;
- let placeholder = 'Address, Invoice or Username';
- let text = placeholder;
+ let placeholder = 'Paste a bitcoin address, lightning invoice or coinos username';
+ let text;
let focus = () => text === placeholder && (text = '');
let keypress = (e) => e.key === 'Enter' && (e.preventDefault() || el.click());
@@ -25,7 +25,11 @@
<Icon icon="arrow-left" style="w-10" />
</button>
-<form method="POST" class="container px-4 mt-20 max-w-xl mx-auto" use:enhance>
+<h1 class="px-3 md:px-0 text-center text-3xl md:text-4xl font-semibold mb-8">
+ Send bitcoin or lightning
+</h1>
+
+<form method="POST" class="container px-4 max-w-xl mx-auto" use:enhance>
{#if form?.error}
<div class="text-red-600 text-center" in:fly>
{form.error}
@@ -35,17 +39,12 @@
<label for="invoice" class="font-bold mb-1 block">To</label>
<input type="hidden" name="text" bind:value={text} />
- <div
- class="expandable-textarea"
- class:text-gray-400={text === placeholder}
- role="textbox"
- contenteditable
- bind:textContent={text}
- on:focus={focus}
+ <textarea
+ {placeholder}
on:keypress={keypress}
- >
- {text}
- </div>
+ class="w-full p-4 border rounded-xl h-48"
+ bind:value={text}
+ />
</div>
<div class="flex justify-end">
diff --git a/src/routes/send/user/[username]/[...amount]/+page.svelte b/src/routes/send/user/[username]/[...amount]/+page.svelte
index 6f4cb313..89cec12f 100644
--- a/src/routes/send/user/[username]/[...amount]/+page.svelte
+++ b/src/routes/send/user/[username]/[...amount]/+page.svelte
@@ -50,11 +50,10 @@
</div>
{/if}
-<div class="container px-4 mt-20 max-w-xl mx-auto">
+<div class="container px-4 mt-20 max-w-xl mx-auto space-y-8">
{#if amount}
- <div class="text-center mb-8">
- <h1 class="text-xl md:text-2xl text-secondary mb-2">Send</h1>
- <p class="text-6xl break-words mb-4">
+ <div class="text-center mb-8 space-y-5">
+ <p class="text-5xl break-words">
{fiat ? f(amountFiat, currency) : s(amount)}
{#if !fiat}
<span class="text-xl md:text-2xl text-secondary">sats</span>
@@ -64,8 +63,8 @@
<h1 class="text-xl md:text-2xl text-secondary mb-2">to</h1>
<div class="flex p-1 gap-2 justify-center">
- <Avatar user={recipient} />
- <p class="text-6xl break-words my-auto">{recipient.username}</p>
+ <Avatar user={recipient} size={'20'} />
+ <p class="text-4xl break-words my-auto">{recipient.username}</p>
</div>
</div>
{:else}