problem: can't see product purchase data for rocket

This commit is contained in:
gsovereignty
2024-07-11 13:22:08 +08:00
parent 44a4bbea4e
commit e8d651e302
10 changed files with 242 additions and 512 deletions

View File

@@ -0,0 +1,44 @@
<script lang="ts">
import * as Card from "@/components/ui/card";
import Pie from "./Pie.svelte";
import * as Table from "@/components/ui/table";
import type { NDKEvent } from "@nostr-dev-kit/ndk";
export let rocket:NDKEvent;
</script>
<Card.Root class="sm:col-span-3">
<Card.Header class="pb-3">
<Card.Title>Merits and Satflow</Card.Title>
<Card.Description class="grid grid-cols-2">
<div class=" grid-cols-1">
This graph displays the Meritization of equity in {rocket.getMatchingTags('d')[0][1]}
<Pie />
</div>
<div class=" grid-cols-1">
<Table.Root>
<Table.Header>
<Table.Row>
<Table.Head>Participant</Table.Head>
<Table.Head class="hidden md:table-cell">Merits</Table.Head>
<Table.Head class="text-right">Sats Received</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row class=" bg-red-800">
<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">17%</Table.Cell>
<Table.Cell class="text-right">250k</Table.Cell>
</Table.Row>
</Table.Body>
</Table.Root>
</div>
</Card.Description>
</Card.Header>
<Card.Footer>
</Card.Footer>
</Card.Root>

View File

@@ -3,7 +3,7 @@
const options = { const options = {
series: [35.1, 23.5, 2.4, 5.4], series: [35.1, 23.5, 2.4, 5.4],
colors: ['#1C64F2', '#16BDCA', '#FDBA8C', '#E74694'], colors: ['#9b1c1c', '#16BDCA', '#FDBA8C', '#E74694'],
chart: { chart: {
height: 320, height: 320,
width: '100%', width: '100%',
@@ -84,21 +84,23 @@
} }
} }
}; };
// <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>
// </Card>
</script> </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>
<Chart {options} class="py-6" /> <Chart {options} class="py-6" />

View File

@@ -1,12 +1,7 @@
<script lang="ts"> <script lang="ts">
import AddProductToRocket from './AddProductToRocket.svelte';
import { goto } from '$app/navigation';
import { base } from '$app/paths';
import { Button } from '$lib/components/ui/button/index.js';
import * as Card from '$lib/components/ui/card/index.js'; import * as Card from '$lib/components/ui/card/index.js';
import { getMission, getRocketURL } from '@/helpers';
import type { NDKEvent } from '@nostr-dev-kit/ndk'; import type { NDKEvent } from '@nostr-dev-kit/ndk';
import { ChevronRight } from 'lucide-svelte'; import AddProductToRocket from './AddProductToRocket.svelte';
import PayNow from './PayNow.svelte'; import PayNow from './PayNow.svelte';
export let product: NDKEvent; export let product: NDKEvent;

View File

@@ -0,0 +1,30 @@
<script lang="ts">
import * as Card from "$lib/components/ui/card/index.js";
import * as Table from "$lib/components/ui/table/index.js";
import type { RocketProduct } from "@/event_helpers/rockets";
import type { NDKEvent } from "@nostr-dev-kit/ndk";
import ProductCardFromId from "./ProductCardFromID.svelte";
import ProductPurchases from "./ProductPurchases.svelte";
export let product:RocketProduct;
export let rocket:NDKEvent;
</script>
<Card.Root class="sm:col-span-3">
<Card.Header class="pb-3">
<Card.Title>Products and Purchases</Card.Title>
<Card.Description class="grid grid-cols-2">
<div class=" grid-cols-1">
<ProductCardFromId {rocket} productID={product.ID} />
</div>
<div class="grid-cols-1">
<ProductPurchases {rocket} {product} />
</div>
</Card.Description>
</Card.Header>
<Card.Footer>
</Card.Footer>

View File

