mirror of
https://github.com/whekin/household-bot.git
synced 2026-03-31 12:04:02 +00:00
fix(miniapp): make dashboard screens react to initial load
This commit is contained in:
@@ -39,129 +39,132 @@ type Props = {
|
||||
}
|
||||
|
||||
export function BalancesScreen(props: Props) {
|
||||
if (!props.dashboard) {
|
||||
return (
|
||||
<div class="balance-list">
|
||||
<p>{props.copy.emptyDashboard ?? ''}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="balance-list">
|
||||
<Show when={props.currentMemberLine}>
|
||||
{(member) => (
|
||||
<article class="balance-item balance-item--accent">
|
||||
<header>
|
||||
<strong>{props.copy.yourBalanceTitle ?? ''}</strong>
|
||||
<span>
|
||||
{member().netDueMajor} {props.dashboard!.currency}
|
||||
</span>
|
||||
</header>
|
||||
<p>{props.copy.yourBalanceBody ?? ''}</p>
|
||||
<div class="balance-breakdown">
|
||||
<article class="stat-card">
|
||||
<span>{props.copy.baseDue ?? ''}</span>
|
||||
<strong>
|
||||
{props.memberBaseDueMajor(member())} {props.dashboard!.currency}
|
||||
</strong>
|
||||
<Show
|
||||
when={props.dashboard}
|
||||
fallback={
|
||||
<div class="balance-list">
|
||||
<p>{props.copy.emptyDashboard ?? ''}</p>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{(dashboard) => (
|
||||
<div class="balance-list">
|
||||
<Show when={props.currentMemberLine}>
|
||||
{(member) => (
|
||||
<article class="balance-item balance-item--accent">
|
||||
<header>
|
||||
<strong>{props.copy.yourBalanceTitle ?? ''}</strong>
|
||||
<span>
|
||||
{member().netDueMajor} {dashboard().currency}
|
||||
</span>
|
||||
</header>
|
||||
<p>{props.copy.yourBalanceBody ?? ''}</p>
|
||||
<div class="balance-breakdown">
|
||||
<article class="stat-card">
|
||||
<span>{props.copy.baseDue ?? ''}</span>
|
||||
<strong>
|
||||
{props.memberBaseDueMajor(member())} {dashboard().currency}
|
||||
</strong>
|
||||
</article>
|
||||
<article class="stat-card">
|
||||
<span>{props.copy.shareOffset ?? ''}</span>
|
||||
<strong>
|
||||
{member().purchaseOffsetMajor} {dashboard().currency}
|
||||
</strong>
|
||||
</article>
|
||||
<article class="stat-card">
|
||||
<span>{props.copy.finalDue ?? ''}</span>
|
||||
<strong>
|
||||
{member().netDueMajor} {dashboard().currency}
|
||||
</strong>
|
||||
</article>
|
||||
<article class="stat-card">
|
||||
<span>{props.copy.paidLabel ?? ''}</span>
|
||||
<strong>
|
||||
{member().paidMajor} {dashboard().currency}
|
||||
</strong>
|
||||
</article>
|
||||
<article class="stat-card">
|
||||
<span>{props.copy.remainingLabel ?? ''}</span>
|
||||
<strong>
|
||||
{member().remainingMajor} {dashboard().currency}
|
||||
</strong>
|
||||
</article>
|
||||
</div>
|
||||
</article>
|
||||
<article class="stat-card">
|
||||
<span>{props.copy.shareOffset ?? ''}</span>
|
||||
<strong>
|
||||
{member().purchaseOffsetMajor} {props.dashboard!.currency}
|
||||
</strong>
|
||||
</article>
|
||||
<article class="stat-card">
|
||||
<span>{props.copy.finalDue ?? ''}</span>
|
||||
<strong>
|
||||
{member().netDueMajor} {props.dashboard!.currency}
|
||||
</strong>
|
||||
</article>
|
||||
<article class="stat-card">
|
||||
<span>{props.copy.paidLabel ?? ''}</span>
|
||||
<strong>
|
||||
{member().paidMajor} {props.dashboard!.currency}
|
||||
</strong>
|
||||
</article>
|
||||
<article class="stat-card">
|
||||
<span>{props.copy.remainingLabel ?? ''}</span>
|
||||
<strong>
|
||||
{member().remainingMajor} {props.dashboard!.currency}
|
||||
</strong>
|
||||
</article>
|
||||
</div>
|
||||
</article>
|
||||
)}
|
||||
</Show>
|
||||
<div class="summary-card-grid">
|
||||
<FinanceSummaryCards
|
||||
dashboard={props.dashboard}
|
||||
utilityTotalMajor={props.utilityTotalMajor}
|
||||
purchaseTotalMajor={props.purchaseTotalMajor}
|
||||
labels={{
|
||||
remaining: props.copy.remainingLabel ?? '',
|
||||
rent: props.copy.shareRent ?? '',
|
||||
utilities: props.copy.shareUtilities ?? '',
|
||||
purchases: props.copy.purchasesTitle ?? ''
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<FinanceVisuals
|
||||
dashboard={props.dashboard}
|
||||
memberVisuals={props.memberBalanceVisuals}
|
||||
purchaseChart={props.purchaseChart}
|
||||
remainingClass={props.memberRemainingClass}
|
||||
labels={{
|
||||
financeVisualsTitle: props.copy.financeVisualsTitle ?? '',
|
||||
financeVisualsBody: props.copy.financeVisualsBody ?? '',
|
||||
membersCount: props.copy.membersCount ?? '',
|
||||
purchaseInvestmentsTitle: props.copy.purchaseInvestmentsTitle ?? '',
|
||||
purchaseInvestmentsBody: props.copy.purchaseInvestmentsBody ?? '',
|
||||
purchaseInvestmentsEmpty: props.copy.purchaseInvestmentsEmpty ?? '',
|
||||
purchaseTotalLabel: props.copy.purchaseTotalLabel ?? '',
|
||||
purchaseShareLabel: props.copy.purchaseShareLabel ?? ''
|
||||
}}
|
||||
/>
|
||||
<article class="balance-item">
|
||||
<header>
|
||||
<strong>{props.copy.householdBalancesTitle ?? ''}</strong>
|
||||
</header>
|
||||
<p>{props.copy.householdBalancesBody ?? ''}</p>
|
||||
</article>
|
||||
<For each={props.dashboard.members}>
|
||||
{(member) => (
|
||||
)}
|
||||
</Show>
|
||||
<div class="summary-card-grid">
|
||||
<FinanceSummaryCards
|
||||
dashboard={dashboard()}
|
||||
utilityTotalMajor={props.utilityTotalMajor}
|
||||
purchaseTotalMajor={props.purchaseTotalMajor}
|
||||
labels={{
|
||||
remaining: props.copy.remainingLabel ?? '',
|
||||
rent: props.copy.shareRent ?? '',
|
||||
utilities: props.copy.shareUtilities ?? '',
|
||||
purchases: props.copy.purchasesTitle ?? ''
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<FinanceVisuals
|
||||
dashboard={dashboard()}
|
||||
memberVisuals={props.memberBalanceVisuals}
|
||||
purchaseChart={props.purchaseChart}
|
||||
remainingClass={props.memberRemainingClass}
|
||||
labels={{
|
||||
financeVisualsTitle: props.copy.financeVisualsTitle ?? '',
|
||||
financeVisualsBody: props.copy.financeVisualsBody ?? '',
|
||||
membersCount: props.copy.membersCount ?? '',
|
||||
purchaseInvestmentsTitle: props.copy.purchaseInvestmentsTitle ?? '',
|
||||
purchaseInvestmentsBody: props.copy.purchaseInvestmentsBody ?? '',
|
||||
purchaseInvestmentsEmpty: props.copy.purchaseInvestmentsEmpty ?? '',
|
||||
purchaseTotalLabel: props.copy.purchaseTotalLabel ?? '',
|
||||
purchaseShareLabel: props.copy.purchaseShareLabel ?? ''
|
||||
}}
|
||||
/>
|
||||
<article class="balance-item">
|
||||
<header>
|
||||
<strong>{member.displayName}</strong>
|
||||
<span>
|
||||
{member.remainingMajor} {props.dashboard!.currency}
|
||||
</span>
|
||||
<strong>{props.copy.householdBalancesTitle ?? ''}</strong>
|
||||
</header>
|
||||
<p>
|
||||
{props.copy.baseDue ?? ''}: {props.memberBaseDueMajor(member)}{' '}
|
||||
{props.dashboard!.currency}
|
||||
</p>
|
||||
<p>
|
||||
{props.copy.shareRent ?? ''}: {member.rentShareMajor} {props.dashboard!.currency}
|
||||
</p>
|
||||
<p>
|
||||
{props.copy.shareUtilities ?? ''}: {member.utilityShareMajor}{' '}
|
||||
{props.dashboard!.currency}
|
||||
</p>
|
||||
<p>
|
||||
{props.copy.shareOffset ?? ''}: {member.purchaseOffsetMajor}{' '}
|
||||
{props.dashboard!.currency}
|
||||
</p>
|
||||
<p>
|
||||
{props.copy.paidLabel ?? ''}: {member.paidMajor} {props.dashboard!.currency}
|
||||
</p>
|
||||
<p class={`balance-status ${props.memberRemainingClass(member)}`}>
|
||||
{props.copy.remainingLabel ?? ''}: {member.remainingMajor} {props.dashboard!.currency}
|
||||
</p>
|
||||
<p>{props.copy.householdBalancesBody ?? ''}</p>
|
||||
</article>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
<For each={dashboard().members}>
|
||||
{(member) => (
|
||||
<article class="balance-item">
|
||||
<header>
|
||||
<strong>{member.displayName}</strong>
|
||||
<span>
|
||||
{member.remainingMajor} {dashboard().currency}
|
||||
</span>
|
||||
</header>
|
||||
<p>
|
||||
{props.copy.baseDue ?? ''}: {props.memberBaseDueMajor(member)}{' '}
|
||||
{dashboard().currency}
|
||||
</p>
|
||||
<p>
|
||||
{props.copy.shareRent ?? ''}: {member.rentShareMajor} {dashboard().currency}
|
||||
</p>
|
||||
<p>
|
||||
{props.copy.shareUtilities ?? ''}: {member.utilityShareMajor}{' '}
|
||||
{dashboard().currency}
|
||||
</p>
|
||||
<p>
|
||||
{props.copy.shareOffset ?? ''}: {member.purchaseOffsetMajor}{' '}
|
||||
{dashboard().currency}
|
||||
</p>
|
||||
<p>
|
||||
{props.copy.paidLabel ?? ''}: {member.paidMajor} {dashboard().currency}
|
||||
</p>
|
||||
<p class={`balance-status ${props.memberRemainingClass(member)}`}>
|
||||
{props.copy.remainingLabel ?? ''}: {member.remainingMajor} {dashboard().currency}
|
||||
</p>
|
||||
</article>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
)}
|
||||
</Show>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -18,130 +18,133 @@ type Props = {
|
||||
}
|
||||
|
||||
export function HomeScreen(props: Props) {
|
||||
if (!props.dashboard) {
|
||||
return (
|
||||
<div class="home-grid">
|
||||
<div class="summary-card-grid">
|
||||
<article class="stat-card">
|
||||
<span>{props.copy.remainingLabel ?? ''}</span>
|
||||
<strong>—</strong>
|
||||
</article>
|
||||
<article class="stat-card">
|
||||
<span>{props.copy.shareRent ?? ''}</span>
|
||||
<strong>—</strong>
|
||||
</article>
|
||||
<article class="stat-card">
|
||||
<span>{props.copy.shareUtilities ?? ''}</span>
|
||||
<strong>—</strong>
|
||||
</article>
|
||||
<article class="stat-card">
|
||||
<span>{props.copy.purchasesTitle ?? ''}</span>
|
||||
<strong>—</strong>
|
||||
return (
|
||||
<Show
|
||||
when={props.dashboard}
|
||||
fallback={
|
||||
<div class="home-grid">
|
||||
<div class="summary-card-grid">
|
||||
<article class="stat-card">
|
||||
<span>{props.copy.remainingLabel ?? ''}</span>
|
||||
<strong>—</strong>
|
||||
</article>
|
||||
<article class="stat-card">
|
||||
<span>{props.copy.shareRent ?? ''}</span>
|
||||
<strong>—</strong>
|
||||
</article>
|
||||
<article class="stat-card">
|
||||
<span>{props.copy.shareUtilities ?? ''}</span>
|
||||
<strong>—</strong>
|
||||
</article>
|
||||
<article class="stat-card">
|
||||
<span>{props.copy.purchasesTitle ?? ''}</span>
|
||||
<strong>—</strong>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{(dashboard) => (
|
||||
<div class="home-grid">
|
||||
<div class="summary-card-grid">
|
||||
<FinanceSummaryCards
|
||||
dashboard={dashboard()}
|
||||
utilityTotalMajor={props.utilityTotalMajor}
|
||||
purchaseTotalMajor={props.purchaseTotalMajor}
|
||||
labels={{
|
||||
remaining: props.copy.remainingLabel ?? '',
|
||||
rent: props.copy.shareRent ?? '',
|
||||
utilities: props.copy.shareUtilities ?? '',
|
||||
purchases: props.copy.purchasesTitle ?? ''
|
||||
}}
|
||||
/>
|
||||
<Show when={props.readyIsAdmin}>
|
||||
<article class="stat-card">
|
||||
<span>{props.copy.pendingRequests ?? ''}</span>
|
||||
<strong>{String(props.pendingMembersCount)}</strong>
|
||||
</article>
|
||||
</Show>
|
||||
</div>
|
||||
|
||||
<Show when={props.currentMemberLine}>
|
||||
{(member) => (
|
||||
<article class="balance-item balance-item--accent">
|
||||
<header>
|
||||
<strong>{props.copy.yourBalanceTitle ?? ''}</strong>
|
||||
<span>
|
||||
{member().remainingMajor} {dashboard().currency}
|
||||
</span>
|
||||
</header>
|
||||
<p>
|
||||
{props.copy.shareRent ?? ''}: {dashboard().rentSourceAmountMajor}{' '}
|
||||
{dashboard().rentSourceCurrency}
|
||||
{dashboard().rentSourceCurrency !== dashboard().currency
|
||||
? ` -> ${dashboard().rentDisplayAmountMajor} ${dashboard().currency}`
|
||||
: ''}
|
||||
</p>
|
||||
<div class="balance-breakdown">
|
||||
<article class="stat-card">
|
||||
<span>{props.copy.baseDue ?? ''}</span>
|
||||
<strong>
|
||||
{props.memberBaseDueMajor(member())} {dashboard().currency}
|
||||
</strong>
|
||||
</article>
|
||||
<article class="stat-card">
|
||||
<span>{props.copy.shareOffset ?? ''}</span>
|
||||
<strong>
|
||||
{member().purchaseOffsetMajor} {dashboard().currency}
|
||||
</strong>
|
||||
</article>
|
||||
<article class="stat-card">
|
||||
<span>{props.copy.finalDue ?? ''}</span>
|
||||
<strong>
|
||||
{member().netDueMajor} {dashboard().currency}
|
||||
</strong>
|
||||
</article>
|
||||
<article class="stat-card">
|
||||
<span>{props.copy.paidLabel ?? ''}</span>
|
||||
<strong>
|
||||
{member().paidMajor} {dashboard().currency}
|
||||
</strong>
|
||||
</article>
|
||||
<article class="stat-card">
|
||||
<span>{props.copy.remainingLabel ?? ''}</span>
|
||||
<strong>
|
||||
{member().remainingMajor} {dashboard().currency}
|
||||
</strong>
|
||||
</article>
|
||||
</div>
|
||||
</article>
|
||||
)}
|
||||
</Show>
|
||||
|
||||
<article class="balance-item balance-item--wide">
|
||||
<header>
|
||||
<strong>{props.copy.latestActivityTitle ?? ''}</strong>
|
||||
</header>
|
||||
{dashboard().ledger.length === 0 ? (
|
||||
<p>{props.copy.latestActivityEmpty ?? ''}</p>
|
||||
) : (
|
||||
<div class="activity-list">
|
||||
<For each={dashboard().ledger.slice(0, 3)}>
|
||||
{(entry) => (
|
||||
<article class="activity-row">
|
||||
<header>
|
||||
<strong>{props.ledgerTitle(entry)}</strong>
|
||||
<span>{props.ledgerPrimaryAmount(entry)}</span>
|
||||
</header>
|
||||
<Show when={props.ledgerSecondaryAmount(entry)}>
|
||||
{(secondary) => <p>{secondary()}</p>}
|
||||
</Show>
|
||||
<p>{entry.actorDisplayName ?? props.copy.ledgerActorFallback ?? ''}</p>
|
||||
</article>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
)}
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="home-grid">
|
||||
<div class="summary-card-grid">
|
||||
<FinanceSummaryCards
|
||||
dashboard={props.dashboard}
|
||||
utilityTotalMajor={props.utilityTotalMajor}
|
||||
purchaseTotalMajor={props.purchaseTotalMajor}
|
||||
labels={{
|
||||
remaining: props.copy.remainingLabel ?? '',
|
||||
rent: props.copy.shareRent ?? '',
|
||||
utilities: props.copy.shareUtilities ?? '',
|
||||
purchases: props.copy.purchasesTitle ?? ''
|
||||
}}
|
||||
/>
|
||||
<Show when={props.readyIsAdmin}>
|
||||
<article class="stat-card">
|
||||
<span>{props.copy.pendingRequests ?? ''}</span>
|
||||
<strong>{String(props.pendingMembersCount)}</strong>
|
||||
</article>
|
||||
</Show>
|
||||
</div>
|
||||
|
||||
<Show when={props.currentMemberLine}>
|
||||
{(member) => (
|
||||
<article class="balance-item balance-item--accent">
|
||||
<header>
|
||||
<strong>{props.copy.yourBalanceTitle ?? ''}</strong>
|
||||
<span>
|
||||
{member().remainingMajor} {props.dashboard!.currency}
|
||||
</span>
|
||||
</header>
|
||||
<p>
|
||||
{props.copy.shareRent ?? ''}: {props.dashboard!.rentSourceAmountMajor}{' '}
|
||||
{props.dashboard!.rentSourceCurrency}
|
||||
{props.dashboard!.rentSourceCurrency !== props.dashboard!.currency
|
||||
? ` -> ${props.dashboard!.rentDisplayAmountMajor} ${props.dashboard!.currency}`
|
||||
: ''}
|
||||
</p>
|
||||
<div class="balance-breakdown">
|
||||
<article class="stat-card">
|
||||
<span>{props.copy.baseDue ?? ''}</span>
|
||||
<strong>
|
||||
{props.memberBaseDueMajor(member())} {props.dashboard!.currency}
|
||||
</strong>
|
||||
</article>
|
||||
<article class="stat-card">
|
||||
<span>{props.copy.shareOffset ?? ''}</span>
|
||||
<strong>
|
||||
{member().purchaseOffsetMajor} {props.dashboard!.currency}
|
||||
</strong>
|
||||
</article>
|
||||
<article class="stat-card">
|
||||
<span>{props.copy.finalDue ?? ''}</span>
|
||||
<strong>
|
||||
{member().netDueMajor} {props.dashboard!.currency}
|
||||
</strong>
|
||||
</article>
|
||||
<article class="stat-card">
|
||||
<span>{props.copy.paidLabel ?? ''}</span>
|
||||
<strong>
|
||||
{member().paidMajor} {props.dashboard!.currency}
|
||||
</strong>
|
||||
</article>
|
||||
<article class="stat-card">
|
||||
<span>{props.copy.remainingLabel ?? ''}</span>
|
||||
<strong>
|
||||
{member().remainingMajor} {props.dashboard!.currency}
|
||||
</strong>
|
||||
</article>
|
||||
</div>
|
||||
</article>
|
||||
)}
|
||||
</Show>
|
||||
|
||||
<article class="balance-item balance-item--wide">
|
||||
<header>
|
||||
<strong>{props.copy.latestActivityTitle ?? ''}</strong>
|
||||
</header>
|
||||
{props.dashboard.ledger.length === 0 ? (
|
||||
<p>{props.copy.latestActivityEmpty ?? ''}</p>
|
||||
) : (
|
||||
<div class="activity-list">
|
||||
<For each={props.dashboard.ledger.slice(0, 3)}>
|
||||
{(entry) => (
|
||||
<article class="activity-row">
|
||||
<header>
|
||||
<strong>{props.ledgerTitle(entry)}</strong>
|
||||
<span>{props.ledgerPrimaryAmount(entry)}</span>
|
||||
</header>
|
||||
<Show when={props.ledgerSecondaryAmount(entry)}>
|
||||
{(secondary) => <p>{secondary()}</p>}
|
||||
</Show>
|
||||
<p>{entry.actorDisplayName ?? props.copy.ledgerActorFallback ?? ''}</p>
|
||||
</article>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
)}
|
||||
</article>
|
||||
</div>
|
||||
)}
|
||||
</Show>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -118,130 +118,467 @@ type Props = {
|
||||
}
|
||||
|
||||
export function LedgerScreen(props: Props) {
|
||||
if (!props.dashboard) {
|
||||
return (
|
||||
<div class="ledger-list">
|
||||
<p>{props.copy.emptyDashboard ?? ''}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="ledger-list">
|
||||
<article class="balance-item">
|
||||
<header>
|
||||
<strong>
|
||||
{props.readyIsAdmin ? props.copy.purchaseReviewTitle : props.copy.purchasesTitle}
|
||||
</strong>
|
||||
</header>
|
||||
<Show when={props.readyIsAdmin}>
|
||||
<p>{props.copy.purchaseReviewBody ?? ''}</p>
|
||||
</Show>
|
||||
{props.purchaseEntries.length === 0 ? (
|
||||
<p>{props.copy.purchasesEmpty ?? ''}</p>
|
||||
) : (
|
||||
<div class="ledger-list">
|
||||
<For each={props.purchaseEntries}>
|
||||
{(entry) => (
|
||||
<article class="ledger-compact-card">
|
||||
<div class="ledger-compact-card__main">
|
||||
<header>
|
||||
<strong>{entry.title}</strong>
|
||||
<span>{entry.occurredAt?.slice(0, 10) ?? '—'}</span>
|
||||
</header>
|
||||
<p>{entry.actorDisplayName ?? props.copy.ledgerActorFallback ?? ''}</p>
|
||||
<div class="ledger-compact-card__meta">
|
||||
<span class="mini-chip">{props.ledgerPrimaryAmount(entry)}</span>
|
||||
<Show when={props.ledgerSecondaryAmount(entry)}>
|
||||
{(secondary) => (
|
||||
<span class="mini-chip mini-chip--muted">{secondary()}</span>
|
||||
)}
|
||||
</Show>
|
||||
<span class="mini-chip mini-chip--muted">
|
||||
{props.purchaseParticipantSummary(entry)}
|
||||
</span>
|
||||
<Show
|
||||
when={props.dashboard}
|
||||
fallback={
|
||||
<div class="ledger-list">
|
||||
<p>{props.copy.emptyDashboard ?? ''}</p>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div class="ledger-list">
|
||||
<article class="balance-item">
|
||||
<header>
|
||||
<strong>
|
||||
{props.readyIsAdmin ? props.copy.purchaseReviewTitle : props.copy.purchasesTitle}
|
||||
</strong>
|
||||
</header>
|
||||
<Show when={props.readyIsAdmin}>
|
||||
<p>{props.copy.purchaseReviewBody ?? ''}</p>
|
||||
</Show>
|
||||
{props.purchaseEntries.length === 0 ? (
|
||||
<p>{props.copy.purchasesEmpty ?? ''}</p>
|
||||
) : (
|
||||
<div class="ledger-list">
|
||||
<For each={props.purchaseEntries}>
|
||||
{(entry) => (
|
||||
<article class="ledger-compact-card">
|
||||
<div class="ledger-compact-card__main">
|
||||
<header>
|
||||
<strong>{entry.title}</strong>
|
||||
<span>{entry.occurredAt?.slice(0, 10) ?? '—'}</span>
|
||||
</header>
|
||||
<p>{entry.actorDisplayName ?? props.copy.ledgerActorFallback ?? ''}</p>
|
||||
<div class="ledger-compact-card__meta">
|
||||
<span class="mini-chip">{props.ledgerPrimaryAmount(entry)}</span>
|
||||
<Show when={props.ledgerSecondaryAmount(entry)}>
|
||||
{(secondary) => (
|
||||
<span class="mini-chip mini-chip--muted">{secondary()}</span>
|
||||
)}
|
||||
</Show>
|
||||
<span class="mini-chip mini-chip--muted">
|
||||
{props.purchaseParticipantSummary(entry)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Show when={props.readyIsAdmin}>
|
||||
<div class="ledger-compact-card__actions">
|
||||
<IconButton
|
||||
label={props.copy.editEntryAction ?? ''}
|
||||
onClick={() => props.onOpenPurchaseEditor(entry.id)}
|
||||
>
|
||||
<PencilIcon />
|
||||
</IconButton>
|
||||
</div>
|
||||
</Show>
|
||||
</article>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
)}
|
||||
</article>
|
||||
<Modal
|
||||
open={Boolean(props.editingPurchaseEntry)}
|
||||
title={props.copy.purchaseReviewTitle ?? ''}
|
||||
description={props.copy.purchaseEditorBody ?? ''}
|
||||
closeLabel={props.copy.closeEditorAction ?? ''}
|
||||
onClose={props.onClosePurchaseEditor}
|
||||
footer={(() => {
|
||||
const entry = props.editingPurchaseEntry
|
||||
|
||||
if (!entry) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="modal-action-row">
|
||||
<Button variant="danger" onClick={() => void props.onDeletePurchase(entry.id)}>
|
||||
{props.deletingPurchaseId === entry.id
|
||||
? props.copy.deletingPurchase
|
||||
: props.copy.purchaseDeleteAction}
|
||||
</Button>
|
||||
<div class="modal-action-row__primary">
|
||||
<Button variant="ghost" onClick={props.onClosePurchaseEditor}>
|
||||
{props.copy.closeEditorAction ?? ''}
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
disabled={props.savingPurchaseId === entry.id}
|
||||
onClick={() => void props.onSavePurchase(entry.id)}
|
||||
>
|
||||
{props.savingPurchaseId === entry.id
|
||||
? props.copy.savingPurchase
|
||||
: props.copy.purchaseSaveAction}
|
||||
</Button>
|
||||
</div>
|
||||
<Show when={props.readyIsAdmin}>
|
||||
<div class="ledger-compact-card__actions">
|
||||
<IconButton
|
||||
label={props.copy.editEntryAction ?? ''}
|
||||
onClick={() => props.onOpenPurchaseEditor(entry.id)}
|
||||
>
|
||||
<PencilIcon />
|
||||
</IconButton>
|
||||
</div>
|
||||
</Show>
|
||||
</article>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
)
|
||||
})()}
|
||||
>
|
||||
{(() => {
|
||||
const entry = props.editingPurchaseEntry
|
||||
)}
|
||||
</article>
|
||||
<Modal
|
||||
open={Boolean(props.editingPurchaseEntry)}
|
||||
title={props.copy.purchaseReviewTitle ?? ''}
|
||||
description={props.copy.purchaseEditorBody ?? ''}
|
||||
closeLabel={props.copy.closeEditorAction ?? ''}
|
||||
onClose={props.onClosePurchaseEditor}
|
||||
footer={(() => {
|
||||
const entry = props.editingPurchaseEntry
|
||||
|
||||
if (!entry) {
|
||||
return null
|
||||
if (!entry) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="modal-action-row">
|
||||
<Button variant="danger" onClick={() => void props.onDeletePurchase(entry.id)}>
|
||||
{props.deletingPurchaseId === entry.id
|
||||
? props.copy.deletingPurchase
|
||||
: props.copy.purchaseDeleteAction}
|
||||
</Button>
|
||||
<div class="modal-action-row__primary">
|
||||
<Button variant="ghost" onClick={props.onClosePurchaseEditor}>
|
||||
{props.copy.closeEditorAction ?? ''}
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
disabled={props.savingPurchaseId === entry.id}
|
||||
onClick={() => void props.onSavePurchase(entry.id)}
|
||||
>
|
||||
{props.savingPurchaseId === entry.id
|
||||
? props.copy.savingPurchase
|
||||
: props.copy.purchaseSaveAction}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})()}
|
||||
>
|
||||
{(() => {
|
||||
const entry = props.editingPurchaseEntry
|
||||
|
||||
if (!entry) {
|
||||
return null
|
||||
}
|
||||
|
||||
const draft = props.purchaseDraftMap[entry.id] ?? props.purchaseDraftForEntry(entry)
|
||||
const splitPreview = props.purchaseSplitPreview(entry.id)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div class="editor-grid">
|
||||
<Field label={props.copy.purchaseReviewTitle ?? ''} wide>
|
||||
<input
|
||||
value={draft.description}
|
||||
onInput={(event) =>
|
||||
props.onPurchaseDescriptionChange(
|
||||
entry.id,
|
||||
entry,
|
||||
event.currentTarget.value
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Field>
|
||||
<Field label={props.copy.paymentAmount ?? ''}>
|
||||
<input
|
||||
value={draft.amountMajor}
|
||||
onInput={(event) =>
|
||||
props.onPurchaseAmountChange(entry.id, entry, event.currentTarget.value)
|
||||
}
|
||||
/>
|
||||
</Field>
|
||||
<Field label={props.copy.settlementCurrency ?? ''}>
|
||||
<select
|
||||
value={draft.currency}
|
||||
onChange={(event) =>
|
||||
props.onPurchaseCurrencyChange(
|
||||
entry.id,
|
||||
entry,
|
||||
event.currentTarget.value as 'USD' | 'GEL'
|
||||
)
|
||||
}
|
||||
>
|
||||
<option value="GEL">GEL</option>
|
||||
<option value="USD">USD</option>
|
||||
</select>
|
||||
</Field>
|
||||
</div>
|
||||
|
||||
<section class="editor-panel">
|
||||
<header class="editor-panel__header">
|
||||
<strong>{props.copy.purchaseSplitTitle ?? ''}</strong>
|
||||
<span>
|
||||
{draft.splitMode === 'custom_amounts'
|
||||
? props.copy.purchaseSplitCustom
|
||||
: props.copy.purchaseSplitEqual}
|
||||
</span>
|
||||
</header>
|
||||
<div class="editor-grid">
|
||||
<Field label={props.copy.purchaseSplitModeLabel ?? ''} wide>
|
||||
<select
|
||||
value={draft.splitMode}
|
||||
onChange={(event) =>
|
||||
props.onPurchaseSplitModeChange(
|
||||
entry.id,
|
||||
entry,
|
||||
event.currentTarget.value as 'equal' | 'custom_amounts'
|
||||
)
|
||||
}
|
||||
>
|
||||
<option value="equal">{props.copy.purchaseSplitEqual ?? ''}</option>
|
||||
<option value="custom_amounts">
|
||||
{props.copy.purchaseSplitCustom ?? ''}
|
||||
</option>
|
||||
</select>
|
||||
</Field>
|
||||
</div>
|
||||
<div class="participant-list">
|
||||
<For each={props.adminMembers}>
|
||||
{(member) => {
|
||||
const included = draft.participants.some(
|
||||
(participant) => participant.memberId === member.id
|
||||
)
|
||||
const previewAmount =
|
||||
splitPreview.find((participant) => participant.memberId === member.id)
|
||||
?.amountMajor ?? '0.00'
|
||||
|
||||
return (
|
||||
<article class="participant-card">
|
||||
<header>
|
||||
<strong>{member.displayName}</strong>
|
||||
<span>
|
||||
{previewAmount} {draft.currency}
|
||||
</span>
|
||||
</header>
|
||||
<div class="participant-card__controls">
|
||||
<Button
|
||||
variant={included ? 'primary' : 'secondary'}
|
||||
onClick={() =>
|
||||
props.onTogglePurchaseParticipant(
|
||||
entry.id,
|
||||
entry,
|
||||
member.id,
|
||||
!included
|
||||
)
|
||||
}
|
||||
>
|
||||
{included
|
||||
? props.copy.participantIncluded
|
||||
: props.copy.participantExcluded}
|
||||
</Button>
|
||||
<Show when={included && draft.splitMode === 'custom_amounts'}>
|
||||
<Field
|
||||
label={props.copy.purchaseCustomShareLabel ?? ''}
|
||||
class="participant-card__field"
|
||||
>
|
||||
<input
|
||||
value={
|
||||
draft.participants.find(
|
||||
(participant) => participant.memberId === member.id
|
||||
)?.shareAmountMajor ?? ''
|
||||
}
|
||||
onInput={(event) =>
|
||||
props.onPurchaseParticipantShareChange(
|
||||
entry.id,
|
||||
entry,
|
||||
member.id,
|
||||
event.currentTarget.value
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Field>
|
||||
</Show>
|
||||
</div>
|
||||
</article>
|
||||
)
|
||||
}}
|
||||
</For>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
)
|
||||
})()}
|
||||
</Modal>
|
||||
<article class="balance-item">
|
||||
<header>
|
||||
<strong>{props.copy.utilityLedgerTitle ?? ''}</strong>
|
||||
</header>
|
||||
{props.utilityEntries.length === 0 ? (
|
||||
<p>{props.copy.utilityLedgerEmpty ?? ''}</p>
|
||||
) : (
|
||||
<div class="ledger-list">
|
||||
<For each={props.utilityEntries}>
|
||||
{(entry) => (
|
||||
<article class="ledger-item">
|
||||
<header>
|
||||
<strong>{props.ledgerTitle(entry)}</strong>
|
||||
<span>{props.ledgerPrimaryAmount(entry)}</span>
|
||||
</header>
|
||||
<Show when={props.ledgerSecondaryAmount(entry)}>
|
||||
{(secondary) => <p>{secondary()}</p>}
|
||||
</Show>
|
||||
<p>{entry.actorDisplayName ?? props.copy.ledgerActorFallback ?? ''}</p>
|
||||
</article>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
)}
|
||||
</article>
|
||||
<article class="balance-item">
|
||||
<header>
|
||||
<strong>{props.copy.paymentsAdminTitle ?? ''}</strong>
|
||||
</header>
|
||||
<Show when={props.readyIsAdmin}>
|
||||
<p>{props.copy.paymentsAdminBody ?? ''}</p>
|
||||
<div class="panel-toolbar">
|
||||
<Button variant="secondary" onClick={props.onOpenAddPayment}>
|
||||
{props.copy.paymentsAddAction ?? ''}
|
||||
</Button>
|
||||
</div>
|
||||
</Show>
|
||||
{props.paymentEntries.length === 0 ? (
|
||||
<p>{props.copy.paymentsEmpty ?? ''}</p>
|
||||
) : (
|
||||
<div class="ledger-list">
|
||||
<For each={props.paymentEntries}>
|
||||
{(entry) => (
|
||||
<article class="ledger-compact-card">
|
||||
<div class="ledger-compact-card__main">
|
||||
<header>
|
||||
<strong>{props.paymentMemberName(entry)}</strong>
|
||||
<span>{entry.occurredAt?.slice(0, 10) ?? '—'}</span>
|
||||
</header>
|
||||
<p>{props.ledgerTitle(entry)}</p>
|
||||
<div class="ledger-compact-card__meta">
|
||||
<span class="mini-chip">{props.ledgerPrimaryAmount(entry)}</span>
|
||||
<Show when={props.ledgerSecondaryAmount(entry)}>
|
||||
{(secondary) => (
|
||||
<span class="mini-chip mini-chip--muted">{secondary()}</span>
|
||||
)}
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
<Show when={props.readyIsAdmin}>
|
||||
<div class="ledger-compact-card__actions">
|
||||
<IconButton
|
||||
label={props.copy.editEntryAction ?? ''}
|
||||
onClick={() => props.onOpenPaymentEditor(entry.id)}
|
||||
>
|
||||
<PencilIcon />
|
||||
</IconButton>
|
||||
</div>
|
||||
</Show>
|
||||
</article>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
)}
|
||||
</article>
|
||||
<Modal
|
||||
open={props.addingPaymentOpen}
|
||||
title={props.copy.paymentsAddAction ?? ''}
|
||||
description={props.copy.paymentCreateBody ?? ''}
|
||||
closeLabel={props.copy.closeEditorAction ?? ''}
|
||||
onClose={props.onCloseAddPayment}
|
||||
footer={
|
||||
<div class="modal-action-row modal-action-row--single">
|
||||
<Button variant="ghost" onClick={props.onCloseAddPayment}>
|
||||
{props.copy.closeEditorAction ?? ''}
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
disabled={props.addingPayment || props.paymentForm.amountMajor.trim().length === 0}
|
||||
onClick={() => void props.onAddPayment()}
|
||||
>
|
||||
{props.addingPayment ? props.copy.addingPayment : props.copy.paymentsAddAction}
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div class="editor-grid">
|
||||
<Field label={props.copy.paymentMember ?? ''} wide>
|
||||
<select
|
||||
value={props.paymentForm.memberId}
|
||||
onChange={(event) => props.onPaymentFormMemberChange(event.currentTarget.value)}
|
||||
>
|
||||
<For each={props.adminMembers}>
|
||||
{(member) => <option value={member.id}>{member.displayName}</option>}
|
||||
</For>
|
||||
</select>
|
||||
</Field>
|
||||
<Field label={props.copy.paymentKind ?? ''}>
|
||||
<select
|
||||
value={props.paymentForm.kind}
|
||||
onChange={(event) =>
|
||||
props.onPaymentFormKindChange(event.currentTarget.value as 'rent' | 'utilities')
|
||||
}
|
||||
>
|
||||
<option value="rent">{props.copy.paymentLedgerRent ?? ''}</option>
|
||||
<option value="utilities">{props.copy.paymentLedgerUtilities ?? ''}</option>
|
||||
</select>
|
||||
</Field>
|
||||
<Field label={props.copy.paymentAmount ?? ''}>
|
||||
<input
|
||||
value={props.paymentForm.amountMajor}
|
||||
onInput={(event) => props.onPaymentFormAmountChange(event.currentTarget.value)}
|
||||
/>
|
||||
</Field>
|
||||
<Field label={props.copy.settlementCurrency ?? ''}>
|
||||
<select
|
||||
value={props.paymentForm.currency}
|
||||
onChange={(event) =>
|
||||
props.onPaymentFormCurrencyChange(event.currentTarget.value as 'USD' | 'GEL')
|
||||
}
|
||||
>
|
||||
<option value="GEL">GEL</option>
|
||||
<option value="USD">USD</option>
|
||||
</select>
|
||||
</Field>
|
||||
</div>
|
||||
</Modal>
|
||||
<Modal
|
||||
open={Boolean(props.editingPaymentEntry)}
|
||||
title={props.copy.paymentsAdminTitle ?? ''}
|
||||
description={props.copy.paymentEditorBody ?? ''}
|
||||
closeLabel={props.copy.closeEditorAction ?? ''}
|
||||
onClose={props.onClosePaymentEditor}
|
||||
footer={(() => {
|
||||
const entry = props.editingPaymentEntry
|
||||
|
||||
const draft = props.purchaseDraftMap[entry.id] ?? props.purchaseDraftForEntry(entry)
|
||||
const splitPreview = props.purchaseSplitPreview(entry.id)
|
||||
if (!entry) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
return (
|
||||
<div class="modal-action-row">
|
||||
<Button variant="danger" onClick={() => void props.onDeletePayment(entry.id)}>
|
||||
{props.deletingPaymentId === entry.id
|
||||
? props.copy.deletingPayment
|
||||
: props.copy.paymentDeleteAction}
|
||||
</Button>
|
||||
<div class="modal-action-row__primary">
|
||||
<Button variant="ghost" onClick={props.onClosePaymentEditor}>
|
||||
{props.copy.closeEditorAction ?? ''}
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
disabled={props.savingPaymentId === entry.id}
|
||||
onClick={() => void props.onSavePayment(entry.id)}
|
||||
>
|
||||
{props.savingPaymentId === entry.id
|
||||
? props.copy.addingPayment
|
||||
: props.copy.paymentSaveAction}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})()}
|
||||
>
|
||||
{(() => {
|
||||
const entry = props.editingPaymentEntry
|
||||
|
||||
if (!entry) {
|
||||
return null
|
||||
}
|
||||
|
||||
const draft = props.paymentDraftMap[entry.id] ?? props.paymentDraftForEntry(entry)
|
||||
|
||||
return (
|
||||
<div class="editor-grid">
|
||||
<Field label={props.copy.purchaseReviewTitle ?? ''} wide>
|
||||
<input
|
||||
value={draft.description}
|
||||
onInput={(event) =>
|
||||
props.onPurchaseDescriptionChange(entry.id, entry, event.currentTarget.value)
|
||||
<Field label={props.copy.paymentMember ?? ''} wide>
|
||||
<select
|
||||
value={draft.memberId}
|
||||
onChange={(event) =>
|
||||
props.onPaymentDraftMemberChange(entry.id, entry, event.currentTarget.value)
|
||||
}
|
||||
/>
|
||||
>
|
||||
<For each={props.adminMembers}>
|
||||
{(member) => <option value={member.id}>{member.displayName}</option>}
|
||||
</For>
|
||||
</select>
|
||||
</Field>
|
||||
<Field label={props.copy.paymentKind ?? ''}>
|
||||
<select
|
||||
value={draft.kind}
|
||||
onChange={(event) =>
|
||||
props.onPaymentDraftKindChange(
|
||||
entry.id,
|
||||
entry,
|
||||
event.currentTarget.value as 'rent' | 'utilities'
|
||||
)
|
||||
}
|
||||
>
|
||||
<option value="rent">{props.copy.paymentLedgerRent ?? ''}</option>
|
||||
<option value="utilities">{props.copy.paymentLedgerUtilities ?? ''}</option>
|
||||
</select>
|
||||
</Field>
|
||||
<Field label={props.copy.paymentAmount ?? ''}>
|
||||
<input
|
||||
value={draft.amountMajor}
|
||||
onInput={(event) =>
|
||||
props.onPurchaseAmountChange(entry.id, entry, event.currentTarget.value)
|
||||
props.onPaymentDraftAmountChange(entry.id, entry, event.currentTarget.value)
|
||||
}
|
||||
/>
|
||||
</Field>
|
||||
@@ -249,7 +586,7 @@ export function LedgerScreen(props: Props) {
|
||||
<select
|
||||
value={draft.currency}
|
||||
onChange={(event) =>
|
||||
props.onPurchaseCurrencyChange(
|
||||
props.onPaymentDraftCurrencyChange(
|
||||
entry.id,
|
||||
entry,
|
||||
event.currentTarget.value as 'USD' | 'GEL'
|
||||
@@ -261,340 +598,10 @@ export function LedgerScreen(props: Props) {
|
||||
</select>
|
||||
</Field>
|
||||
</div>
|
||||
|
||||
<section class="editor-panel">
|
||||
<header class="editor-panel__header">
|
||||
<strong>{props.copy.purchaseSplitTitle ?? ''}</strong>
|
||||
<span>
|
||||
{draft.splitMode === 'custom_amounts'
|
||||
? props.copy.purchaseSplitCustom
|
||||
: props.copy.purchaseSplitEqual}
|
||||
</span>
|
||||
</header>
|
||||
<div class="editor-grid">
|
||||
<Field label={props.copy.purchaseSplitModeLabel ?? ''} wide>
|
||||
<select
|
||||
value={draft.splitMode}
|
||||
onChange={(event) =>
|
||||
props.onPurchaseSplitModeChange(
|
||||
entry.id,
|
||||
entry,
|
||||
event.currentTarget.value as 'equal' | 'custom_amounts'
|
||||
)
|
||||
}
|
||||
>
|
||||
<option value="equal">{props.copy.purchaseSplitEqual ?? ''}</option>
|
||||
<option value="custom_amounts">{props.copy.purchaseSplitCustom ?? ''}</option>
|
||||
</select>
|
||||
</Field>
|
||||
</div>
|
||||
<div class="participant-list">
|
||||
<For each={props.adminMembers}>
|
||||
{(member) => {
|
||||
const included = draft.participants.some(
|
||||
(participant) => participant.memberId === member.id
|
||||
)
|
||||
const previewAmount =
|
||||
splitPreview.find((participant) => participant.memberId === member.id)
|
||||
?.amountMajor ?? '0.00'
|
||||
|
||||
return (
|
||||
<article class="participant-card">
|
||||
<header>
|
||||
<strong>{member.displayName}</strong>
|
||||
<span>
|
||||
{previewAmount} {draft.currency}
|
||||
</span>
|
||||
</header>
|
||||
<div class="participant-card__controls">
|
||||
<Button
|
||||
variant={included ? 'primary' : 'secondary'}
|
||||
onClick={() =>
|
||||
props.onTogglePurchaseParticipant(
|
||||
entry.id,
|
||||
entry,
|
||||
member.id,
|
||||
!included
|
||||
)
|
||||
}
|
||||
>
|
||||
{included
|
||||
? props.copy.participantIncluded
|
||||
: props.copy.participantExcluded}
|
||||
</Button>
|
||||
<Show when={included && draft.splitMode === 'custom_amounts'}>
|
||||
<Field
|
||||
label={props.copy.purchaseCustomShareLabel ?? ''}
|
||||
class="participant-card__field"
|
||||
>
|
||||
<input
|
||||
value={
|
||||
draft.participants.find(
|
||||
(participant) => participant.memberId === member.id
|
||||
)?.shareAmountMajor ?? ''
|
||||
}
|
||||
onInput={(event) =>
|
||||
props.onPurchaseParticipantShareChange(
|
||||
entry.id,
|
||||
entry,
|
||||
member.id,
|
||||
event.currentTarget.value
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Field>
|
||||
</Show>
|
||||
</div>
|
||||
</article>
|
||||
)
|
||||
}}
|
||||
</For>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
)
|
||||
})()}
|
||||
</Modal>
|
||||
<article class="balance-item">
|
||||
<header>
|
||||
<strong>{props.copy.utilityLedgerTitle ?? ''}</strong>
|
||||
</header>
|
||||
{props.utilityEntries.length === 0 ? (
|
||||
<p>{props.copy.utilityLedgerEmpty ?? ''}</p>
|
||||
) : (
|
||||
<div class="ledger-list">
|
||||
<For each={props.utilityEntries}>
|
||||
{(entry) => (
|
||||
<article class="ledger-item">
|
||||
<header>
|
||||
<strong>{props.ledgerTitle(entry)}</strong>
|
||||
<span>{props.ledgerPrimaryAmount(entry)}</span>
|
||||
</header>
|
||||
<Show when={props.ledgerSecondaryAmount(entry)}>
|
||||
{(secondary) => <p>{secondary()}</p>}
|
||||
</Show>
|
||||
<p>{entry.actorDisplayName ?? props.copy.ledgerActorFallback ?? ''}</p>
|
||||
</article>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
)}
|
||||
</article>
|
||||
<article class="balance-item">
|
||||
<header>
|
||||
<strong>{props.copy.paymentsAdminTitle ?? ''}</strong>
|
||||
</header>
|
||||
<Show when={props.readyIsAdmin}>
|
||||
<p>{props.copy.paymentsAdminBody ?? ''}</p>
|
||||
<div class="panel-toolbar">
|
||||
<Button variant="secondary" onClick={props.onOpenAddPayment}>
|
||||
{props.copy.paymentsAddAction ?? ''}
|
||||
</Button>
|
||||
</div>
|
||||
</Show>
|
||||
{props.paymentEntries.length === 0 ? (
|
||||
<p>{props.copy.paymentsEmpty ?? ''}</p>
|
||||
) : (
|
||||
<div class="ledger-list">
|
||||
<For each={props.paymentEntries}>
|
||||
{(entry) => (
|
||||
<article class="ledger-compact-card">
|
||||
<div class="ledger-compact-card__main">
|
||||
<header>
|
||||
<strong>{props.paymentMemberName(entry)}</strong>
|
||||
<span>{entry.occurredAt?.slice(0, 10) ?? '—'}</span>
|
||||
</header>
|
||||
<p>{props.ledgerTitle(entry)}</p>
|
||||
<div class="ledger-compact-card__meta">
|
||||
<span class="mini-chip">{props.ledgerPrimaryAmount(entry)}</span>
|
||||
<Show when={props.ledgerSecondaryAmount(entry)}>
|
||||
{(secondary) => (
|
||||
<span class="mini-chip mini-chip--muted">{secondary()}</span>
|
||||
)}
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
<Show when={props.readyIsAdmin}>
|
||||
<div class="ledger-compact-card__actions">
|
||||
<IconButton
|
||||
label={props.copy.editEntryAction ?? ''}
|
||||
onClick={() => props.onOpenPaymentEditor(entry.id)}
|
||||
>
|
||||
<PencilIcon />
|
||||
</IconButton>
|
||||
</div>
|
||||
</Show>
|
||||
</article>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
)}
|
||||
</article>
|
||||
<Modal
|
||||
open={props.addingPaymentOpen}
|
||||
title={props.copy.paymentsAddAction ?? ''}
|
||||
description={props.copy.paymentCreateBody ?? ''}
|
||||
closeLabel={props.copy.closeEditorAction ?? ''}
|
||||
onClose={props.onCloseAddPayment}
|
||||
footer={
|
||||
<div class="modal-action-row modal-action-row--single">
|
||||
<Button variant="ghost" onClick={props.onCloseAddPayment}>
|
||||
{props.copy.closeEditorAction ?? ''}
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
disabled={props.addingPayment || props.paymentForm.amountMajor.trim().length === 0}
|
||||
onClick={() => void props.onAddPayment()}
|
||||
>
|
||||
{props.addingPayment ? props.copy.addingPayment : props.copy.paymentsAddAction}
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div class="editor-grid">
|
||||
<Field label={props.copy.paymentMember ?? ''} wide>
|
||||
<select
|
||||
value={props.paymentForm.memberId}
|
||||
onChange={(event) => props.onPaymentFormMemberChange(event.currentTarget.value)}
|
||||
>
|
||||
<For each={props.adminMembers}>
|
||||
{(member) => <option value={member.id}>{member.displayName}</option>}
|
||||
</For>
|
||||
</select>
|
||||
</Field>
|
||||
<Field label={props.copy.paymentKind ?? ''}>
|
||||
<select
|
||||
value={props.paymentForm.kind}
|
||||
onChange={(event) =>
|
||||
props.onPaymentFormKindChange(event.currentTarget.value as 'rent' | 'utilities')
|
||||
}
|
||||
>
|
||||
<option value="rent">{props.copy.paymentLedgerRent ?? ''}</option>
|
||||
<option value="utilities">{props.copy.paymentLedgerUtilities ?? ''}</option>
|
||||
</select>
|
||||
</Field>
|
||||
<Field label={props.copy.paymentAmount ?? ''}>
|
||||
<input
|
||||
value={props.paymentForm.amountMajor}
|
||||
onInput={(event) => props.onPaymentFormAmountChange(event.currentTarget.value)}
|
||||
/>
|
||||
</Field>
|
||||
<Field label={props.copy.settlementCurrency ?? ''}>
|
||||
<select
|
||||
value={props.paymentForm.currency}
|
||||
onChange={(event) =>
|
||||
props.onPaymentFormCurrencyChange(event.currentTarget.value as 'USD' | 'GEL')
|
||||
}
|
||||
>
|
||||
<option value="GEL">GEL</option>
|
||||
<option value="USD">USD</option>
|
||||
</select>
|
||||
</Field>
|
||||
</div>
|
||||
</Modal>
|
||||
<Modal
|
||||
open={Boolean(props.editingPaymentEntry)}
|
||||
title={props.copy.paymentsAdminTitle ?? ''}
|
||||
description={props.copy.paymentEditorBody ?? ''}
|
||||
closeLabel={props.copy.closeEditorAction ?? ''}
|
||||
onClose={props.onClosePaymentEditor}
|
||||
footer={(() => {
|
||||
const entry = props.editingPaymentEntry
|
||||
|
||||
if (!entry) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="modal-action-row">
|
||||
<Button variant="danger" onClick={() => void props.onDeletePayment(entry.id)}>
|
||||
{props.deletingPaymentId === entry.id
|
||||
? props.copy.deletingPayment
|
||||
: props.copy.paymentDeleteAction}
|
||||
</Button>
|
||||
<div class="modal-action-row__primary">
|
||||
<Button variant="ghost" onClick={props.onClosePaymentEditor}>
|
||||
{props.copy.closeEditorAction ?? ''}
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
disabled={props.savingPaymentId === entry.id}
|
||||
onClick={() => void props.onSavePayment(entry.id)}
|
||||
>
|
||||
{props.savingPaymentId === entry.id
|
||||
? props.copy.addingPayment
|
||||
: props.copy.paymentSaveAction}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})()}
|
||||
>
|
||||
{(() => {
|
||||
const entry = props.editingPaymentEntry
|
||||
|
||||
if (!entry) {
|
||||
return null
|
||||
}
|
||||
|
||||
const draft = props.paymentDraftMap[entry.id] ?? props.paymentDraftForEntry(entry)
|
||||
|
||||
return (
|
||||
<div class="editor-grid">
|
||||
<Field label={props.copy.paymentMember ?? ''} wide>
|
||||
<select
|
||||
value={draft.memberId}
|
||||
onChange={(event) =>
|
||||
props.onPaymentDraftMemberChange(entry.id, entry, event.currentTarget.value)
|
||||
}
|
||||
>
|
||||
<For each={props.adminMembers}>
|
||||
{(member) => <option value={member.id}>{member.displayName}</option>}
|
||||
</For>
|
||||
</select>
|
||||
</Field>
|
||||
<Field label={props.copy.paymentKind ?? ''}>
|
||||
<select
|
||||
value={draft.kind}
|
||||
onChange={(event) =>
|
||||
props.onPaymentDraftKindChange(
|
||||
entry.id,
|
||||
entry,
|
||||
event.currentTarget.value as 'rent' | 'utilities'
|
||||
)
|
||||
}
|
||||
>
|
||||
<option value="rent">{props.copy.paymentLedgerRent ?? ''}</option>
|
||||
<option value="utilities">{props.copy.paymentLedgerUtilities ?? ''}</option>
|
||||
</select>
|
||||
</Field>
|
||||
<Field label={props.copy.paymentAmount ?? ''}>
|
||||
<input
|
||||
value={draft.amountMajor}
|
||||
onInput={(event) =>
|
||||
props.onPaymentDraftAmountChange(entry.id, entry, event.currentTarget.value)
|
||||
}
|
||||
/>
|
||||
</Field>
|
||||
<Field label={props.copy.settlementCurrency ?? ''}>
|
||||
<select
|
||||
value={draft.currency}
|
||||
onChange={(event) =>
|
||||
props.onPaymentDraftCurrencyChange(
|
||||
entry.id,
|
||||
entry,
|
||||
event.currentTarget.value as 'USD' | 'GEL'
|
||||
)
|
||||
}
|
||||
>
|
||||
<option value="GEL">GEL</option>
|
||||
<option value="USD">USD</option>
|
||||
</select>
|
||||
</Field>
|
||||
</div>
|
||||
)
|
||||
})()}
|
||||
</Modal>
|
||||
</div>
|
||||
)
|
||||
})()}
|
||||
</Modal>
|
||||
</div>
|
||||
</Show>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user