mirror of
https://github.com/whekin/household-bot.git
synced 2026-03-31 12:04:02 +00:00
refactor(miniapp): split home and balances screens
This commit is contained in:
@@ -40,11 +40,11 @@ import { HeroBanner } from './components/layout/hero-banner'
|
|||||||
import { NavigationTabs } from './components/layout/navigation-tabs'
|
import { NavigationTabs } from './components/layout/navigation-tabs'
|
||||||
import { ProfileCard } from './components/layout/profile-card'
|
import { ProfileCard } from './components/layout/profile-card'
|
||||||
import { TopBar } from './components/layout/top-bar'
|
import { TopBar } from './components/layout/top-bar'
|
||||||
import { FinanceSummaryCards } from './components/finance/finance-summary-cards'
|
|
||||||
import { FinanceVisuals } from './components/finance/finance-visuals'
|
|
||||||
import { BlockedState } from './components/session/blocked-state'
|
import { BlockedState } from './components/session/blocked-state'
|
||||||
import { LoadingState } from './components/session/loading-state'
|
import { LoadingState } from './components/session/loading-state'
|
||||||
import { OnboardingState } from './components/session/onboarding-state'
|
import { OnboardingState } from './components/session/onboarding-state'
|
||||||
|
import { BalancesScreen } from './screens/balances-screen'
|
||||||
|
import { HomeScreen } from './screens/home-screen'
|
||||||
import {
|
import {
|
||||||
demoAdminSettings,
|
demoAdminSettings,
|
||||||
demoCycleState,
|
demoCycleState,
|
||||||
@@ -1854,122 +1854,17 @@ function App() {
|
|||||||
switch (activeNav()) {
|
switch (activeNav()) {
|
||||||
case 'balances':
|
case 'balances':
|
||||||
return (
|
return (
|
||||||
<div class="balance-list">
|
<BalancesScreen
|
||||||
<ShowDashboard
|
copy={copy()}
|
||||||
dashboard={dashboard()}
|
dashboard={dashboard()}
|
||||||
fallback={<p>{copy().emptyDashboard}</p>}
|
currentMemberLine={currentMemberLine()}
|
||||||
render={(data) => (
|
utilityTotalMajor={utilityTotalMajor()}
|
||||||
<>
|
purchaseTotalMajor={purchaseTotalMajor()}
|
||||||
{currentMemberLine() ? (
|
memberBalanceVisuals={memberBalanceVisuals()}
|
||||||
<article class="balance-item balance-item--accent">
|
purchaseChart={purchaseInvestmentChart()}
|
||||||
<header>
|
memberBaseDueMajor={memberBaseDueMajor}
|
||||||
<strong>{copy().yourBalanceTitle}</strong>
|
memberRemainingClass={memberRemainingClass}
|
||||||
<span>
|
/>
|
||||||
{currentMemberLine()!.netDueMajor} {data.currency}
|
|
||||||
</span>
|
|
||||||
</header>
|
|
||||||
<p>{copy().yourBalanceBody}</p>
|
|
||||||
<div class="balance-breakdown">
|
|
||||||
<div class="stat-card">
|
|
||||||
<span>{copy().baseDue}</span>
|
|
||||||
<strong>
|
|
||||||
{memberBaseDueMajor(currentMemberLine()!)} {data.currency}
|
|
||||||
</strong>
|
|
||||||
</div>
|
|
||||||
<div class="stat-card">
|
|
||||||
<span>{copy().shareOffset}</span>
|
|
||||||
<strong>
|
|
||||||
{currentMemberLine()!.purchaseOffsetMajor} {data.currency}
|
|
||||||
</strong>
|
|
||||||
</div>
|
|
||||||
<div class="stat-card">
|
|
||||||
<span>{copy().finalDue}</span>
|
|
||||||
<strong>
|
|
||||||
{currentMemberLine()!.netDueMajor} {data.currency}
|
|
||||||
</strong>
|
|
||||||
</div>
|
|
||||||
<div class="stat-card">
|
|
||||||
<span>{copy().paidLabel}</span>
|
|
||||||
<strong>
|
|
||||||
{currentMemberLine()!.paidMajor} {data.currency}
|
|
||||||
</strong>
|
|
||||||
</div>
|
|
||||||
<div class="stat-card">
|
|
||||||
<span>{copy().remainingLabel}</span>
|
|
||||||
<strong>
|
|
||||||
{currentMemberLine()!.remainingMajor} {data.currency}
|
|
||||||
</strong>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
) : null}
|
|
||||||
<div class="home-grid home-grid--summary">
|
|
||||||
<FinanceSummaryCards
|
|
||||||
dashboard={data}
|
|
||||||
utilityTotalMajor={utilityTotalMajor()}
|
|
||||||
purchaseTotalMajor={purchaseTotalMajor()}
|
|
||||||
labels={{
|
|
||||||
remaining: copy().remainingLabel,
|
|
||||||
rent: copy().shareRent,
|
|
||||||
utilities: copy().shareUtilities,
|
|
||||||
purchases: copy().purchasesTitle
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<FinanceVisuals
|
|
||||||
dashboard={data}
|
|
||||||
memberVisuals={memberBalanceVisuals()}
|
|
||||||
purchaseChart={purchaseInvestmentChart()}
|
|
||||||
remainingClass={memberRemainingClass}
|
|
||||||
labels={{
|
|
||||||
financeVisualsTitle: copy().financeVisualsTitle,
|
|
||||||
financeVisualsBody: copy().financeVisualsBody,
|
|
||||||
membersCount: copy().membersCount,
|
|
||||||
purchaseInvestmentsTitle: copy().purchaseInvestmentsTitle,
|
|
||||||
purchaseInvestmentsBody: copy().purchaseInvestmentsBody,
|
|
||||||
purchaseInvestmentsEmpty: copy().purchaseInvestmentsEmpty,
|
|
||||||
purchaseTotalLabel: copy().purchaseTotalLabel,
|
|
||||||
purchaseShareLabel: copy().purchaseShareLabel
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<article class="balance-item">
|
|
||||||
<header>
|
|
||||||
<strong>{copy().householdBalancesTitle}</strong>
|
|
||||||
</header>
|
|
||||||
<p>{copy().householdBalancesBody}</p>
|
|
||||||
</article>
|
|
||||||
{data.members.map((member) => (
|
|
||||||
<article class="balance-item">
|
|
||||||
<header>
|
|
||||||
<strong>{member.displayName}</strong>
|
|
||||||
<span>
|
|
||||||
{member.remainingMajor} {data.currency}
|
|
||||||
</span>
|
|
||||||
</header>
|
|
||||||
<p>
|
|
||||||
{copy().baseDue}: {memberBaseDueMajor(member)} {data.currency}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
{copy().shareRent}: {member.rentShareMajor} {data.currency}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
{copy().shareUtilities}: {member.utilityShareMajor} {data.currency}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
{copy().shareOffset}: {member.purchaseOffsetMajor} {data.currency}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
{copy().paidLabel}: {member.paidMajor} {data.currency}
|
|
||||||
</p>
|
|
||||||
<p class={`balance-status ${memberRemainingClass(member)}`}>
|
|
||||||
{copy().remainingLabel}: {member.remainingMajor} {data.currency}
|
|
||||||
</p>
|
|
||||||
</article>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
case 'ledger':
|
case 'ledger':
|
||||||
return (
|
return (
|
||||||
@@ -3583,166 +3478,22 @@ function App() {
|
|||||||
)
|
)
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<div class="home-grid home-grid--summary">
|
<HomeScreen
|
||||||
<ShowDashboard
|
copy={copy()}
|
||||||
dashboard={dashboard()}
|
dashboard={dashboard()}
|
||||||
fallback={
|
readyIsAdmin={Boolean(readySession()?.member.isAdmin)}
|
||||||
<>
|
pendingMembersCount={pendingMembers().length}
|
||||||
<article class="stat-card">
|
currentMemberLine={currentMemberLine()}
|
||||||
<span>{copy().remainingLabel}</span>
|
utilityTotalMajor={utilityTotalMajor()}
|
||||||
<strong>—</strong>
|
purchaseTotalMajor={purchaseTotalMajor()}
|
||||||
</article>
|
memberBalanceVisuals={memberBalanceVisuals()}
|
||||||
<article class="stat-card">
|
purchaseChart={purchaseInvestmentChart()}
|
||||||
<span>{copy().shareRent}</span>
|
memberBaseDueMajor={memberBaseDueMajor}
|
||||||
<strong>—</strong>
|
memberRemainingClass={memberRemainingClass}
|
||||||
</article>
|
ledgerTitle={ledgerTitle}
|
||||||
<article class="stat-card">
|
ledgerPrimaryAmount={ledgerPrimaryAmount}
|
||||||
<span>{copy().shareUtilities}</span>
|
ledgerSecondaryAmount={ledgerSecondaryAmount}
|
||||||
<strong>—</strong>
|
/>
|
||||||
</article>
|
|
||||||
<article class="stat-card">
|
|
||||||
<span>{copy().purchasesTitle}</span>
|
|
||||||
<strong>—</strong>
|
|
||||||
</article>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
render={(data) => (
|
|
||||||
<FinanceSummaryCards
|
|
||||||
dashboard={data}
|
|
||||||
utilityTotalMajor={utilityTotalMajor()}
|
|
||||||
purchaseTotalMajor={purchaseTotalMajor()}
|
|
||||||
labels={{
|
|
||||||
remaining: copy().remainingLabel,
|
|
||||||
rent: copy().shareRent,
|
|
||||||
utilities: copy().shareUtilities,
|
|
||||||
purchases: copy().purchasesTitle
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{readySession()?.member.isAdmin ? (
|
|
||||||
<article class="stat-card">
|
|
||||||
<span>{copy().pendingRequests}</span>
|
|
||||||
<strong>{String(pendingMembers().length)}</strong>
|
|
||||||
</article>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{currentMemberLine() ? (
|
|
||||||
<article class="balance-item balance-item--accent">
|
|
||||||
<header>
|
|
||||||
<strong>{copy().yourBalanceTitle}</strong>
|
|
||||||
<span>
|
|
||||||
{currentMemberLine()!.remainingMajor} {dashboard()?.currency ?? ''}
|
|
||||||
</span>
|
|
||||||
</header>
|
|
||||||
<p>{copy().yourBalanceBody}</p>
|
|
||||||
<ShowDashboard
|
|
||||||
dashboard={dashboard()}
|
|
||||||
fallback={null}
|
|
||||||
render={(data) => (
|
|
||||||
<p>
|
|
||||||
{copy().shareRent}: {data.rentSourceAmountMajor} {data.rentSourceCurrency}
|
|
||||||
{data.rentSourceCurrency !== data.currency
|
|
||||||
? ` -> ${data.rentDisplayAmountMajor} ${data.currency}`
|
|
||||||
: ''}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<div class="balance-breakdown">
|
|
||||||
<div class="stat-card">
|
|
||||||
<span>{copy().baseDue}</span>
|
|
||||||
<strong>
|
|
||||||
{memberBaseDueMajor(currentMemberLine()!)} {dashboard()?.currency ?? ''}
|
|
||||||
</strong>
|
|
||||||
</div>
|
|
||||||
<div class="stat-card">
|
|
||||||
<span>{copy().shareOffset}</span>
|
|
||||||
<strong>
|
|
||||||
{currentMemberLine()!.purchaseOffsetMajor} {dashboard()?.currency ?? ''}
|
|
||||||
</strong>
|
|
||||||
</div>
|
|
||||||
<div class="stat-card">
|
|
||||||
<span>{copy().finalDue}</span>
|
|
||||||
<strong>
|
|
||||||
{currentMemberLine()!.netDueMajor} {dashboard()?.currency ?? ''}
|
|
||||||
</strong>
|
|
||||||
</div>
|
|
||||||
<div class="stat-card">
|
|
||||||
<span>{copy().paidLabel}</span>
|
|
||||||
<strong>
|
|
||||||
{currentMemberLine()!.paidMajor} {dashboard()?.currency ?? ''}
|
|
||||||
</strong>
|
|
||||||
</div>
|
|
||||||
<div class="stat-card">
|
|
||||||
<span>{copy().remainingLabel}</span>
|
|
||||||
<strong>
|
|
||||||
{currentMemberLine()!.remainingMajor} {dashboard()?.currency ?? ''}
|
|
||||||
</strong>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
) : (
|
|
||||||
<article class="balance-item">
|
|
||||||
<header>
|
|
||||||
<strong>{copy().overviewTitle}</strong>
|
|
||||||
</header>
|
|
||||||
<p>{copy().overviewBody}</p>
|
|
||||||
</article>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<ShowDashboard
|
|
||||||
dashboard={dashboard()}
|
|
||||||
fallback={null}
|
|
||||||
render={(data) => (
|
|
||||||
<FinanceVisuals
|
|
||||||
dashboard={data}
|
|
||||||
memberVisuals={memberBalanceVisuals()}
|
|
||||||
purchaseChart={purchaseInvestmentChart()}
|
|
||||||
remainingClass={memberRemainingClass}
|
|
||||||
labels={{
|
|
||||||
financeVisualsTitle: copy().financeVisualsTitle,
|
|
||||||
financeVisualsBody: copy().financeVisualsBody,
|
|
||||||
membersCount: copy().membersCount,
|
|
||||||
purchaseInvestmentsTitle: copy().purchaseInvestmentsTitle,
|
|
||||||
purchaseInvestmentsBody: copy().purchaseInvestmentsBody,
|
|
||||||
purchaseInvestmentsEmpty: copy().purchaseInvestmentsEmpty,
|
|
||||||
purchaseTotalLabel: copy().purchaseTotalLabel,
|
|
||||||
purchaseShareLabel: copy().purchaseShareLabel
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<article class="balance-item balance-item--wide">
|
|
||||||
<header>
|
|
||||||
<strong>{copy().latestActivityTitle}</strong>
|
|
||||||
</header>
|
|
||||||
<ShowDashboard
|
|
||||||
dashboard={dashboard()}
|
|
||||||
fallback={<p>{copy().latestActivityEmpty}</p>}
|
|
||||||
render={(data) =>
|
|
||||||
data.ledger.length === 0 ? (
|
|
||||||
<p>{copy().latestActivityEmpty}</p>
|
|
||||||
) : (
|
|
||||||
<div class="activity-list">
|
|
||||||
{data.ledger.slice(0, 3).map((entry) => (
|
|
||||||
<article class="activity-row">
|
|
||||||
<header>
|
|
||||||
<strong>{ledgerTitle(entry)}</strong>
|
|
||||||
<span>{ledgerPrimaryAmount(entry)}</span>
|
|
||||||
</header>
|
|
||||||
<Show when={ledgerSecondaryAmount(entry)}>
|
|
||||||
{(secondary) => <p>{secondary()}</p>}
|
|
||||||
</Show>
|
|
||||||
<p>{entry.actorDisplayName ?? copy().ledgerActorFallback}</p>
|
|
||||||
</article>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
167
apps/miniapp/src/screens/balances-screen.tsx
Normal file
167
apps/miniapp/src/screens/balances-screen.tsx
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
import { For, Show } from 'solid-js'
|
||||||
|
|
||||||
|
import { FinanceSummaryCards } from '../components/finance/finance-summary-cards'
|
||||||
|
import { FinanceVisuals } from '../components/finance/finance-visuals'
|
||||||
|
import type { MiniAppDashboard } from '../miniapp-api'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
copy: Record<string, string | undefined>
|
||||||
|
dashboard: MiniAppDashboard | null
|
||||||
|
currentMemberLine: MiniAppDashboard['members'][number] | null
|
||||||
|
utilityTotalMajor: string
|
||||||
|
purchaseTotalMajor: string
|
||||||
|
memberBalanceVisuals: {
|
||||||
|
member: MiniAppDashboard['members'][number]
|
||||||
|
totalMinor: bigint
|
||||||
|
barWidthPercent: number
|
||||||
|
segments: {
|
||||||
|
key: string
|
||||||
|
label: string
|
||||||
|
amountMajor: string
|
||||||
|
amountMinor: bigint
|
||||||
|
widthPercent: number
|
||||||
|
}[]
|
||||||
|
}[]
|
||||||
|
purchaseChart: {
|
||||||
|
totalMajor: string
|
||||||
|
slices: {
|
||||||
|
key: string
|
||||||
|
label: string
|
||||||
|
amountMajor: string
|
||||||
|
color: string
|
||||||
|
percentage: number
|
||||||
|
dasharray: string
|
||||||
|
dashoffset: string
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
memberBaseDueMajor: (member: MiniAppDashboard['members'][number]) => string
|
||||||
|
memberRemainingClass: (member: MiniAppDashboard['members'][number]) => string
|
||||||
|
}
|
||||||
|
|
||||||
|
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>
|
||||||
|
</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="home-grid home-grid--summary">
|
||||||
|
<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) => (
|
||||||
|
<article class="balance-item">
|
||||||
|
<header>
|
||||||
|
<strong>{member.displayName}</strong>
|
||||||
|
<span>
|
||||||
|
{member.remainingMajor} {props.dashboard!.currency}
|
||||||
|
</span>
|
||||||
|
</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>
|
||||||
|
</article>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
197
apps/miniapp/src/screens/home-screen.tsx
Normal file
197
apps/miniapp/src/screens/home-screen.tsx
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
import { For, Show } from 'solid-js'
|
||||||
|
|
||||||
|
import { FinanceSummaryCards } from '../components/finance/finance-summary-cards'
|
||||||
|
import { FinanceVisuals } from '../components/finance/finance-visuals'
|
||||||
|
import type { MiniAppDashboard } from '../miniapp-api'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
copy: Record<string, string | undefined>
|
||||||
|
dashboard: MiniAppDashboard | null
|
||||||
|
readyIsAdmin: boolean
|
||||||
|
pendingMembersCount: number
|
||||||
|
currentMemberLine: MiniAppDashboard['members'][number] | null
|
||||||
|
utilityTotalMajor: string
|
||||||
|
purchaseTotalMajor: string
|
||||||
|
memberBalanceVisuals: {
|
||||||
|
member: MiniAppDashboard['members'][number]
|
||||||
|
totalMinor: bigint
|
||||||
|
barWidthPercent: number
|
||||||
|
segments: {
|
||||||
|
key: string
|
||||||
|
label: string
|
||||||
|
amountMajor: string
|
||||||
|
amountMinor: bigint
|
||||||
|
widthPercent: number
|
||||||
|
}[]
|
||||||
|
}[]
|
||||||
|
purchaseChart: {
|
||||||
|
totalMajor: string
|
||||||
|
slices: {
|
||||||
|
key: string
|
||||||
|
label: string
|
||||||
|
amountMajor: string
|
||||||
|
color: string
|
||||||
|
percentage: number
|
||||||
|
dasharray: string
|
||||||
|
dashoffset: string
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
memberBaseDueMajor: (member: MiniAppDashboard['members'][number]) => string
|
||||||
|
memberRemainingClass: (member: MiniAppDashboard['members'][number]) => string
|
||||||
|
ledgerTitle: (entry: MiniAppDashboard['ledger'][number]) => string
|
||||||
|
ledgerPrimaryAmount: (entry: MiniAppDashboard['ledger'][number]) => string
|
||||||
|
ledgerSecondaryAmount: (entry: MiniAppDashboard['ledger'][number]) => string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export function HomeScreen(props: Props) {
|
||||||
|
if (!props.dashboard) {
|
||||||
|
return (
|
||||||
|
<div class="home-grid home-grid--summary">
|
||||||
|
<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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class="home-grid home-grid--summary">
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<Show
|
||||||
|
when={props.currentMemberLine}
|
||||||
|
fallback={
|
||||||
|
<article class="balance-item">
|
||||||
|
<header>
|
||||||
|
<strong>{props.copy.overviewTitle ?? ''}</strong>
|
||||||
|
</header>
|
||||||
|
<p>{props.copy.overviewBody ?? ''}</p>
|
||||||
|
</article>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{(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.yourBalanceBody ?? ''}</p>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<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 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>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user