@@ -1,11 +1,13 @@
<script lang="ts"> <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 Card from '@/components/ui/card';
import * as Table from '@/components/ui/table'; import * as Table from '@/components/ui/table';
import { Badge } from '@/components/ui/badge'; import type { RocketProduct } from '@/event_helpers/rockets';
import { unixToRelativeTime } from '@/helpers';
import { ndk } from '@/ndk';
import { NDKEvent } from '@nostr-dev-kit/ndk';
import { Avatar, Name } from '@nostr-dev-kit/ndk-svelte-components';
import { onDestroy, onMount } from 'svelte';
import { derived } from 'svelte/store';
export let product: RocketProduct; export let product: RocketProduct;
export let rocket: NDKEvent; export let rocket: NDKEvent;
@@ -21,6 +23,59 @@
zaps?.unsubscribe(); zaps?.unsubscribe();
}); });
let productEvent: NDKEvent | undefined;
onMount(() => {
$ndk.fetchEvent(product.ID).then((e) => {
if (e) {
productEvent = e;
}
});
});
let newZaps = derived(zaps, ($zaps) => {
let zapMap = new Map<string, NDKEvent>();
for (let z of $zaps) {
if (!product.Purchases.get(z.id)) {
let zapRequestEvent = getZapRequest(z);
if (zapRequestEvent) {
for (let zapEtag of zapRequestEvent.getMatchingTags('e')) {
if (zapEtag && zapEtag.length > 1 && zapEtag[1].length == 64) {
if (product.ID == zapEtag[1]) {
//todo: validate zapper pubkey is from a LSP specified in rocket
//todo: validate amount is same as product amount in rocket
zapMap.set(z.id, z);
}
}
}
}
}
}
return zapMap;
});
function getZapRequest(zapReceipt: NDKEvent): NDKEvent | undefined {
let zapRequestEvent: NDKEvent | undefined = undefined;
let zapRequest = zapReceipt.getMatchingTags('description');
if (zapRequest.length == 1) {
let zapRequestJSON = JSON.parse(zapRequest[0][1]);
if (zapRequestJSON) {
zapRequestEvent = new NDKEvent($ndk, zapRequestJSON);
}
}
return zapRequestEvent;
}
function getZapAmount(zapRequest?: NDKEvent): number {
let amount = 0;
let amountTag = zapRequest?.getMatchingTags('amount');
if (amountTag?.length == 1) {
amount = parseInt(amountTag[0][1], 10);
}
return amount;
}
//fetch payments from rocket::product and live zaps and make a store Map<productID, []payments>
//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. //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> </script>
@@ -31,32 +86,47 @@
}}>{z.id}</a }}>{z.id}</a
><br />{/each} ><br />{/each}
<Card.Root {#each $newZaps as [id, zapReceipt]}{/each}
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.Root>
>
<Card.Header class="px-7"> <Card.Header class="px-7">
<Card.Title>Purchases</Card.Title> <Card.Title>Purchases</Card.Title>
<Card.Description>Purchase history for {product.ID}</Card.Description> <Card.Description
>Purchase history for {productEvent?.getMatchingTags('name')[0][1]}</Card.Description
>
</Card.Header> </Card.Header>
<Card.Content> <Card.Content>
<Table.Root> <Table.Root>
<Table.Header> <Table.Header>
<Table.Row> <Table.Row>
<Table.Head>Buyer</Table.Head> <Table.Head>Buyer</Table.Head>
<Table.Head class="hidden md:table-cell">Date</Table.Head> <Table.Head class="hidden md:table-cell">Sats Paid</Table.Head>
<Table.Head class="text-right">Amount</Table.Head> <Table.Head class="text-right"></Table.Head>
</Table.Row> </Table.Row>
</Table.Header> </Table.Header>
<Table.Body> <Table.Body>
<Table.Row class="bg-accent"> {#each $newZaps as [id, zapReceipt]}
<Table.Row class=" bg-red-800">
<Table.Cell> <Table.Cell>
<div class="font-medium">Liam Johnson</div> <div class="flex flex-nowrap">
<div class="hidden text-sm text-muted-foreground md:inline">liam@example.com</div> <Avatar
ndk={$ndk}
pubkey={getZapRequest(zapReceipt)?.author.pubkey}
class="h-10 w-10 flex-none rounded-full object-cover"
/>
<Name
ndk={$ndk}
pubkey={getZapRequest(zapReceipt)?.author.pubkey}
class="inline-block truncate p-2"
/>
</div>
</Table.Cell> </Table.Cell>
<Table.Cell class="hidden md:table-cell">2023-06-23</Table.Cell> <Table.Cell class="hidden md:table-cell"
<Table.Cell class="text-right">$250.00</Table.Cell> >{getZapAmount(getZapRequest(zapReceipt)) / 1000}</Table.Cell
>
<Table.Cell class="text-right">{unixToRelativeTime(zapReceipt.created_at*1000)}</Table.Cell>
</Table.Row> </Table.Row>
{/each}
</Table.Body> </Table.Body>
</Table.Root> </Table.Root>
</Card.Content> </Card.Content>

View File

@@ -1,12 +1,9 @@
<script lang="ts"> <script lang="ts">
import type { NDKEvent } from '@nostr-dev-kit/ndk'; import type { NDKEvent } from '@nostr-dev-kit/ndk';
import Todo from './Todo.svelte'; import { getMapOfProductsFromRocket } from '@/event_helpers/rockets';
import { writable, type Readable } from 'svelte/store'; import ProductCardFromID from './ProductCardFromID.svelte';
import { getMapOfProductsFromRocket, type RocketProduct } from '@/event_helpers/rockets';
import ProductPurchases from './ProductPurchases.svelte'; import ProductPurchases from './ProductPurchases.svelte';
import ProductCard from './ProductCard.svelte';
import ProductCardFromEvent from './ProductCardFromEvent.svelte';
export let rocketEvent: NDKEvent; export let rocketEvent: NDKEvent;
@@ -16,7 +13,7 @@
{#if rocketEvent && rocketProducts.size > 0} {#if rocketEvent && rocketProducts.size > 0}
{#each rocketProducts as [id, product]} {#each rocketProducts as [id, product]}
<ProductCardFromEvent rocket={rocketEvent} productID={product.ID} /> <ProductCardFromID rocket={rocketEvent} productID={product.ID} />
<ProductPurchases rocket={rocketEvent} {product} />{/each} <ProductPurchases rocket={rocketEvent} {product} />{/each}
{/if} {/if}

View File

@@ -1,32 +1,30 @@
<script lang="ts"> <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 * as Breadcrumb from "$lib/components/ui/breadcrumb/index.js";
import { Button } from "$lib/components/ui/button/index.js"; import { RocketProduct } from "@/event_helpers/rockets";
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";
import type { NDKEvent } from "@nostr-dev-kit/ndk"; import type { NDKEvent } from "@nostr-dev-kit/ndk";
import Pie from "./Pie.svelte"; import { writable, type Writable } from "svelte/store";
import MeritsAndSatflow from "./MeritsAndSatflow.svelte";
import ProductFomo from "./ProductFomo.svelte";
export let rocket:NDKEvent; export let rocket:NDKEvent;
let products: Writable<RocketProduct[]> = writable([])
$: {
//fetch products from rocket and populate a store of them
let _products:RocketProduct[] = []
for (let p of rocket.getMatchingTags("product")) {
_products.push(new RocketProduct(p))
}
products.set(_products)
}
</script>
</script> </script>
<div class="flex flex-col sm:gap-4"> <div class="flex flex-col sm:gap-4">
<header <header
@@ -45,448 +43,14 @@
</Breadcrumb.List> </Breadcrumb.List>
</Breadcrumb.Root> </Breadcrumb.Root>
</header>
</header> </header>
<main <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" 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"> <MeritsAndSatflow {rocket} />
<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"> {#each $products as product}
<Card.Header class="pb-3"> <ProductFomo {rocket} {product} />
<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>
{/each} {/each}
</main> </main>
</div> </div>

View File

@@ -20,3 +20,33 @@ export function getMission(rocketEvent:NDKEvent):string {
} }
return "" return ""
} }
export function unixToRelativeTime(timestamp: number): string {
const currentTime = Date.now();
const secondsAgo = Math.floor((currentTime - timestamp) / 1000);
if (secondsAgo < 60) {
return `${secondsAgo} seconds ago`;
} else if (secondsAgo < 3600) {
const minutesAgo = Math.floor(secondsAgo / 60);
return `${minutesAgo} minute${minutesAgo === 1 ? '' : 's'} ago`;
} else if (secondsAgo < 86400) {
const hoursAgo = Math.floor(secondsAgo / 3600);
return `${hoursAgo} hour${hoursAgo === 1 ? '' : 's'} ago`;
} else if (secondsAgo < 604800) {
const daysAgo = Math.floor(secondsAgo / 86400);
return `${daysAgo} day${daysAgo === 1 ? '' : 's'} ago`;
} else {
const formattedDate = new Date(timestamp).toLocaleString('en-US', {
weekday: 'short',
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
hour12: true
});
return formattedDate;
}
}

View File

@@ -9,10 +9,8 @@
import Heading from '../../../components/Heading.svelte'; import Heading from '../../../components/Heading.svelte';
import ProductCard from '../../../components/ProductCard.svelte'; import ProductCard from '../../../components/ProductCard.svelte';
import ProductsForRocket from '../../../components/ProductsForRocket.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'; import RocketDashboard from '../../../components/RocketDashboard.svelte';
import Todo from '../../../components/Todo.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. //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%. //second pass: fetch ignition event for each, rebuild current state and validate all proofs, compute votepower and display only the states with > 50%.