mirror of
https://github.com/whekin/household-bot.git
synced 2026-03-31 21:14:02 +00:00
refactor(miniapp): unify settings design with editable-list pattern
This commit is contained in:
@@ -1455,18 +1455,20 @@ a {
|
|||||||
gap: var(--spacing-md);
|
gap: var(--spacing-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ledger-actions {
|
/* ── Editable List (used in Ledger, Settings, etc.) ───── */
|
||||||
|
|
||||||
|
.editable-list-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
padding-bottom: var(--spacing-sm);
|
padding-bottom: var(--spacing-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ledger-list {
|
.editable-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ledger-entry {
|
.editable-list-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -1484,48 +1486,48 @@ a {
|
|||||||
transition: background var(--transition-fast);
|
transition: background var(--transition-fast);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ledger-entry:hover:not(:disabled) {
|
.editable-list-row:hover:not(:disabled) {
|
||||||
background: var(--bg-input);
|
background: var(--bg-input);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ledger-entry:disabled {
|
.editable-list-row:disabled {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ledger-entry:last-child {
|
.editable-list-row:last-child {
|
||||||
border-bottom: 0;
|
border-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ledger-entry__main {
|
.editable-list-row__main {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1px;
|
gap: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ledger-entry__title {
|
.editable-list-row__title {
|
||||||
font-size: var(--text-sm);
|
font-size: var(--text-sm);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ledger-entry__actor {
|
.editable-list-row__subtitle {
|
||||||
font-size: var(--text-xs);
|
font-size: var(--text-xs);
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ledger-entry__amounts {
|
.editable-list-row__meta {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
gap: 1px;
|
gap: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ledger-entry__amounts strong {
|
.editable-list-row__meta strong {
|
||||||
font-size: var(--text-sm);
|
font-size: var(--text-sm);
|
||||||
font-variant-numeric: tabular-nums;
|
font-variant-numeric: tabular-nums;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ledger-entry__secondary {
|
.editable-list-row__secondary {
|
||||||
font-size: var(--text-xs);
|
font-size: var(--text-xs);
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
font-variant-numeric: tabular-nums;
|
font-variant-numeric: tabular-nums;
|
||||||
@@ -1617,85 +1619,7 @@ a {
|
|||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Members ──────────────────────────────────────────── */
|
/* ── Settings Route ───────────────────────────────────── */
|
||||||
|
|
||||||
.members-list,
|
|
||||||
.pending-list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--spacing-sm);
|
|
||||||
}
|
|
||||||
|
|
||||||
.member-row {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.member-row.interactive {
|
|
||||||
margin: 0 calc(var(--spacing-lg) * -1);
|
|
||||||
padding: var(--spacing-sm) var(--spacing-lg);
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background var(--transition-fast);
|
|
||||||
}
|
|
||||||
|
|
||||||
.member-row.interactive:hover {
|
|
||||||
background: var(--bg-input);
|
|
||||||
}
|
|
||||||
|
|
||||||
.member-row__info {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--spacing-xs);
|
|
||||||
}
|
|
||||||
|
|
||||||
.member-row__info strong {
|
|
||||||
font-size: var(--text-sm);
|
|
||||||
}
|
|
||||||
|
|
||||||
.member-row__badges {
|
|
||||||
display: flex;
|
|
||||||
gap: var(--spacing-xs);
|
|
||||||
}
|
|
||||||
|
|
||||||
.member-row__weight {
|
|
||||||
font-size: var(--text-xs);
|
|
||||||
color: var(--text-muted);
|
|
||||||
}
|
|
||||||
|
|
||||||
.pending-member-row {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pending-member-row__handle {
|
|
||||||
font-size: var(--text-xs);
|
|
||||||
color: var(--text-muted);
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pending-member-actions {
|
|
||||||
display: flex;
|
|
||||||
gap: var(--spacing-sm);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Topics ───────────────────────────────────────────── */
|
|
||||||
|
|
||||||
.topics-list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--spacing-xs);
|
|
||||||
}
|
|
||||||
|
|
||||||
.topic-row {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: var(--spacing-xs) 0;
|
|
||||||
font-size: var(--text-sm);
|
|
||||||
color: var(--text-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Testing Card ─────────────────────────────────────── */
|
/* ── Testing Card ─────────────────────────────────────── */
|
||||||
|
|
||||||
|
|||||||
@@ -571,7 +571,7 @@ export default function LedgerRoute() {
|
|||||||
defaultOpen
|
defaultOpen
|
||||||
>
|
>
|
||||||
<Show when={effectiveIsAdmin()}>
|
<Show when={effectiveIsAdmin()}>
|
||||||
<div class="ledger-actions">
|
<div class="editable-list-actions">
|
||||||
<Button
|
<Button
|
||||||
variant="primary"
|
variant="primary"
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -603,23 +603,25 @@ export default function LedgerRoute() {
|
|||||||
when={purchaseLedger().length > 0}
|
when={purchaseLedger().length > 0}
|
||||||
fallback={<p class="empty-state">{copy().purchasesEmpty}</p>}
|
fallback={<p class="empty-state">{copy().purchasesEmpty}</p>}
|
||||||
>
|
>
|
||||||
<div class="ledger-list">
|
<div class="editable-list">
|
||||||
<For each={purchaseLedger()}>
|
<For each={purchaseLedger()}>
|
||||||
{(entry) => (
|
{(entry) => (
|
||||||
<button
|
<button
|
||||||
class="ledger-entry"
|
class="editable-list-row"
|
||||||
onClick={() => effectiveIsAdmin() && openPurchaseEditor(entry)}
|
onClick={() => effectiveIsAdmin() && openPurchaseEditor(entry)}
|
||||||
disabled={!effectiveIsAdmin()}
|
disabled={!effectiveIsAdmin()}
|
||||||
>
|
>
|
||||||
<div class="ledger-entry__main">
|
<div class="editable-list-row__main">
|
||||||
<span class="ledger-entry__title">{entry.title}</span>
|
<span class="editable-list-row__title">{entry.title}</span>
|
||||||
<span class="ledger-entry__actor">{entry.actorDisplayName}</span>
|
<span class="editable-list-row__subtitle">
|
||||||
|
{entry.actorDisplayName}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="ledger-entry__amounts">
|
<div class="editable-list-row__meta">
|
||||||
<strong>{ledgerPrimaryAmount(entry)}</strong>
|
<strong>{ledgerPrimaryAmount(entry)}</strong>
|
||||||
<Show when={ledgerSecondaryAmount(entry)}>
|
<Show when={ledgerSecondaryAmount(entry)}>
|
||||||
{(secondary) => (
|
{(secondary) => (
|
||||||
<span class="ledger-entry__secondary">{secondary()}</span>
|
<span class="editable-list-row__secondary">{secondary()}</span>
|
||||||
)}
|
)}
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
@@ -633,7 +635,7 @@ export default function LedgerRoute() {
|
|||||||
{/* ── Utility bills ──────────────────────── */}
|
{/* ── Utility bills ──────────────────────── */}
|
||||||
<Collapsible title={copy().utilityLedgerTitle}>
|
<Collapsible title={copy().utilityLedgerTitle}>
|
||||||
<Show when={effectiveIsAdmin()}>
|
<Show when={effectiveIsAdmin()}>
|
||||||
<div class="ledger-actions">
|
<div class="editable-list-actions">
|
||||||
<Button variant="primary" size="sm" onClick={() => setAddUtilityOpen(true)}>
|
<Button variant="primary" size="sm" onClick={() => setAddUtilityOpen(true)}>
|
||||||
<Plus size={14} />
|
<Plus size={14} />
|
||||||
{copy().addUtilityBillAction}
|
{copy().addUtilityBillAction}
|
||||||
@@ -644,19 +646,21 @@ export default function LedgerRoute() {
|
|||||||
when={utilityLedger().length > 0}
|
when={utilityLedger().length > 0}
|
||||||
fallback={<p class="empty-state">{copy().utilityLedgerEmpty}</p>}
|
fallback={<p class="empty-state">{copy().utilityLedgerEmpty}</p>}
|
||||||
>
|
>
|
||||||
<div class="ledger-list">
|
<div class="editable-list">
|
||||||
<For each={utilityLedger()}>
|
<For each={utilityLedger()}>
|
||||||
{(entry) => (
|
{(entry) => (
|
||||||
<button
|
<button
|
||||||
class="ledger-entry"
|
class="editable-list-row"
|
||||||
onClick={() => effectiveIsAdmin() && openUtilityEditor(entry)}
|
onClick={() => effectiveIsAdmin() && openUtilityEditor(entry)}
|
||||||
disabled={!effectiveIsAdmin()}
|
disabled={!effectiveIsAdmin()}
|
||||||
>
|
>
|
||||||
<div class="ledger-entry__main">
|
<div class="editable-list-row__main">
|
||||||
<span class="ledger-entry__title">{entry.title}</span>
|
<span class="editable-list-row__title">{entry.title}</span>
|
||||||
<span class="ledger-entry__actor">{entry.actorDisplayName}</span>
|
<span class="editable-list-row__subtitle">
|
||||||
|
{entry.actorDisplayName}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="ledger-entry__amounts">
|
<div class="editable-list-row__meta">
|
||||||
<strong>{ledgerPrimaryAmount(entry)}</strong>
|
<strong>{ledgerPrimaryAmount(entry)}</strong>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
@@ -674,7 +678,7 @@ export default function LedgerRoute() {
|
|||||||
: {})}
|
: {})}
|
||||||
>
|
>
|
||||||
<Show when={effectiveIsAdmin()}>
|
<Show when={effectiveIsAdmin()}>
|
||||||
<div class="ledger-actions">
|
<div class="editable-list-actions">
|
||||||
<Button variant="primary" size="sm" onClick={() => setAddPaymentOpen(true)}>
|
<Button variant="primary" size="sm" onClick={() => setAddPaymentOpen(true)}>
|
||||||
<Plus size={14} />
|
<Plus size={14} />
|
||||||
{copy().paymentsAddAction}
|
{copy().paymentsAddAction}
|
||||||
@@ -685,23 +689,25 @@ export default function LedgerRoute() {
|
|||||||
when={paymentLedger().length > 0}
|
when={paymentLedger().length > 0}
|
||||||
fallback={<p class="empty-state">{copy().paymentsEmpty}</p>}
|
fallback={<p class="empty-state">{copy().paymentsEmpty}</p>}
|
||||||
>
|
>
|
||||||
<div class="ledger-list">
|
<div class="editable-list">
|
||||||
<For each={paymentLedger()}>
|
<For each={paymentLedger()}>
|
||||||
{(entry) => (
|
{(entry) => (
|
||||||
<button
|
<button
|
||||||
class="ledger-entry"
|
class="editable-list-row"
|
||||||
onClick={() => effectiveIsAdmin() && openPaymentEditor(entry)}
|
onClick={() => effectiveIsAdmin() && openPaymentEditor(entry)}
|
||||||
disabled={!effectiveIsAdmin()}
|
disabled={!effectiveIsAdmin()}
|
||||||
>
|
>
|
||||||
<div class="ledger-entry__main">
|
<div class="editable-list-row__main">
|
||||||
<span class="ledger-entry__title">
|
<span class="editable-list-row__title">
|
||||||
{entry.paymentKind === 'rent'
|
{entry.paymentKind === 'rent'
|
||||||
? copy().paymentLedgerRent
|
? copy().paymentLedgerRent
|
||||||
: copy().paymentLedgerUtilities}
|
: copy().paymentLedgerUtilities}
|
||||||
</span>
|
</span>
|
||||||
<span class="ledger-entry__actor">{entry.actorDisplayName}</span>
|
<span class="editable-list-row__subtitle">
|
||||||
|
{entry.actorDisplayName}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="ledger-entry__amounts">
|
<div class="editable-list-row__meta">
|
||||||
<strong>{ledgerPrimaryAmount(entry)}</strong>
|
<strong>{ledgerPrimaryAmount(entry)}</strong>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Show, For, createSignal } from 'solid-js'
|
import { Show, For, createSignal } from 'solid-js'
|
||||||
import { ArrowLeft, Globe, User } from 'lucide-solid'
|
import { ArrowLeft, Globe, Plus, User } from 'lucide-solid'
|
||||||
import { useNavigate } from '@solidjs/router'
|
import { useNavigate } from '@solidjs/router'
|
||||||
|
|
||||||
import { useSession } from '../contexts/session-context'
|
import { useSession } from '../contexts/session-context'
|
||||||
@@ -366,6 +366,42 @@ export default function SettingsRoute() {
|
|||||||
</Card>
|
</Card>
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
|
|
||||||
|
{/* Utility Categories */}
|
||||||
|
<Collapsible title={copy().utilityCategoriesTitle} body={copy().utilityCategoriesBody}>
|
||||||
|
<div class="editable-list-actions">
|
||||||
|
<Button variant="primary" size="sm" onClick={() => openAddCategory()}>
|
||||||
|
<Plus size={14} />
|
||||||
|
{copy().addCategoryAction}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<Show
|
||||||
|
when={adminSettings()?.categories}
|
||||||
|
fallback={<p class="empty-state">{copy().utilityCategoriesBody}</p>}
|
||||||
|
>
|
||||||
|
{(categories) => (
|
||||||
|
<Show
|
||||||
|
when={categories().length > 0}
|
||||||
|
fallback={<p class="empty-state">{copy().utilityCategoriesBody}</p>}
|
||||||
|
>
|
||||||
|
<div class="editable-list">
|
||||||
|
<For each={categories()}>
|
||||||
|
{(category) => (
|
||||||
|
<button class="editable-list-row" onClick={() => openEditCategory(category)}>
|
||||||
|
<div class="editable-list-row__main">
|
||||||
|
<span class="editable-list-row__title">{category.name}</span>
|
||||||
|
</div>
|
||||||
|
<Badge variant={category.isActive ? 'accent' : 'muted'}>
|
||||||
|
{category.isActive ? copy().onLabel : copy().offLabel}
|
||||||
|
</Badge>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
)}
|
||||||
|
</Show>
|
||||||
|
</Collapsible>
|
||||||
|
|
||||||
{/* Billing cycle */}
|
{/* Billing cycle */}
|
||||||
<Collapsible title={copy().billingCycleTitle}>
|
<Collapsible title={copy().billingCycleTitle}>
|
||||||
<Card>
|
<Card>
|
||||||
@@ -395,20 +431,19 @@ export default function SettingsRoute() {
|
|||||||
when={pendingMembers().length > 0}
|
when={pendingMembers().length > 0}
|
||||||
fallback={<p class="empty-state">{copy().pendingMembersEmpty}</p>}
|
fallback={<p class="empty-state">{copy().pendingMembersEmpty}</p>}
|
||||||
>
|
>
|
||||||
<div class="pending-list">
|
<div class="editable-list">
|
||||||
<For each={pendingMembers()}>
|
<For each={pendingMembers()}>
|
||||||
{(member) => (
|
{(member) => (
|
||||||
<Card>
|
<div class="editable-list-row">
|
||||||
<div class="pending-member-row">
|
<div class="editable-list-row__main">
|
||||||
<div>
|
<span class="editable-list-row__title">{member.displayName}</span>
|
||||||
<strong>{member.displayName}</strong>
|
|
||||||
<Show when={member.username}>
|
<Show when={member.username}>
|
||||||
{(username) => (
|
{(username) => (
|
||||||
<span class="pending-member-row__handle">@{username()}</span>
|
<span class="editable-list-row__subtitle">@{username()}</span>
|
||||||
)}
|
)}
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
<div class="pending-member-actions">
|
<div class="editable-list-row__meta">
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -433,7 +468,6 @@ export default function SettingsRoute() {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
</div>
|
</div>
|
||||||
@@ -444,18 +478,17 @@ export default function SettingsRoute() {
|
|||||||
<Collapsible title={copy().houseSectionMembers} body={copy().membersBody}>
|
<Collapsible title={copy().houseSectionMembers} body={copy().membersBody}>
|
||||||
<Show when={adminSettings()?.members}>
|
<Show when={adminSettings()?.members}>
|
||||||
{(members) => (
|
{(members) => (
|
||||||
<div class="members-list">
|
<div class="editable-list">
|
||||||
<For each={members()}>
|
<For each={members()}>
|
||||||
{(member) => (
|
{(member) => (
|
||||||
<Card>
|
<button class="editable-list-row" onClick={() => openEditMember(member)}>
|
||||||
<div
|
<div class="editable-list-row__main">
|
||||||
class="member-row interactive"
|
<span class="editable-list-row__title">{member.displayName}</span>
|
||||||
style={{ cursor: 'pointer' }}
|
<span class="editable-list-row__subtitle">
|
||||||
onClick={() => openEditMember(member)}
|
{copy().rentWeightLabel}: {member.rentShareWeight}
|
||||||
>
|
</span>
|
||||||
<div class="member-row__info">
|
</div>
|
||||||
<strong>{member.displayName}</strong>
|
<div class="editable-list-row__meta">
|
||||||
<div class="member-row__badges">
|
|
||||||
<Badge variant={member.isAdmin ? 'accent' : 'muted'}>
|
<Badge variant={member.isAdmin ? 'accent' : 'muted'}>
|
||||||
{member.isAdmin ? copy().adminTag : copy().residentTag}
|
{member.isAdmin ? copy().adminTag : copy().residentTag}
|
||||||
</Badge>
|
</Badge>
|
||||||
@@ -467,14 +500,7 @@ export default function SettingsRoute() {
|
|||||||
: copy().memberStatusLeft}
|
: copy().memberStatusLeft}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</button>
|
||||||
<div class="member-row__weight">
|
|
||||||
<span>
|
|
||||||
{copy().rentWeightLabel}: {member.rentShareWeight}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
</div>
|
</div>
|
||||||
@@ -486,7 +512,7 @@ export default function SettingsRoute() {
|
|||||||
<Collapsible title={copy().houseSectionTopics} body={copy().topicBindingsBody}>
|
<Collapsible title={copy().houseSectionTopics} body={copy().topicBindingsBody}>
|
||||||
<Show when={adminSettings()?.topics}>
|
<Show when={adminSettings()?.topics}>
|
||||||
{(topics) => (
|
{(topics) => (
|
||||||
<div class="topics-list">
|
<div class="editable-list">
|
||||||
<For each={topics()}>
|
<For each={topics()}>
|
||||||
{(topic) => {
|
{(topic) => {
|
||||||
const roleLabel = () => {
|
const roleLabel = () => {
|
||||||
@@ -500,12 +526,16 @@ export default function SettingsRoute() {
|
|||||||
return labels[topic.role] ?? topic.role
|
return labels[topic.role] ?? topic.role
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div class="topic-row">
|
<div class="editable-list-row">
|
||||||
<span>{roleLabel()}</span>
|
<div class="editable-list-row__main">
|
||||||
|
<span class="editable-list-row__title">{roleLabel()}</span>
|
||||||
|
</div>
|
||||||
|
<div class="editable-list-row__meta">
|
||||||
<Badge variant={topic.telegramThreadId ? 'accent' : 'muted'}>
|
<Badge variant={topic.telegramThreadId ? 'accent' : 'muted'}>
|
||||||
{topic.telegramThreadId ? copy().topicBound : copy().topicUnbound}
|
{topic.telegramThreadId ? copy().topicBound : copy().topicUnbound}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
</For>
|
</For>
|
||||||
@@ -513,40 +543,6 @@ export default function SettingsRoute() {
|
|||||||
)}
|
)}
|
||||||
</Show>
|
</Show>
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
|
|
||||||
{/* Utility Categories */}
|
|
||||||
<Collapsible title={copy().utilityCategoriesTitle} body={copy().utilityCategoriesBody}>
|
|
||||||
<Show
|
|
||||||
when={adminSettings()?.categories}
|
|
||||||
fallback={<p class="empty-state">{copy().utilityCategoriesBody}</p>}
|
|
||||||
>
|
|
||||||
{(categories) => (
|
|
||||||
<div class="categories-list">
|
|
||||||
<For each={categories()}>
|
|
||||||
{(category) => (
|
|
||||||
<Card>
|
|
||||||
<div
|
|
||||||
class="category-row interactive"
|
|
||||||
style={{ cursor: 'pointer' }}
|
|
||||||
onClick={() => openEditCategory(category)}
|
|
||||||
>
|
|
||||||
<div class="category-row__info">
|
|
||||||
<strong>{category.name}</strong>
|
|
||||||
<Badge variant={category.isActive ? 'accent' : 'muted'}>
|
|
||||||
{category.isActive ? copy().onLabel : copy().offLabel}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
)}
|
|
||||||
</For>
|
|
||||||
<Button variant="secondary" size="sm" onClick={() => openAddCategory()}>
|
|
||||||
{copy().addCategoryAction}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Show>
|
|
||||||
</Collapsible>
|
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
{/* ── Billing Settings Editor Modal ────────────── */}
|
{/* ── Billing Settings Editor Modal ────────────── */}
|
||||||
|
|||||||
Reference in New Issue
Block a user