mirror of
https://github.com/aljazceru/hypergolic.git
synced 2025-12-19 14:34:20 +01:00
problem: rocket page sux
This commit is contained in:
147
package-lock.json
generated
147
package-lock.json
generated
@@ -38,7 +38,7 @@
|
||||
"@types/node": "^20.14.2",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"flowbite": "^2.3.0",
|
||||
"flowbite-svelte": "^0.46.1",
|
||||
"flowbite-svelte": "^0.46.14",
|
||||
"flowbite-svelte-icons": "^1.6.1",
|
||||
"postcss": "^8.4.38",
|
||||
"prettier": "^3.1.1",
|
||||
@@ -462,18 +462,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/dom": {
|
||||
"version": "1.6.5",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.5.tgz",
|
||||
"integrity": "sha512-Nsdud2X65Dz+1RHjAIP0t8z5e2ff/IRbei6BqFrl1urT8sDVzM1HMQ+R0XcU5ceRfyO3I6ayeqIfh+6Wb8LGTw==",
|
||||
"version": "1.6.7",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.7.tgz",
|
||||
"integrity": "sha512-wmVfPG5o2xnKDU4jx/m4w5qva9FWHcnZ8BvzEe90D/RpwsJaTAVYPEPdQ8sbr/N8zZTAHlZUTQdqg8ZUbzHmng==",
|
||||
"dependencies": {
|
||||
"@floating-ui/core": "^1.0.0",
|
||||
"@floating-ui/utils": "^0.2.0"
|
||||
"@floating-ui/core": "^1.6.0",
|
||||
"@floating-ui/utils": "^0.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/utils": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.2.tgz",
|
||||
"integrity": "sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw=="
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.4.tgz",
|
||||
"integrity": "sha512-dWO2pw8hhi+WrXq1YJy2yCuWoL20PddgGaqTgVe4cOS9Q6qklXCiA1tJEqX6BEwRNSCP84/afac9hd4MS+zEUA=="
|
||||
},
|
||||
"node_modules/@gcornut/valibot-json-schema": {
|
||||
"version": "0.0.27",
|
||||
@@ -824,6 +824,59 @@
|
||||
"node": ">=18.16.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/plugin-node-resolve": {
|
||||
"version": "15.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz",
|
||||
"integrity": "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@rollup/pluginutils": "^5.0.1",
|
||||
"@types/resolve": "1.20.2",
|
||||
"deepmerge": "^4.2.2",
|
||||
"is-builtin-module": "^3.2.1",
|
||||
"is-module": "^1.0.0",
|
||||
"resolve": "^1.22.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"rollup": "^2.78.0||^3.0.0||^4.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"rollup": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/pluginutils": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz",
|
||||
"integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "^1.0.0",
|
||||
"estree-walker": "^2.0.2",
|
||||
"picomatch": "^2.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"rollup": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/pluginutils/node_modules/estree-walker": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz",
|
||||
@@ -1290,6 +1343,12 @@
|
||||
"integrity": "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/resolve": {
|
||||
"version": "1.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
|
||||
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/validator": {
|
||||
"version": "13.11.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.10.tgz",
|
||||
@@ -1381,9 +1440,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/apexcharts": {
|
||||
"version": "3.49.1",
|
||||
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.49.1.tgz",
|
||||
"integrity": "sha512-MqGtlq/KQuO8j0BBsUJYlRG8VBctKwYdwuBtajHgHTmSgUU3Oai+8oYN/rKCXwXzrUlYA+GiMgotAIbXY2BCGw==",
|
||||
"version": "3.50.0",
|
||||
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.50.0.tgz",
|
||||
"integrity": "sha512-LJT1PNAm+NoIU3aogL2P+ViC0y/Cjik54FdzzGV54UNnGQLBoLe5ok3fxsJDTgyez45BGYT8gqNpYKqhdfy5sg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@yr/monotone-cubic-spline": "^1.0.3",
|
||||
@@ -1593,6 +1652,18 @@
|
||||
"node": ">=6.14.2"
|
||||
}
|
||||
},
|
||||
"node_modules/builtin-modules": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz",
|
||||
"integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/callsites": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||
@@ -2175,24 +2246,35 @@
|
||||
}
|
||||
},
|
||||
"node_modules/flowbite": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/flowbite/-/flowbite-2.3.0.tgz",
|
||||
"integrity": "sha512-pm3JRo8OIJHGfFYWgaGpPv8E+UdWy0Z3gEAGufw+G/1dusaU/P1zoBLiQpf2/+bYAi+GBQtPVG86KYlV0W+AFQ==",
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/flowbite/-/flowbite-2.4.1.tgz",
|
||||
"integrity": "sha512-I++vDsSOOlzHNuxY2OcFMNVC4CNzpPU2K14YHJ81cYrANXdzgizqniMB/1KQ219x8fqw+S0msY9Q45ZSXDqAPw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@popperjs/core": "^2.9.3",
|
||||
"flowbite-datepicker": "^1.3.0",
|
||||
"mini-svg-data-uri": "^1.4.3"
|
||||
}
|
||||
},
|
||||
"node_modules/flowbite-svelte": {
|
||||
"version": "0.46.1",
|
||||
"resolved": "https://registry.npmjs.org/flowbite-svelte/-/flowbite-svelte-0.46.1.tgz",
|
||||
"integrity": "sha512-GMQP4Fxn4mYu12XTNRbOHMYMklbP29sKFZUZYekryxhWak2zbSlI1ozhBtnrD2WDbFBPqZ64IcyButOkYpz+WQ==",
|
||||
"node_modules/flowbite-datepicker": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/flowbite-datepicker/-/flowbite-datepicker-1.3.0.tgz",
|
||||
"integrity": "sha512-CLVqzuoE2vkUvWYK/lJ6GzT0be5dlTbH3uuhVwyB67+PjqJWABm2wv68xhBf5BqjpBxvTSQ3mrmLHpPJ2tvrSQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.6.3",
|
||||
"apexcharts": "^3.48.0",
|
||||
"flowbite": "^2.3.0",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"flowbite": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/flowbite-svelte": {
|
||||
"version": "0.46.14",
|
||||
"resolved": "https://registry.npmjs.org/flowbite-svelte/-/flowbite-svelte-0.46.14.tgz",
|
||||
"integrity": "sha512-eIoQ7WQfyhHkCwmg2T9V46U27J+zpISmYcwPtPn689bNV9q8TB1e6Nvn1ElhjEjcIKZsHQBQWzJ6qk/8mkvxgg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.6.7",
|
||||
"apexcharts": "^3.49.2",
|
||||
"flowbite": "^2.4.1",
|
||||
"tailwind-merge": "^2.3.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -2429,6 +2511,21 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-builtin-module": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz",
|
||||
"integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"builtin-modules": "^3.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-core-module": {
|
||||
"version": "2.13.1",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
|
||||
@@ -2467,6 +2564,12 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-module": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
|
||||
"integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/is-number": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"@types/node": "^20.14.2",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"flowbite": "^2.3.0",
|
||||
"flowbite-svelte": "^0.46.1",
|
||||
"flowbite-svelte": "^0.46.14",
|
||||
"flowbite-svelte-icons": "^1.6.1",
|
||||
"postcss": "^8.4.38",
|
||||
"prettier": "^3.1.1",
|
||||
|
||||
104
src/components/Pie.svelte
Normal file
104
src/components/Pie.svelte
Normal file
@@ -0,0 +1,104 @@
|
||||
<script lang="ts">
|
||||
import { Card, Chart } from 'flowbite-svelte';
|
||||
|
||||
const options = {
|
||||
series: [35.1, 23.5, 2.4, 5.4],
|
||||
colors: ['#1C64F2', '#16BDCA', '#FDBA8C', '#E74694'],
|
||||
chart: {
|
||||
height: 320,
|
||||
width: '100%',
|
||||
type: 'donut'
|
||||
},
|
||||
stroke: {
|
||||
colors: ['transparent'],
|
||||
lineCap: ''
|
||||
},
|
||||
plotOptions: {
|
||||
pie: {
|
||||
donut: {
|
||||
labels: {
|
||||
show: true,
|
||||
name: {
|
||||
show: true,
|
||||
fontFamily: 'Inter, sans-serif',
|
||||
offsetY: 20
|
||||
},
|
||||
total: {
|
||||
showAlways: false,
|
||||
show: false,
|
||||
label: 'Merit Distribution',
|
||||
fontFamily: 'Inter, sans-serif',
|
||||
formatter: function (w) {
|
||||
const sum = w.globals.seriesTotals.reduce((a, b) => {
|
||||
return a + b;
|
||||
}, 0);
|
||||
return `${sum}k`;
|
||||
}
|
||||
},
|
||||
value: {
|
||||
show: true,
|
||||
fontFamily: 'Inter, sans-serif',
|
||||
offsetY: -20,
|
||||
formatter: function (value) {
|
||||
return value + 'k';
|
||||
}
|
||||
}
|
||||
},
|
||||
size: '40%'
|
||||
}
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
padding: {
|
||||
top: -2
|
||||
}
|
||||
},
|
||||
labels: ['Direct', 'Sponsor', 'Affiliate', 'Email marketing'],
|
||||
dataLabels: {
|
||||
enabled: false
|
||||
},
|
||||
legend: {
|
||||
show: false,
|
||||
position: 'bottom',
|
||||
fontFamily: 'Inter, sans-serif'
|
||||
},
|
||||
yaxis: {
|
||||
|
||||
labels: {
|
||||
formatter: function (value) {
|
||||
return value + 'k';
|
||||
}
|
||||
}
|
||||
},
|
||||
xaxis: {
|
||||
labels: {
|
||||
formatter: function (value) {
|
||||
return value + 'k';
|
||||
}
|
||||
},
|
||||
axisTicks: {
|
||||
show: false
|
||||
},
|
||||
axisBorder: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<Card>
|
||||
<div class="flex justify-between items-start w-full">
|
||||
<div class="flex-col items-center">
|
||||
<div class="flex items-center mb-1">
|
||||
<h5 class="text-xl font-bold leading-none text-gray-900 dark:text-white me-1">Merit Distribution</h5>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<Chart {options} class="py-6" />
|
||||
|
||||
|
||||
22
src/components/ProductCardFromEvent.svelte
Normal file
22
src/components/ProductCardFromEvent.svelte
Normal file
@@ -0,0 +1,22 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { ndk } from "@/ndk";
|
||||
import type { NDKEvent } from "@nostr-dev-kit/ndk";
|
||||
import ProductCard from './ProductCard.svelte';
|
||||
export let productID: string;
|
||||
export let rocket:NDKEvent;
|
||||
let productEvent:NDKEvent | undefined;
|
||||
|
||||
onMount(()=>{
|
||||
$ndk.fetchEvent(productID).then((e)=>{
|
||||
if (e) {
|
||||
productEvent = e
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
{#if productEvent}
|
||||
<ProductCard {rocket} product={productEvent} />
|
||||
{/if}
|
||||
63
src/components/ProductPurchases.svelte
Normal file
63
src/components/ProductPurchases.svelte
Normal file
@@ -0,0 +1,63 @@
|
||||
<script lang="ts">
|
||||
import type { RocketProduct } from '@/event_helpers/rockets';
|
||||
import { ndk } from '@/ndk';
|
||||
import type { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||
import { onDestroy } from 'svelte';
|
||||
import * as Card from '@/components/ui/card';
|
||||
import * as Table from '@/components/ui/table';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
|
||||
export let product: RocketProduct;
|
||||
export let rocket: NDKEvent;
|
||||
|
||||
let zaps = $ndk.storeSubscribe(
|
||||
[{ '#a': [`31108:${rocket.author.pubkey}:${rocket.dTag}`], kinds: [9735] }],
|
||||
{
|
||||
subId: product.ID
|
||||
}
|
||||
);
|
||||
|
||||
onDestroy(() => {
|
||||
zaps?.unsubscribe();
|
||||
});
|
||||
|
||||
//todo: validate zaps against product, publish store of all successful payments including those already in rocket. Publish another store with successful payments that are not yet included in rocket state so we can update the state and republish.
|
||||
</script>
|
||||
|
||||
{#each $zaps as z}<a
|
||||
href="#"
|
||||
on:click={() => {
|
||||
console.log(z.rawEvent());
|
||||
}}>{z.id}</a
|
||||
><br />{/each}
|
||||
|
||||
<Card.Root
|
||||
data-x-chunk-name="dashboard-05-chunk-3"
|
||||
data-x-chunk-description="A table of recent orders showing the following columns: Customer, Type, Status, Date, and Amount."
|
||||
>
|
||||
<Card.Header class="px-7">
|
||||
<Card.Title>Purchases</Card.Title>
|
||||
<Card.Description>Purchase history for {product.ID}</Card.Description>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.Head>Buyer</Table.Head>
|
||||
<Table.Head class="hidden md:table-cell">Date</Table.Head>
|
||||
<Table.Head class="text-right">Amount</Table.Head>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
<Table.Row class="bg-accent">
|
||||
<Table.Cell>
|
||||
<div class="font-medium">Liam Johnson</div>
|
||||
<div class="hidden text-sm text-muted-foreground md:inline">liam@example.com</div>
|
||||
</Table.Cell>
|
||||
<Table.Cell class="hidden md:table-cell">2023-06-23</Table.Cell>
|
||||
<Table.Cell class="text-right">$250.00</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
23
src/components/ProductsForRocket.svelte
Normal file
23
src/components/ProductsForRocket.svelte
Normal file
@@ -0,0 +1,23 @@
|
||||
<script lang="ts">
|
||||
import type { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||
|
||||
import Todo from './Todo.svelte';
|
||||
import { writable, type Readable } from 'svelte/store';
|
||||
import { getMapOfProductsFromRocket, type RocketProduct } from '@/event_helpers/rockets';
|
||||
import ProductPurchases from './ProductPurchases.svelte';
|
||||
import ProductCard from './ProductCard.svelte';
|
||||
import ProductCardFromEvent from './ProductCardFromEvent.svelte';
|
||||
|
||||
|
||||
export let rocketEvent: NDKEvent;
|
||||
|
||||
$: rocketProducts = getMapOfProductsFromRocket(rocketEvent);
|
||||
</script>
|
||||
|
||||
{#if rocketEvent && rocketProducts.size > 0}
|
||||
{#each rocketProducts as [id, product]}
|
||||
<ProductCardFromEvent rocket={rocketEvent} productID={product.ID} />
|
||||
<ProductPurchases rocket={rocketEvent} {product} />{/each}
|
||||
{/if}
|
||||
|
||||
|
||||
490
src/components/RocketDashboard copy.svelte
Normal file
490
src/components/RocketDashboard copy.svelte
Normal file
@@ -0,0 +1,490 @@
|
||||
<script lang="ts">
|
||||
import ChevronLeft from "lucide-svelte/icons/chevron-left";
|
||||
import ChevronRight from "lucide-svelte/icons/chevron-right";
|
||||
import Copy from "lucide-svelte/icons/copy";
|
||||
import CreditCard from "lucide-svelte/icons/credit-card";
|
||||
import EllipsisVertical from "lucide-svelte/icons/ellipsis-vertical";
|
||||
import File from "lucide-svelte/icons/file";
|
||||
import ListFilter from "lucide-svelte/icons/list-filter";
|
||||
import Truck from "lucide-svelte/icons/truck";
|
||||
|
||||
import { Badge } from "$lib/components/ui/badge/index.js";
|
||||
import * as Breadcrumb from "$lib/components/ui/breadcrumb/index.js";
|
||||
import { Button } from "$lib/components/ui/button/index.js";
|
||||
import * as Card from "$lib/components/ui/card/index.js";
|
||||
import * as DropdownMenu from "$lib/components/ui/dropdown-menu/index.js";
|
||||
import * as Pagination from "$lib/components/ui/pagination/index.js";
|
||||
import { Progress } from "$lib/components/ui/progress/index.js";
|
||||
import { Separator } from "$lib/components/ui/separator/index.js";
|
||||
import * as Table from "$lib/components/ui/table/index.js";
|
||||
import * as Tabs from "$lib/components/ui/tabs/index.js";
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<div class="flex flex-col sm:gap-4">
|
||||
<header
|
||||
class="sticky top-0 z-30 flex h-14 items-center gap-4 border-b bg-background px-4 sm:static sm:h-auto sm:border-0 sm:bg-transparent sm:px-6"
|
||||
>
|
||||
|
||||
<Breadcrumb.Root class="hidden md:flex">
|
||||
<Breadcrumb.List>
|
||||
<Breadcrumb.Item>
|
||||
<Breadcrumb.Link href="##">Rocket Name</Breadcrumb.Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Separator />
|
||||
<Breadcrumb.Item>
|
||||
<Breadcrumb.Link href="##">Dashboard</Breadcrumb.Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Separator />
|
||||
<Breadcrumb.Item>
|
||||
<Breadcrumb.Page>Recent Orders</Breadcrumb.Page>
|
||||
</Breadcrumb.Item>
|
||||
</Breadcrumb.List>
|
||||
</Breadcrumb.Root>
|
||||
</header>
|
||||
<main
|
||||
class="grid flex-1 items-start gap-4 p-4 sm:px-6 sm:py-0 md:gap-8 lg:grid-cols-3 xl:grid-cols-3"
|
||||
>
|
||||
<div class="grid auto-rows-max items-start gap-4 md:gap-8 lg:col-span-2">
|
||||
<div class="grid gap-4 sm:grid-cols-2 md:grid-cols-4 lg:grid-cols-2 xl:grid-cols-4">
|
||||
<Card.Root class="sm:col-span-2">
|
||||
<Card.Header class="pb-3">
|
||||
<Card.Title>Your Orders</Card.Title>
|
||||
<Card.Description class="max-w-lg text-balance leading-relaxed">
|
||||
Introducing Our Dynamic Orders Dashboard for Seamless Management and
|
||||
Insightful Analysis.
|
||||
</Card.Description>
|
||||
</Card.Header>
|
||||
<Card.Footer>
|
||||
<Button>Create New Order</Button>
|
||||
</Card.Footer>
|
||||
</Card.Root>
|
||||
<Card.Root>
|
||||
<Card.Header class="pb-2">
|
||||
<Card.Description>This Week</Card.Description>
|
||||
<Card.Title class="text-4xl">$1329</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
<div class="text-xs text-muted-foreground">+25% from last week</div>
|
||||
</Card.Content>
|
||||
<Card.Footer>
|
||||
<Progress value={25} aria-label="25% increase" />
|
||||
</Card.Footer>
|
||||
</Card.Root>
|
||||
<Card.Root>
|
||||
<Card.Header class="pb-2">
|
||||
<Card.Description>This Month</Card.Description>
|
||||
<Card.Title class="text-3xl">$5,329</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
<div class="text-xs text-muted-foreground">+10% from last month</div>
|
||||
</Card.Content>
|
||||
<Card.Footer>
|
||||
<Progress value={12} aria-label="12% increase" />
|
||||
</Card.Footer>
|
||||
</Card.Root>
|
||||
</div>
|
||||
<Tabs.Root value="week">
|
||||
<div class="flex items-center">
|
||||
<Tabs.List>
|
||||
<Tabs.Trigger value="week">Week</Tabs.Trigger>
|
||||
<Tabs.Trigger value="month">Month</Tabs.Trigger>
|
||||
<Tabs.Trigger value="year">Year</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
<div class="ml-auto flex items-center gap-2">
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger asChild let:builder>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
class="h-7 gap-1 text-sm"
|
||||
builders={[builder]}
|
||||
>
|
||||
<ListFilter class="h-3.5 w-3.5" />
|
||||
<span class="sr-only sm:not-sr-only">Filter</span>
|
||||
</Button>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content align="end">
|
||||
<DropdownMenu.Label>Filter by</DropdownMenu.Label>
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.CheckboxItem checked>
|
||||
Fulfilled
|
||||
</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.CheckboxItem>Declined</DropdownMenu.CheckboxItem>
|
||||
<DropdownMenu.CheckboxItem>Refunded</DropdownMenu.CheckboxItem>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
<Button size="sm" variant="outline" class="h-7 gap-1 text-sm">
|
||||
<File class="h-3.5 w-3.5" />
|
||||
<span class="sr-only sm:not-sr-only">Export</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<Tabs.Content value="week">
|
||||
<Card.Root>
|
||||
<Card.Header class="px-7">
|
||||
<Card.Title>Orders</Card.Title>
|
||||
<Card.Description>Recent orders from your store.</Card.Description>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.Head>Customer</Table.Head>
|
||||
<Table.Head class="hidden sm:table-cell">
|
||||
Type
|
||||
</Table.Head>
|
||||
<Table.Head class="hidden sm:table-cell">
|
||||
Status
|
||||
</Table.Head>
|
||||
<Table.Head class="hidden md:table-cell">
|
||||
Date
|
||||
</Table.Head>
|
||||
<Table.Head class="text-right">Amount</Table.Head>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
<Table.Row class="bg-accent">
|
||||
<Table.Cell>
|
||||
<div class="font-medium">Liam Johnson</div>
|
||||
<div
|
||||
class="hidden text-sm text-muted-foreground md:inline"
|
||||
>
|
||||
liam@example.com
|
||||
</div>
|
||||
</Table.Cell>
|
||||
<Table.Cell class="hidden sm:table-cell">
|
||||
Sale
|
||||
</Table.Cell>
|
||||
<Table.Cell class="hidden sm:table-cell">
|
||||
<Badge class="text-xs" variant="secondary">
|
||||
Fulfilled
|
||||
</Badge>
|
||||
</Table.Cell>
|
||||
<Table.Cell class="hidden md:table-cell">
|
||||
2023-06-23
|
||||
</Table.Cell>
|
||||
<Table.Cell class="text-right">$250.00</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<div class="font-medium">Olivia Smith</div>
|
||||
<div
|
||||
class="hidden text-sm text-muted-foreground md:inline"
|
||||
>
|
||||
olivia@example.com
|
||||
</div>
|
||||
</Table.Cell>
|
||||
<Table.Cell class="hidden sm:table-cell">
|
||||
Refund
|
||||
</Table.Cell>
|
||||
<Table.Cell class="hidden sm:table-cell">
|
||||
<Badge class="text-xs" variant="outline">
|
||||
Declined
|
||||
</Badge>
|
||||
</Table.Cell>
|
||||
<Table.Cell class="hidden md:table-cell">
|
||||
2023-06-24
|
||||
</Table.Cell>
|
||||
<Table.Cell class="text-right">$150.00</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<div class="font-medium">Noah Williams</div>
|
||||
<div
|
||||
class="hidden text-sm text-muted-foreground md:inline"
|
||||
>
|
||||
noah@example.com
|
||||
</div>
|
||||
</Table.Cell>
|
||||
<Table.Cell class="hidden sm:table-cell">
|
||||
Subscription
|
||||
</Table.Cell>
|
||||
<Table.Cell class="hidden sm:table-cell">
|
||||
<Badge class="text-xs" variant="secondary">
|
||||
Fulfilled
|
||||
</Badge>
|
||||
</Table.Cell>
|
||||
<Table.Cell class="hidden md:table-cell">
|
||||
2023-06-25
|
||||
</Table.Cell>
|
||||
<Table.Cell class="text-right">$350.00</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<div class="font-medium">Emma Brown</div>
|
||||
<div
|
||||
class="hidden text-sm text-muted-foreground md:inline"
|
||||
>
|
||||
emma@example.com
|
||||
</div>
|
||||
</Table.Cell>
|
||||
<Table.Cell class="hidden sm:table-cell">
|
||||
Sale
|
||||
</Table.Cell>
|
||||
<Table.Cell class="hidden sm:table-cell">
|
||||
<Badge class="text-xs" variant="secondary">
|
||||
Fulfilled
|
||||
</Badge>
|
||||
</Table.Cell>
|
||||
<Table.Cell class="hidden md:table-cell">
|
||||
2023-06-26
|
||||
</Table.Cell>
|
||||
<Table.Cell class="text-right">$450.00</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<div class="font-medium">Liam Johnson</div>
|
||||
<div
|
||||
class="hidden text-sm text-muted-foreground md:inline"
|
||||
>
|
||||
liam@example.com
|
||||
</div>
|
||||
</Table.Cell>
|
||||
<Table.Cell class="hidden sm:table-cell">
|
||||
Sale
|
||||
</Table.Cell>
|
||||
<Table.Cell class="hidden sm:table-cell">
|
||||
<Badge class="text-xs" variant="secondary">
|
||||
Fulfilled
|
||||
</Badge>
|
||||
</Table.Cell>
|
||||
<Table.Cell class="hidden md:table-cell">
|
||||
2023-06-23
|
||||
</Table.Cell>
|
||||
<Table.Cell class="text-right">$250.00</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<div class="font-medium">Liam Johnson</div>
|
||||
<div
|
||||
class="hidden text-sm text-muted-foreground md:inline"
|
||||
>
|
||||
liam@example.com
|
||||
</div>
|
||||
</Table.Cell>
|
||||
<Table.Cell class="hidden sm:table-cell">
|
||||
Sale
|
||||
</Table.Cell>
|
||||
<Table.Cell class="hidden sm:table-cell">
|
||||
<Badge class="text-xs" variant="secondary">
|
||||
Fulfilled
|
||||
</Badge>
|
||||
</Table.Cell>
|
||||
<Table.Cell class="hidden md:table-cell">
|
||||
2023-06-23
|
||||
</Table.Cell>
|
||||
<Table.Cell class="text-right">$250.00</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<div class="font-medium">Olivia Smith</div>
|
||||
<div
|
||||
class="hidden text-sm text-muted-foreground md:inline"
|
||||
>
|
||||
olivia@example.com
|
||||
</div>
|
||||
</Table.Cell>
|
||||
<Table.Cell class="hidden sm:table-cell">
|
||||
Refund
|
||||
</Table.Cell>
|
||||
<Table.Cell class="hidden sm:table-cell">
|
||||
<Badge class="text-xs" variant="outline">
|
||||
Declined
|
||||
</Badge>
|
||||
</Table.Cell>
|
||||
<Table.Cell class="hidden md:table-cell">
|
||||
2023-06-24
|
||||
</Table.Cell>
|
||||
<Table.Cell class="text-right">$150.00</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<div class="font-medium">Emma Brown</div>
|
||||
<div
|
||||
class="hidden text-sm text-muted-foreground md:inline"
|
||||
>
|
||||
emma@example.com
|
||||
</div>
|
||||
</Table.Cell>
|
||||
<Table.Cell class="hidden sm:table-cell">
|
||||
Sale
|
||||
</Table.Cell>
|
||||
<Table.Cell class="hidden sm:table-cell">
|
||||
<Badge class="text-xs" variant="secondary">
|
||||
Fulfilled
|
||||
</Badge>
|
||||
</Table.Cell>
|
||||
<Table.Cell class="hidden md:table-cell">
|
||||
2023-06-26
|
||||
</Table.Cell>
|
||||
<Table.Cell class="text-right">$450.00</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
</Tabs.Content>
|
||||
</Tabs.Root>
|
||||
</div>
|
||||
<div>
|
||||
<Card.Root class="overflow-hidden">
|
||||
<Card.Header class="flex flex-row items-start bg-muted/50">
|
||||
<div class="grid gap-0.5">
|
||||
<Card.Title class="group flex items-center gap-2 text-lg">
|
||||
Order Oe31b70H
|
||||
<Button
|
||||
size="icon"
|
||||
variant="outline"
|
||||
class="h-6 w-6 opacity-0 transition-opacity group-hover:opacity-100"
|
||||
>
|
||||
<Copy class="h-3 w-3" />
|
||||
<span class="sr-only">Copy Order ID</span>
|
||||
</Button>
|
||||
</Card.Title>
|
||||
<Card.Description>Date: November 23, 2023</Card.Description>
|
||||
</div>
|
||||
<div class="ml-auto flex items-center gap-1">
|
||||
<Button size="sm" variant="outline" class="h-8 gap-1">
|
||||
<Truck class="h-3.5 w-3.5" />
|
||||
<span class="lg:sr-only xl:not-sr-only xl:whitespace-nowrap">
|
||||
Track Order
|
||||
</span>
|
||||
</Button>
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger asChild let:builder>
|
||||
<Button
|
||||
builders={[builder]}
|
||||
size="icon"
|
||||
variant="outline"
|
||||
class="h-8 w-8"
|
||||
>
|
||||
<EllipsisVertical class="h-3.5 w-3.5" />
|
||||
<span class="sr-only">More</span>
|
||||
</Button>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content align="end">
|
||||
<DropdownMenu.Item>Edit</DropdownMenu.Item>
|
||||
<DropdownMenu.Item>Export</DropdownMenu.Item>
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.Item>Trash</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
</div>
|
||||
</Card.Header>
|
||||
<Card.Content class="p-6 text-sm">
|
||||
<div class="grid gap-3">
|
||||
<div class="font-semibold">Order Details</div>
|
||||
<ul class="grid gap-3">
|
||||
<li class="flex items-center justify-between">
|
||||
<span class="text-muted-foreground">
|
||||
Glimmer Lamps x <span>2</span>
|
||||
</span>
|
||||
<span>$250.00</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<span class="text-muted-foreground">
|
||||
Aqua Filters x <span>1</span>
|
||||
</span>
|
||||
<span>$49.00</span>
|
||||
</li>
|
||||
</ul>
|
||||
<Separator class="my-2" />
|
||||
<ul class="grid gap-3">
|
||||
<li class="flex items-center justify-between">
|
||||
<span class="text-muted-foreground">Subtotal</span>
|
||||
<span>$299.00</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<span class="text-muted-foreground">Shipping</span>
|
||||
<span>$5.00</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<span class="text-muted-foreground">Tax</span>
|
||||
<span>$25.00</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between font-semibold">
|
||||
<span class="text-muted-foreground">Total</span>
|
||||
<span>$329.00</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<Separator class="my-4" />
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="grid gap-3">
|
||||
<div class="font-semibold">Shipping Information</div>
|
||||
<address class="grid gap-0.5 not-italic text-muted-foreground">
|
||||
<span>Liam Johnson</span>
|
||||
<span>1234 Main St.</span>
|
||||
<span>Anytown, CA 12345</span>
|
||||
</address>
|
||||
</div>
|
||||
<div class="grid auto-rows-max gap-3">
|
||||
<div class="font-semibold">Billing Information</div>
|
||||
<div class="text-muted-foreground">Same as shipping address</div>
|
||||
</div>
|
||||
</div>
|
||||
<Separator class="my-4" />
|
||||
<div class="grid gap-3">
|
||||
<div class="font-semibold">Customer Information</div>
|
||||
<dl class="grid gap-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<dt class="text-muted-foreground">Customer</dt>
|
||||
<dd>Liam Johnson</dd>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<dt class="text-muted-foreground">Email</dt>
|
||||
<dd>
|
||||
<a href="mailto:">liam@acme.com</a>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<dt class="text-muted-foreground">Phone</dt>
|
||||
<dd>
|
||||
<a href="tel:">+1 234 567 890</a>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
<Separator class="my-4" />
|
||||
<div class="grid gap-3">
|
||||
<div class="font-semibold">Payment Information</div>
|
||||
<dl class="grid gap-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<dt class="flex items-center gap-1 text-muted-foreground">
|
||||
<CreditCard class="h-4 w-4" />
|
||||
Visa
|
||||
</dt>
|
||||
<dd>**** **** **** 4532</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</Card.Content>
|
||||
<Card.Footer class="flex flex-row items-center border-t bg-muted/50 px-6 py-3">
|
||||
<div class="text-xs text-muted-foreground">
|
||||
Updated <time dateTime="2023-11-23">November 23, 2023</time>
|
||||
</div>
|
||||
<Pagination.Root count={10} class="ml-auto mr-0 w-auto">
|
||||
<Pagination.Content>
|
||||
<Pagination.Item>
|
||||
<Button size="icon" variant="outline" class="h-6 w-6">
|
||||
<ChevronLeft class="h-3.5 w-3.5" />
|
||||
<span class="sr-only">Previous Order</span>
|
||||
</Button>
|
||||
</Pagination.Item>
|
||||
<Pagination.Item>
|
||||
<Button size="icon" variant="outline" class="h-6 w-6">
|
||||
<ChevronRight class="h-3.5 w-3.5" />
|
||||
<span class="sr-only">Next Order</span>
|
||||
</Button>
|
||||
</Pagination.Item>
|
||||
</Pagination.Content>
|
||||
</Pagination.Root>
|
||||
</Card.Footer>
|
||||
</Card.Root>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
@@ -18,6 +18,11 @@
|
||||
import { Separator } from "$lib/components/ui/separator/index.js";
|
||||
import * as Table from "$lib/components/ui/table/index.js";
|
||||
import * as Tabs from "$lib/components/ui/tabs/index.js";
|
||||
import type { NDKEvent } from "@nostr-dev-kit/ndk";
|
||||
import Pie from "./Pie.svelte";
|
||||
|
||||
export let rocket:NDKEvent;
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
@@ -30,19 +35,17 @@
|
||||
|
||||
<Breadcrumb.Root class="hidden md:flex">
|
||||
<Breadcrumb.List>
|
||||
<Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<Breadcrumb.Link href="##">{rocket.getMatchingTags('d')[0][1]}</Breadcrumb.Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Separator />
|
||||
<Breadcrumb.Item>
|
||||
<Breadcrumb.Link href="##">Dashboard</Breadcrumb.Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Separator />
|
||||
<Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<Breadcrumb.Page>Dashboard</Breadcrumb.Page>
|
||||
</Breadcrumb.Item>
|
||||
|
||||
</Breadcrumb.List>
|
||||
</Breadcrumb.Root>
|
||||
</header>
|
||||
<Pie />
|
||||
<main
|
||||
class="grid flex-1 items-start gap-4 p-4 sm:px-6 sm:py-0 md:gap-8 lg:grid-cols-3 xl:grid-cols-3"
|
||||
|
||||
98
src/lib/event_helpers/rockets.ts
Normal file
98
src/lib/event_helpers/rockets.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { NDKEvent, type NDKTag } from "@nostr-dev-kit/ndk";
|
||||
import type NDKSvelte from "@nostr-dev-kit/ndk-svelte";
|
||||
|
||||
export function getZapData(ndk:NDKSvelte, zap: NDKEvent, rocket:NDKEvent) {
|
||||
let productPrice = 0;
|
||||
let zapAmount = 0;
|
||||
let productID: string | undefined = undefined;
|
||||
let buyerPubkey: string | undefined = undefined;
|
||||
let zapRequest: NDKEvent | undefined = undefined;
|
||||
|
||||
let desc = zap.getMatchingTags('description');
|
||||
if (desc && desc.length == 1 && rocket) {
|
||||
zapRequest = new NDKEvent(ndk, JSON.parse(desc[0][1]));
|
||||
let zapRequestETags = zapRequest.getMatchingTags('e');
|
||||
|
||||
if (zapRequestETags && zapRequestETags.length > 0) {
|
||||
for (let productIDfromZapRequest of zapRequestETags) {
|
||||
if (productIDfromZapRequest.length > 1) {
|
||||
let productsInRocket = getMapOfProductsFromRocket(rocket);
|
||||
if (productsInRocket.size > 0) {
|
||||
productID = productIDfromZapRequest[1];
|
||||
if (productID.length == 64) {
|
||||
let productDataFromRocket = productsInRocket.get(productID);
|
||||
if (productDataFromRocket) {
|
||||
productPrice = productDataFromRocket.Price;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let amount = zapRequest.getMatchingTags('amount');
|
||||
if (amount && amount.length == 1) {
|
||||
if (amount[0].length == 2) {
|
||||
zapAmount = parseInt(amount[0][1], 10);
|
||||
}
|
||||
}
|
||||
buyerPubkey = zapRequest.author.pubkey;
|
||||
}
|
||||
let success = false;
|
||||
if (zapRequest && productID && buyerPubkey && productPrice && zapAmount) {
|
||||
if (zapAmount >= productPrice && productID.length == 64 && buyerPubkey.length == 64) {
|
||||
success = true;
|
||||
return {
|
||||
productPrice: productPrice,
|
||||
zapAmount: zapAmount,
|
||||
productID: productID,
|
||||
buyerPubkey: buyerPubkey,
|
||||
zapReceipt: zap.id
|
||||
};
|
||||
}
|
||||
}
|
||||
if (!success) {
|
||||
console.log('invalid product payment zap found:', zapRequest?.rawEvent());
|
||||
}
|
||||
}
|
||||
|
||||
export class RocketProduct {
|
||||
ID: string;
|
||||
Price: number;
|
||||
ValidAfter: number; //unix time
|
||||
MaxPurchases: number;
|
||||
Purchases: Map<string, ProductPayment>;
|
||||
|
||||
constructor(tag: NDKTag) {
|
||||
this.Purchases = new Map();
|
||||
this.ID = tag[1].split(':')[0];
|
||||
this.Price = parseInt(tag[1].split(':')[1], 10);
|
||||
this.ValidAfter = parseInt(tag[1].split(':')[2], 10);
|
||||
this.MaxPurchases = parseInt(tag[1].split(':')[3], 10);
|
||||
let purchases = JSON.parse(tag[3]);
|
||||
for (let p of purchases) {
|
||||
let payment = new ProductPayment(p);
|
||||
this.Purchases.set(payment.ZapID, payment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ProductPayment {
|
||||
ZapID: string;
|
||||
BuyerPubkey: string;
|
||||
WitnessedAt: number;
|
||||
constructor(purchase: string) {
|
||||
this.ZapID = purchase.split(':')[0];
|
||||
this.BuyerPubkey = purchase.split(':')[1];
|
||||
this.WitnessedAt = parseInt(purchase.split(':')[2], 10);
|
||||
}
|
||||
}
|
||||
|
||||
export function getMapOfProductsFromRocket(rocket: NDKEvent): Map<string, RocketProduct> {
|
||||
let productIDs = new Map<string, RocketProduct>();
|
||||
for (let product of rocket.getMatchingTags('product')) {
|
||||
if (product.length > 1 && product[1].split(':') && product[1].split(':').length > 0) {
|
||||
productIDs.set(product[1].split(':')[0], new RocketProduct(product));
|
||||
}
|
||||
}
|
||||
return productIDs;
|
||||
}
|
||||
@@ -1,15 +1,18 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { ndk } from '@/ndk';
|
||||
import { NDKEvent, type NDKTag } from '@nostr-dev-kit/ndk';
|
||||
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||
import type { ExtendedBaseType, NDKEventStore } from '@nostr-dev-kit/ndk-svelte';
|
||||
import { onDestroy } from 'svelte';
|
||||
import { derived, type Readable } from 'svelte/store';
|
||||
import CreateNewProduct from '../../../components/CreateNewProduct.svelte';
|
||||
import Heading from '../../../components/Heading.svelte';
|
||||
import ProductCard from '../../../components/ProductCard.svelte';
|
||||
import ProductsForRocket from '../../../components/ProductsForRocket.svelte';
|
||||
import Subheading from '../../../components/Subheading.svelte';
|
||||
import Todo from '../../../components/Todo.svelte';
|
||||
import EmptyList from '../../../components/EmptyList.svelte';
|
||||
import RocketDashboard from '../../../components/RocketDashboard.svelte';
|
||||
//flow if we only have a d-tag: fetch all 31108's with this d-tag, sort by WoT, put Nostrocket Name Service one at the top. Dedupe same rocket (same state, shadows) from multiple users, just show them all as everyone agreeing.
|
||||
//second pass: fetch ignition event for each, rebuild current state and validate all proofs, compute votepower and display only the states with > 50%.
|
||||
|
||||
@@ -19,7 +22,6 @@
|
||||
|
||||
let rocketEvents: NDKEventStore<NDKEvent> | undefined;
|
||||
let latestRocketEvent: Readable<ExtendedBaseType<NDKEvent> | undefined>;
|
||||
let zaps: Readable<ExtendedBaseType<NDKEvent>[]>;
|
||||
|
||||
let candidateProducts: Readable<ExtendedBaseType<NDKEvent>[]>;
|
||||
onDestroy(() => {
|
||||
@@ -70,63 +72,16 @@
|
||||
return e.kind == 1908;
|
||||
});
|
||||
});
|
||||
|
||||
zaps = derived(rocketEvents, ($events) => {
|
||||
return $events.filter((e) => {
|
||||
return e.kind == 9735;
|
||||
});
|
||||
});
|
||||
|
||||
let existingProducts = derived(latestRocketEvent, ($latestRocketEvent)=>{
|
||||
let m = new Map<string, any>()
|
||||
if ($latestRocketEvent) {
|
||||
let products = getMapOfProducts($latestRocketEvent)
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RocketProduct {
|
||||
ID: string;
|
||||
Price: number;
|
||||
ValidAfter: number; //unix time
|
||||
MaxPurchases: number;
|
||||
Purchases: Map<string, ProductPayment>;
|
||||
constructor(tag:NDKTag) {
|
||||
this.Purchases = new Map()
|
||||
this.ID = tag[1].split(':')[0]
|
||||
this.Price = parseInt(tag[1].split(':')[1], 10)
|
||||
this.ValidAfter = parseInt(tag[1].split(':')[2], 10)
|
||||
this.MaxPurchases = parseInt(tag[1].split(':')[3], 10)
|
||||
let purchases = JSON.parse(tag[3])
|
||||
for (let p of purchases) {
|
||||
let payment = new ProductPayment(p)
|
||||
this.Purchases.set(payment.ZapID, payment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ProductPayment {
|
||||
ZapID: string;
|
||||
BuyerPubkey: string;
|
||||
WitnessedAt: number;
|
||||
constructor(purchase:string) {
|
||||
this.ZapID = purchase.split(":")[0]
|
||||
this.BuyerPubkey = purchase.split(":")[1]
|
||||
this.WitnessedAt = parseInt(purchase.split(":")[2], 10)
|
||||
}
|
||||
}
|
||||
|
||||
function getMapOfProducts(rocket: NDKEvent): Map<string, RocketProduct> {
|
||||
let productIDs = new Map<string, RocketProduct>();
|
||||
for (let product of rocket.getMatchingTags('product')) {
|
||||
if (product.length > 1 && product[1].split(':') && product[1].split(':').length > 0) {
|
||||
productIDs.set(product[1].split(':')[0], new RocketProduct(product));
|
||||
}
|
||||
}
|
||||
return productIDs;
|
||||
class ZapPurchase {
|
||||
Amount: number;
|
||||
ProductID: string;
|
||||
Buyer: string;
|
||||
ZapReceiptID: string;
|
||||
constructor(zapReceipt: NDKEvent) {}
|
||||
}
|
||||
|
||||
//todo: check that this zap is not already included in the payment JSON for the product
|
||||
@@ -134,65 +89,13 @@
|
||||
//todo: make the page flash or something and show each time someone buys the product.
|
||||
//todo: split this out so that we can consume it for the payment page too (so that we know if there are really products left or they're all sold)
|
||||
//todo: make store of all purchases (in rocket and zaps), sort by timestamp and render with profile of buyer
|
||||
function getZapData(zap: NDKEvent) {
|
||||
let productPrice = 0;
|
||||
let zapAmount = 0;
|
||||
let productID: string | undefined = undefined;
|
||||
let buyerPubkey: string | undefined = undefined;
|
||||
let zapRequest: NDKEvent | undefined = undefined;
|
||||
|
||||
let desc = zap.getMatchingTags('description');
|
||||
if (desc && desc.length == 1 && $latestRocketEvent) {
|
||||
zapRequest = new NDKEvent($ndk, JSON.parse(desc[0][1]));
|
||||
let zapRequestETags = zapRequest.getMatchingTags('e');
|
||||
|
||||
if (zapRequestETags && zapRequestETags.length > 0) {
|
||||
for (let productIDfromZapRequest of zapRequestETags) {
|
||||
if (productIDfromZapRequest.length > 1) {
|
||||
let productsInRocket = getMapOfProducts($latestRocketEvent);
|
||||
if (productsInRocket.size > 0) {
|
||||
productID = productIDfromZapRequest[1];
|
||||
if (productID.length == 64) {
|
||||
let productDataFromRocket = productsInRocket.get(productID);
|
||||
if (productDataFromRocket) {
|
||||
productPrice = productDataFromRocket.Price
|
||||
}
|
||||
} else {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let amount = zapRequest.getMatchingTags('amount');
|
||||
if (amount && amount.length == 1) {
|
||||
if (amount[0].length == 2) {
|
||||
zapAmount = parseInt(amount[0][1], 10);
|
||||
}
|
||||
}
|
||||
buyerPubkey = zapRequest.author.pubkey;
|
||||
}
|
||||
let success = false;
|
||||
if (zapRequest && productID && buyerPubkey && productPrice && zapAmount) {
|
||||
if (zapAmount >= productPrice && productID.length == 64 && buyerPubkey.length == 64) {
|
||||
success = true;
|
||||
return {
|
||||
productPrice: productPrice,
|
||||
zapAmount: zapAmount,
|
||||
productID: productID,
|
||||
buyerPubkey: buyerPubkey,
|
||||
zapReceipt: zap.id
|
||||
};
|
||||
}
|
||||
}
|
||||
if (!success) {
|
||||
console.log('invalid product payment zap found:', zapRequest?.rawEvent());
|
||||
}
|
||||
}
|
||||
|
||||
//todo: handle shadow events (fetch the shadowed event and render it instead)
|
||||
</script>
|
||||
|
||||
{#if latestRocketEvent && $latestRocketEvent}
|
||||
<RocketDashboard rocket={$latestRocketEvent} />
|
||||
{/if}
|
||||
{#if latestRocketEvent && $latestRocketEvent && false}
|
||||
<Heading title={$latestRocketEvent.getMatchingTags('d')[0][1]} />
|
||||
|
||||
<Todo
|
||||
@@ -201,27 +104,28 @@
|
||||
'modify relevant data and republish event according to https://github.com/nostrocket/NIPS/blob/main/31108.md and https://github.com/nostrocket/NIPS/blob/main/MSBR334000.md '
|
||||
]}
|
||||
/>
|
||||
{#if candidateProducts && $candidateProducts}
|
||||
<Subheading title="Product Candidates" />
|
||||
<CreateNewProduct rocketEvent={$latestRocketEvent} />
|
||||
{#each $candidateProducts as r}<ProductCard rocket={$latestRocketEvent} product={r} />{/each}
|
||||
{/if}
|
||||
<div class="flex flex-col gap-1 text-left">
|
||||
<h3 class="text-xl font-bold tracking-tight">
|
||||
{$latestRocketEvent.getMatchingTags('d')[0][1].toLocaleUpperCase()} Products
|
||||
</h3>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
If there are products available for purchase they will be listed here
|
||||
</p>
|
||||
</div>
|
||||
<ProductsForRocket rocketEvent={$latestRocketEvent} />
|
||||
|
||||
{#if zaps && $zaps}
|
||||
{#each $zaps as z}
|
||||
{#if getZapData(z)}{getZapData(z)?.buyerPubkey}{/if}
|
||||
<p
|
||||
on:click={() => {
|
||||
let zapdata = getZapData(z);
|
||||
if (zapdata) {
|
||||
console.log(zapdata);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{z.id}
|
||||
</p>{/each}
|
||||
{/if}
|
||||
<div class="flex flex-col gap-1 text-left pt-4">
|
||||
<h3 class="text-xl font-bold tracking-tight">
|
||||
{$latestRocketEvent.getMatchingTags('d')[0][1].toLocaleUpperCase()} Product Proposals
|
||||
</h3>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
If particpants of {$latestRocketEvent.getMatchingTags('d')[0][1]} have proposed any new products that are not yet included for sale, they will be listed here.
|
||||
</p>
|
||||
</div>
|
||||
{#each $candidateProducts as r}<ProductCard rocket={$latestRocketEvent} product={r} />{/each}
|
||||
<CreateNewProduct rocketEvent={$latestRocketEvent} />
|
||||
{:else}
|
||||
<Heading title="Fetching events for the requested rocket" />
|
||||
IGNITION: {rIgnitionOrActual} <br />
|
||||
NAME: {rName} <br />
|
||||
PUBKEY: {rPubkey} <br />
|
||||
|
||||
Reference in New Issue
Block a user