diff options
| author | benj <benj@rse8.com> | 2026-04-10 11:13:34 +0800 |
|---|---|---|
| committer | benj <benj@rse8.com> | 2026-04-10 11:13:34 +0800 |
| commit | 493746b14c1251a45b061d2e3edd9160c929d2b9 (patch) | |
| tree | 1607cceb94c1aac1a17a01bb5c0d71b97342e892 /web/ui/src/routes/dashboard/usage | |
| parent | c041641634650c31e03c70dcad132fd94cb08e63 (diff) | |
| download | tidyindex-493746b14c1251a45b061d2e3edd9160c929d2b9.tar tidyindex-493746b14c1251a45b061d2e3edd9160c929d2b9.tar.gz tidyindex-493746b14c1251a45b061d2e3edd9160c929d2b9.tar.bz2 tidyindex-493746b14c1251a45b061d2e3edd9160c929d2b9.tar.lz tidyindex-493746b14c1251a45b061d2e3edd9160c929d2b9.tar.xz tidyindex-493746b14c1251a45b061d2e3edd9160c929d2b9.tar.zst tidyindex-493746b14c1251a45b061d2e3edd9160c929d2b9.zip | |
a basic ui and landing web interface for tidyindex.com
Diffstat (limited to 'web/ui/src/routes/dashboard/usage')
| -rw-r--r-- | web/ui/src/routes/dashboard/usage/+page.server.ts | 13 | ||||
| -rw-r--r-- | web/ui/src/routes/dashboard/usage/+page.svelte | 152 |
2 files changed, 165 insertions, 0 deletions
diff --git a/web/ui/src/routes/dashboard/usage/+page.server.ts b/web/ui/src/routes/dashboard/usage/+page.server.ts new file mode 100644 index 0000000..34ff002 --- /dev/null +++ b/web/ui/src/routes/dashboard/usage/+page.server.ts @@ -0,0 +1,13 @@ +import { PLANS } from '$lib/plans'; +import { usageByDataset, usageByKey, usageCountThisMonth } from '$lib/server/usage'; +import type { PageServerLoad } from './$types'; + +export const load: PageServerLoad = async ({ locals }) => { + const account = locals.account!; + return { + plan: PLANS[account.plan], + total: usageCountThisMonth(account.id), + byDataset: usageByDataset(account.id), + byKey: usageByKey(account.id) + }; +}; diff --git a/web/ui/src/routes/dashboard/usage/+page.svelte b/web/ui/src/routes/dashboard/usage/+page.svelte new file mode 100644 index 0000000..883460c --- /dev/null +++ b/web/ui/src/routes/dashboard/usage/+page.svelte @@ -0,0 +1,152 @@ +<script lang="ts"> + import { pushToast } from '$lib/stores/toasts'; + import type { PageData } from './$types'; + + let { data }: { data: PageData } = $props(); + + const monthName = new Date().toLocaleDateString('en-US', { + month: 'long', + year: 'numeric' + }); + + function fmt(n: number): string { + return n.toLocaleString('en-US'); + } + + const curlExample = `curl https://api.tidyindex.com/v1/datasets/irs-990/records/20-0049703 \\ + -H "Authorization: Bearer YOUR_API_KEY"`; + + async function copyCurl() { + try { + await navigator.clipboard.writeText(curlExample); + pushToast('Copied', 'success'); + } catch { + pushToast("Couldn't copy", 'error'); + } + } + + let pct = $derived.by(() => { + if (!Number.isFinite(data.plan.requestsPerMonth)) return 0; + const p = (data.total / data.plan.requestsPerMonth) * 100; + return Math.min(100, Math.max(0, p)); + }); + + let limitLabel = $derived( + Number.isFinite(data.plan.requestsPerMonth) + ? fmt(data.plan.requestsPerMonth) + : 'unlimited' + ); +</script> + +<p class="section-marker">ยง 02 · usage</p> +<h1 class="page-title">This month.</h1> +<p class="page-subtitle"> + Requests counted against your plan limit for {monthName}. +</p> + +<div class="card"> + <div class="usage-summary"> + <div> + <p class="text-mono text-mute" style="margin:0 0 6px;"> + requests / {limitLabel} + </p> + <p class="usage-count"> + {fmt(data.total)} + {#if Number.isFinite(data.plan.requestsPerMonth)} + <small> of {limitLabel}</small> + {/if} + </p> + </div> + <div> + <span class="badge">{data.plan.name.toLowerCase()} plan</span> + </div> + </div> + {#if Number.isFinite(data.plan.requestsPerMonth)} + <div class="usage-bar" aria-label="Usage bar"> + <div class="usage-bar-fill" style="width: {pct}%"></div> + </div> + {/if} +</div> + +<div class="card mt-24"> + <div class="card-head"> + <h2 class="card-title">By dataset</h2> + <p class="card-sub">Which datasets drew the most traffic.</p> + </div> + {#if data.byDataset.length === 0} + <div class="empty-state"> + <p class="empty-title">No requests yet this month.</p> + <p>Usage data will show up here once your keys start making calls.</p> + </div> + {:else} + <table class="usage-table"> + <thead> + <tr> + <th>dataset</th> + <th style="text-align:right;">requests</th> + <th style="text-align:right; width: 140px;">share</th> + </tr> + </thead> + <tbody> + {#each data.byDataset as row} + <tr> + <td>{row.dataset}</td> + <td class="num">{fmt(row.count)}</td> + <td class="num"> + {data.total > 0 + ? ((row.count / data.total) * 100).toFixed(1) + : '0.0'}% + </td> + </tr> + {/each} + </tbody> + </table> + {/if} +</div> + +<div class="card mt-24"> + <div class="card-head"> + <h2 class="card-title">By key</h2> + <p class="card-sub">Traffic attributed to each of your keys.</p> + </div> + {#if data.byKey.length === 0} + <div class="empty-state"> + <p class="empty-title">No keys yet.</p> + <p>Create one on the Keys tab.</p> + </div> + {:else} + <table class="usage-table"> + <thead> + <tr> + <th>key</th> + <th style="text-align:right;">requests</th> + </tr> + </thead> + <tbody> + {#each data.byKey as row} + <tr> + <td>{row.name}</td> + <td class="num">{fmt(row.count)}</td> + </tr> + {/each} + </tbody> + </table> + {/if} +</div> + +<div class="card mt-24"> + <div class="card-head"> + <h2 class="card-title">API base URL</h2> + </div> + <p class="card-sub">All endpoints live under this host.</p> + <pre class="code-block mt-16">https://api.tidyindex.com/v1</pre> +</div> + +<div class="card mt-24"> + <div class="card-head"> + <h2 class="card-title">Quick start</h2> + <button class="btn btn-sm btn-ghost" onclick={copyCurl}>Copy curl</button> + </div> + <p class="card-sub">Fetch one record from the IRS 990 dataset.</p> + <pre class="code-block mt-16">{curlExample}</pre> +</div> |
