feat(subscriptions): paying card set for several at once and kept in Firefly, quieter table

Each runway line says whether its card came from the last payment or
was set by hand; x selects bills and a sets the card for all of them,
stored in the Firefly preference ffui_bill_cards so every device sees
it. Price change moves to the subscription page, per year turns grey,
group sums follow the one-currency mode, key chips fit their buttons and
the panels no longer show a focus ring. Linking found payments is y.
This commit is contained in:
hh committed 2026-10-16 18:22:01 +00:00
1 parent 54895b9305
commit c610aa58fa
10 files changed
+285 -130

No files matched your search

+44 -29
View File
@@ -24,6 +24,7 @@
import Calendar from "./Calendar.svelte";
import CardMenu from "./CardMenu.svelte";
import Money from "./Money.svelte";
import Sums from "./Sums.svelte";
let {
needs,
@@ -69,8 +70,20 @@
needs.flatMap((n) => n.dues).find((d) => d.bill.id === id)?.bill ?? null;
}
function chosen(due: Due): string {
const card = byCurrency && due.card ? `${due.card.name}: ` : "";
if (due.manual) {
return `${card}set by you`;
}
if (!due.lastPaid) {
return "never paid";
}
const where = byCurrency ? "" : " here";
return `${card}last paid${where} ${formatDate(due.lastPaid, "short")}`;
}
const segment = (on: boolean) =>
buttonVariants({ size: "xs", variant: on ? "default" : "outline" });
buttonVariants({ size: "sm", variant: on ? "default" : "outline" });
</script>
<section class="cladd-surface cladd-outline mb-3 rounded-cladd-lg">
@@ -122,7 +135,7 @@
</p>
{:else}
<ContextMenu>
<ContextMenuTrigger {oncontextmenu}>
<ContextMenuTrigger class="outline-none" {oncontextmenu}>
<div
class="grid gap-3 p-3 [grid-template-columns:repeat(auto-fill,minmax(16rem,1fr))]"
>
@@ -143,7 +156,7 @@
<div
class="font-semibold text-[26px] text-cladd-fg leading-tight"
>
<Money {...total} />
<Money {...total} exact />
</div>
{/each}
{#if need.short && need.currency}
@@ -170,28 +183,33 @@
{/if}
<ul class="mt-2 border-cladd-outline border-t pt-1 text-[12px]">
{#each need.dues as due, i (`${due.bill.id}:${due.date}:${i}`)}
<li
class="flex h-6 items-center gap-2"
data-bill={due.bill.id}
>
<span
class="num w-12 shrink-0 {due.overdue
? "text-expense"
: "text-cladd-fg-soft"}"
>{due.overdue
? "overdue"
: formatDate(due.date, "short")}</span
<li class="py-0.5" data-bill={due.bill.id}>
<div class="flex h-5 items-center gap-2">
<span
class="num w-12 shrink-0 {due.overdue
? "text-expense"
: "text-cladd-fg-soft"}"
>{due.overdue
? "overdue"
: formatDate(due.date, "short")}</span
>
<a
class="min-w-0 flex-1 truncate text-cladd-fg hover:underline"
href="/subscriptions/show/{due.bill.id}"
>{due.bill.attributes.name}</a
>
<Money
{...due.pay}
approx={due.approx}
class="font-medium text-cladd-fg"
exact
/>
</div>
<div
class="truncate ps-14 text-[11px] text-cladd-fg-softer leading-4"
>
<a
class="min-w-0 flex-1 truncate text-cladd-fg hover:underline"
href="/subscriptions/show/{due.bill.id}"
>{due.bill.attributes.name}</a
>
<Money
{...due.pay}
approx={due.approx}
class="font-medium text-cladd-fg"
/>
{chosen(due)}
</div>
</li>
{/each}
</ul>
@@ -211,7 +229,7 @@
>Edit</ContextMenuItem
>
<ContextMenuSeparator />
<CardMenu billId={target.id} {cards} />
<CardMenu billIds={[target.id]} {cards} />
{/if}
</ContextMenuContent>
</ContextMenu>
@@ -225,10 +243,7 @@
>Annual renewals in {RENEWAL_DAYS} days:</span
>
<span class="font-semibold text-cladd-fg"
>{renewals.length},
{#each renewalTotals as total, i (total.code)}
{i > 0 ? " + " : ""}<Money {...total} />
{/each}</span
>{renewals.length}, <Sums inline parts={renewalTotals} /></span
>
{#each renewals as due (due.bill.id)}
<span class="text-cladd-fg-soft"