aboutsummaryrefslogtreecommitdiffstats
path: root/src/routes/(app)/buy
diff options
context:
space:
mode:
authorJohn Barratt <[email protected]>2023-03-07 10:29:49 -0800
committerJohn Barratt <[email protected]>2023-03-07 10:29:49 -0800
commit8ef9568cd1bd822495ce005d4e4cfaa6cb0d92ce (patch)
tree4ea5530cc2b7e5d970dc8a84cacfecfe2cce46b7 /src/routes/(app)/buy
parentc97fb43abba375920fff361ec5fa4eb64a5a0d3f (diff)
parent0f44e3283c25c9783a52cf20957661da49d2f90b (diff)
downloadcoinos-ui-notifications.tar.gz
coinos-ui-notifications.zip
Merge branch 'master' of https://github.com/coinos/coinos-ui into notificationsnotifications
Diffstat (limited to 'src/routes/(app)/buy')
-rw-r--r--src/routes/(app)/buy/+page.server.js27
-rw-r--r--src/routes/(app)/buy/+page.svelte150
2 files changed, 177 insertions, 0 deletions
diff --git a/src/routes/(app)/buy/+page.server.js b/src/routes/(app)/buy/+page.server.js
new file mode 100644
index 00000000..fc7ae473
--- /dev/null
+++ b/src/routes/(app)/buy/+page.server.js
@@ -0,0 +1,27 @@
+import { env } from '$env/dynamic/private';
+import { error, fail, redirect } from '@sveltejs/kit';
+import { post, auth } from '$lib/utils';
+import { createTicket } from '$lib/ticket';
+
+export const actions = {
+ default: async ({ cookies, request }) => {
+ let form = await request.formData();
+
+ let amount = parseFloat(form.get('amount'));
+ if (!amount || amount < 0 || amount > 500) return fail(400, { message: 'Invalid amount' });
+
+ await post(
+ '/buy',
+ {
+ amount: form.get('amount'),
+ number: form.get('number').replace(/\s+/g, ''),
+ month: form.get('expiry').slice(0, 2),
+ year: form.get('expiry').slice(-2),
+ cvc: form.get('cvc')
+ },
+ auth(cookies)
+ );
+
+ throw redirect(307, `/${form.get('username')}/payments`);
+ }
+};
diff --git a/src/routes/(app)/buy/+page.svelte b/src/routes/(app)/buy/+page.svelte
new file mode 100644
index 00000000..2f556ada
--- /dev/null
+++ b/src/routes/(app)/buy/+page.svelte
@@ -0,0 +1,150 @@
+<script>
+ import { enhance } from '$app/forms';
+ import { Icon, Lock, Card, Calendar, Spinner } from '$comp';
+ import { animatedRate, selectedRate } from '$lib/store';
+ import { f } from '$lib/utils';
+
+ export let data, form;
+
+ let { username, currency } = data.user;
+ let loading;
+
+ let amount, n, d, cvc;
+
+ let ease = (t) => (t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t);
+ let i;
+
+ let o = $selectedRate;
+ $: animate($selectedRate);
+ let animate = (n) => {
+ clearInterval(i);
+
+ let t = 0;
+ let d = o - n;
+
+ i = setInterval(() => {
+ $animatedRate = (o - d * ease(t / 100)).toFixed(2);
+ if (t > 80) (o = n) && clearInterval(i);
+ t++;
+ }, 10);
+ };
+</script>
+
+<div class="container px-4 max-w-xl mx-auto mt-10 space-y-5">
+ <div class="w-full flex">
+ <a href="/" class="mx-auto">
+ <Icon icon="logo" />
+ </a>
+ </div>
+
+ <div class="flex justify-center items-center">
+ <div class="shadow-xl rounded-3xl px-10 pt-5 pb-10 space-y-5 w-full mx-5">
+ <div class="relative">
+ <h1 class="text-2xl md:text-3xl font-semibold text-center">Buy Bitcoin</h1>
+ </div>
+ <p class="text-secondary text-center">Purchase up to $500 worth of Bitcoin</p>
+
+ {#if form?.message}
+ <div class="text-red-600 text-center">
+ {form.message}
+ </div>
+ {/if}
+
+ <form
+ method="POST"
+ class="flex flex-wrap gap-5 w-full p-5 mt-20"
+ use:enhance={() => {
+ loading = true;
+ return ({ update }) => update() && (loading = false);
+ }}
+ >
+ <input type="hidden" name="username" value={username} />
+
+ <label class="relative w-full flex flex-col">
+ <span class="font-bold mb-3">Amount</span>
+ <input
+ class="rounded-md peer pl-12 pr-2 py-2 border-2 border-gray-200 placeholder-gray-300"
+ type="text"
+ name="amount"
+ placeholder="&bull;&bull;&bull;"
+ bind:value={amount}
+ />
+ <span
+ class="absolute bottom-[9px] left-0 translate-x-1/2 w-6 text-black peer-placeholder-shown:text-gray-300
+ ">$</span
+ >
+ </label>
+
+ <label class="relative w-full flex flex-col">
+ <span class="font-bold mb-3">Card number</span>
+ <input
+ class="rounded-md peer pl-12 pr-2 py-2 border-2 border-gray-200 placeholder-gray-300"
+ type="text"
+ name="number"
+ placeholder="0000 0000 0000 0000"
+ bind:value={n}
+ />
+ <Card />
+ </label>
+
+ <label class="relative flex-1 flex flex-col">
+ <span class="font-bold mb-3">Expire date</span>
+ <input
+ class="rounded-md peer pl-12 pr-2 py-2 border-2 border-gray-200 placeholder-gray-300"
+ type="text"
+ name="expiry"
+ placeholder="MM/YY"
+ bind:value={d}
+ />
+ <Calendar />
+ </label>
+
+ <label class="relative flex-1 flex flex-col">
+ <span class="font-bold flex items-center gap-3 mb-3">
+ CVC/CVV
+ <span class="relative group">
+ <span
+ class="hidden group-hover:flex justify-center items-center px-2 py-1 text-xs absolute -right-2 transform translate-x-full -translate-y-1/2 w-max top-1/2 bg-black text-white"
+ >
+ 3-digit number on the back of your card</span
+ >
+ </span>
+ </span>
+ <input
+ class="rounded-md peer pl-12 pr-2 py-2 border-2 border-gray-200 placeholder-gray-300"
+ type="text"
+ name="cvc"
+ placeholder="&bull;&bull;&bull;"
+ bind:value={cvc}
+ />
+ <Lock />
+ </label>
+
+ <div class="w-full flex mt-8">
+ <button
+ class="bg-black text-white border rounded-full px-8 py-4 font-bold hover:opacity-80 mx-auto text-xl"
+ type="submit"
+ disabled={loading}
+ >
+ {#if loading}
+ <Spinner />
+ {:else}
+ Submit
+ {/if}
+ </button>
+ </div>
+ </form>
+
+ <div class="flex">
+ <div class="text-secondary flex mr-auto">
+ <div class="flex mr-1">
+ <div class="my-auto mr-1">1</div>
+ <img src="/images/bitcoin.svg" class="w-5 my-auto" alt="Bitcoin" />
+ </div>
+ <div>= {f($animatedRate, currency)}</div>
+ </div>
+ <p class="text-secondary">3% credit card fee applies</p>
+ </div>
+ </div>
+ </div>
+</div>