mirror of
https://github.com/whekin/household-bot.git
synced 2026-03-31 15:54:03 +00:00
fix(miniapp): refresh billing state and clean up controls
This commit is contained in:
@@ -431,6 +431,7 @@ function App() {
|
||||
const editingPaymentEntry = createMemo(
|
||||
() => paymentLedger().find((entry) => entry.id === editingPaymentId()) ?? null
|
||||
)
|
||||
const defaultPaymentMemberId = createMemo(() => adminSettings()?.members[0]?.id ?? '')
|
||||
const editingUtilityBill = createMemo(
|
||||
() => cycleState()?.utilityBills.find((bill) => bill.id === editingUtilityBillId()) ?? null
|
||||
)
|
||||
@@ -777,7 +778,10 @@ function App() {
|
||||
})
|
||||
setPaymentForm((current) => ({
|
||||
...current,
|
||||
memberId: current.memberId || payload.members[0]?.id || '',
|
||||
memberId:
|
||||
(current.memberId && payload.members.some((member) => member.id === current.memberId)
|
||||
? current.memberId
|
||||
: payload.members[0]?.id) ?? '',
|
||||
currency: payload.settings.settlementCurrency
|
||||
}))
|
||||
} catch (error) {
|
||||
@@ -1214,6 +1218,7 @@ function App() {
|
||||
rentCurrency: settings.rentCurrency,
|
||||
utilityCurrency: settings.settlementCurrency
|
||||
}))
|
||||
await refreshHouseholdData(initData, true, true)
|
||||
setBillingSettingsOpen(false)
|
||||
} finally {
|
||||
setSavingBillingSettings(false)
|
||||
@@ -1241,6 +1246,7 @@ function App() {
|
||||
period: state.cycle?.period ?? current.period,
|
||||
utilityCurrency: billingForm().settlementCurrency
|
||||
}))
|
||||
await refreshHouseholdData(initData, true, true)
|
||||
setCycleRentOpen(false)
|
||||
} finally {
|
||||
setOpeningCycle(false)
|
||||
@@ -1268,6 +1274,7 @@ function App() {
|
||||
})
|
||||
setCycleState(state)
|
||||
setUtilityBillDrafts(cycleUtilityBillDrafts(state.utilityBills))
|
||||
await refreshHouseholdData(initData, true, true)
|
||||
setCycleRentOpen(false)
|
||||
} finally {
|
||||
setSavingCycleRent(false)
|
||||
@@ -1304,6 +1311,7 @@ function App() {
|
||||
...current,
|
||||
utilityAmountMajor: ''
|
||||
}))
|
||||
await refreshHouseholdData(initData, true, true)
|
||||
setAddingUtilityBillOpen(false)
|
||||
} finally {
|
||||
setSavingUtilityBill(false)
|
||||
@@ -1337,6 +1345,7 @@ function App() {
|
||||
})
|
||||
setCycleState(state)
|
||||
setUtilityBillDrafts(cycleUtilityBillDrafts(state.utilityBills))
|
||||
await refreshHouseholdData(initData, true, true)
|
||||
setEditingUtilityBillId(null)
|
||||
} finally {
|
||||
setSavingUtilityBillId(null)
|
||||
@@ -1356,6 +1365,7 @@ function App() {
|
||||
const state = await deleteMiniAppUtilityBill(initData, billId)
|
||||
setCycleState(state)
|
||||
setUtilityBillDrafts(cycleUtilityBillDrafts(state.utilityBills))
|
||||
await refreshHouseholdData(initData, true, true)
|
||||
setEditingUtilityBillId((current) => (current === billId ? null : current))
|
||||
} finally {
|
||||
setDeletingUtilityBillId(null)
|
||||
@@ -1437,11 +1447,12 @@ function App() {
|
||||
const initData = webApp?.initData?.trim()
|
||||
const currentReady = readySession()
|
||||
const draft = paymentForm()
|
||||
const memberId = draft.memberId.trim() || defaultPaymentMemberId()
|
||||
if (
|
||||
!initData ||
|
||||
currentReady?.mode !== 'live' ||
|
||||
!currentReady.member.isAdmin ||
|
||||
draft.memberId.trim().length === 0 ||
|
||||
memberId.length === 0 ||
|
||||
draft.amountMajor.trim().length === 0
|
||||
) {
|
||||
return
|
||||
@@ -1450,9 +1461,13 @@ function App() {
|
||||
setAddingPayment(true)
|
||||
|
||||
try {
|
||||
await addMiniAppPayment(initData, draft)
|
||||
await addMiniAppPayment(initData, {
|
||||
...draft,
|
||||
memberId
|
||||
})
|
||||
setPaymentForm((current) => ({
|
||||
...current,
|
||||
memberId,
|
||||
amountMajor: ''
|
||||
}))
|
||||
await refreshHouseholdData(initData, true, true)
|
||||
@@ -1582,7 +1597,11 @@ function App() {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSaveRentWeight(memberId: string, closeEditor = true) {
|
||||
async function handleSaveRentWeight(
|
||||
memberId: string,
|
||||
closeEditor = true,
|
||||
refreshAfterSave = true
|
||||
) {
|
||||
const initData = webApp?.initData?.trim()
|
||||
const currentReady = readySession()
|
||||
const nextWeight = Number(rentWeightDrafts()[memberId] ?? '')
|
||||
@@ -1612,6 +1631,9 @@ function App() {
|
||||
...current,
|
||||
[member.id]: String(member.rentShareWeight)
|
||||
}))
|
||||
if (refreshAfterSave) {
|
||||
await refreshHouseholdData(initData, true, true)
|
||||
}
|
||||
if (closeEditor) {
|
||||
setEditingMemberId(null)
|
||||
}
|
||||
@@ -1620,7 +1642,11 @@ function App() {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSaveMemberStatus(memberId: string, closeEditor = true) {
|
||||
async function handleSaveMemberStatus(
|
||||
memberId: string,
|
||||
closeEditor = true,
|
||||
refreshAfterSave = true
|
||||
) {
|
||||
const initData = webApp?.initData?.trim()
|
||||
const currentReady = readySession()
|
||||
const nextStatus = memberStatusDrafts()[memberId]
|
||||
@@ -1651,6 +1677,9 @@ function App() {
|
||||
resolvedMemberAbsencePolicy(member.id, member.status).policy ??
|
||||
defaultAbsencePolicyForStatus(member.status)
|
||||
}))
|
||||
if (refreshAfterSave) {
|
||||
await refreshHouseholdData(initData, true, true)
|
||||
}
|
||||
if (closeEditor) {
|
||||
setEditingMemberId(null)
|
||||
}
|
||||
@@ -1659,7 +1688,11 @@ function App() {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSaveMemberAbsencePolicy(memberId: string, closeEditor = true) {
|
||||
async function handleSaveMemberAbsencePolicy(
|
||||
memberId: string,
|
||||
closeEditor = true,
|
||||
refreshAfterSave = true
|
||||
) {
|
||||
const initData = webApp?.initData?.trim()
|
||||
const currentReady = readySession()
|
||||
const member = adminSettings()?.members.find((entry) => entry.id === memberId)
|
||||
@@ -1702,6 +1735,9 @@ function App() {
|
||||
...current,
|
||||
[memberId]: savedPolicy.policy
|
||||
}))
|
||||
if (refreshAfterSave) {
|
||||
await refreshHouseholdData(initData, true, true)
|
||||
}
|
||||
if (closeEditor) {
|
||||
setEditingMemberId(null)
|
||||
}
|
||||
@@ -1736,6 +1772,7 @@ function App() {
|
||||
const hasNameChange = nextDisplayName !== member.displayName
|
||||
const hasStatusChange = nextStatus !== member.status
|
||||
const hasWeightChange = nextWeight !== member.rentShareWeight
|
||||
const requiresDashboardRefresh = hasStatusChange || wantsAwayPolicySave || hasWeightChange
|
||||
|
||||
if (!hasNameChange && !hasStatusChange && !wantsAwayPolicySave && !hasWeightChange) {
|
||||
return
|
||||
@@ -1749,15 +1786,22 @@ function App() {
|
||||
}
|
||||
|
||||
if (hasStatusChange) {
|
||||
await handleSaveMemberStatus(memberId, false)
|
||||
await handleSaveMemberStatus(memberId, false, false)
|
||||
}
|
||||
|
||||
if (wantsAwayPolicySave) {
|
||||
await handleSaveMemberAbsencePolicy(memberId, false)
|
||||
await handleSaveMemberAbsencePolicy(memberId, false, false)
|
||||
}
|
||||
|
||||
if (hasWeightChange) {
|
||||
await handleSaveRentWeight(memberId, false)
|
||||
await handleSaveRentWeight(memberId, false, false)
|
||||
}
|
||||
|
||||
if (requiresDashboardRefresh) {
|
||||
const initData = webApp?.initData?.trim()
|
||||
if (initData) {
|
||||
await refreshHouseholdData(initData, true, true)
|
||||
}
|
||||
}
|
||||
|
||||
setEditingMemberId(null)
|
||||
@@ -1800,6 +1844,7 @@ function App() {
|
||||
return (
|
||||
<BalancesScreen
|
||||
copy={copy()}
|
||||
locale={locale()}
|
||||
dashboard={dashboard()}
|
||||
currentMemberLine={currentMemberLine()}
|
||||
/>
|
||||
@@ -1808,6 +1853,7 @@ function App() {
|
||||
return (
|
||||
<LedgerScreen
|
||||
copy={copy()}
|
||||
locale={locale()}
|
||||
dashboard={dashboard()}
|
||||
readyIsAdmin={effectiveIsAdmin()}
|
||||
adminMembers={adminSettings()?.members ?? []}
|
||||
@@ -1875,7 +1921,14 @@ function App() {
|
||||
)
|
||||
}))
|
||||
}
|
||||
onOpenAddPayment={() => setAddingPaymentOpen(true)}
|
||||
onOpenAddPayment={() => {
|
||||
setPaymentForm((current) => ({
|
||||
...current,
|
||||
memberId: current.memberId.trim() || defaultPaymentMemberId(),
|
||||
currency: adminSettings()?.settings.settlementCurrency ?? current.currency
|
||||
}))
|
||||
setAddingPaymentOpen(true)
|
||||
}}
|
||||
onCloseAddPayment={() => setAddingPaymentOpen(false)}
|
||||
onAddPayment={handleAddPayment}
|
||||
onPaymentFormMemberChange={(value) =>
|
||||
@@ -1936,6 +1989,7 @@ function App() {
|
||||
return (
|
||||
<HouseScreen
|
||||
copy={copy()}
|
||||
locale={locale()}
|
||||
readyIsAdmin={effectiveIsAdmin()}
|
||||
householdDefaultLocale={readySession()?.member.householdDefaultLocale ?? 'en'}
|
||||
dashboard={dashboard()}
|
||||
@@ -2219,6 +2273,7 @@ function App() {
|
||||
return (
|
||||
<HomeScreen
|
||||
copy={copy()}
|
||||
locale={locale()}
|
||||
dashboard={dashboard()}
|
||||
currentMemberLine={currentMemberLine()}
|
||||
utilityTotalMajor={utilityTotalMajor()}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { Show } from 'solid-js'
|
||||
|
||||
import { cn } from '../../lib/cn'
|
||||
import { formatFriendlyDate } from '../../lib/dates'
|
||||
import { majorStringToMinor, sumMajorStrings } from '../../lib/money'
|
||||
import type { MiniAppDashboard } from '../../miniapp-api'
|
||||
import { MiniChip, StatCard } from '../ui'
|
||||
|
||||
type Props = {
|
||||
copy: Record<string, string | undefined>
|
||||
locale: 'en' | 'ru'
|
||||
dashboard: MiniAppDashboard
|
||||
member: MiniAppDashboard['members'][number]
|
||||
detail?: boolean
|
||||
@@ -123,7 +125,8 @@ export function MemberBalanceCard(props: Props) {
|
||||
<Show when={props.dashboard.rentFxEffectiveDate}>
|
||||
{(date) => (
|
||||
<MiniChip muted>
|
||||
{props.copy.fxEffectiveDateLabel ?? ''}: {date()}
|
||||
{props.copy.fxEffectiveDateLabel ?? ''}:{' '}
|
||||
{formatFriendlyDate(date(), props.locale)}
|
||||
</MiniChip>
|
||||
)}
|
||||
</Show>
|
||||
|
||||
@@ -124,3 +124,15 @@ export function XIcon(props: IconProps) {
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function TrashIcon(props: IconProps) {
|
||||
return (
|
||||
<svg {...iconProps(props)}>
|
||||
<path d="M4 7h16" />
|
||||
<path d="M10 11v6" />
|
||||
<path d="M14 11v6" />
|
||||
<path d="M6 7l1 13a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2l1-13" />
|
||||
<path d="M9 4h6l1 3H8l1-3Z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ export const dictionary = {
|
||||
loadingBody: 'Validating Telegram session and membership…',
|
||||
loadingBadge: 'Secure session',
|
||||
demoBadge: 'Demo mode',
|
||||
liveBadge: 'Live household',
|
||||
liveBadge: 'Connected home',
|
||||
joinTitle: 'Welcome to your household',
|
||||
joinBody:
|
||||
'You are not a member of {household} yet. Send a join request and wait for admin approval.',
|
||||
@@ -134,11 +134,11 @@ export const dictionary = {
|
||||
paymentAmount: 'Payment amount',
|
||||
paymentMember: 'Member',
|
||||
paymentSaveAction: 'Save payment',
|
||||
paymentDeleteAction: 'Delete payment',
|
||||
paymentDeleteAction: 'Delete',
|
||||
paymentEditorBody: 'Review the payment record in one focused editor.',
|
||||
deletingPayment: 'Deleting payment…',
|
||||
purchaseSaveAction: 'Save purchase',
|
||||
purchaseDeleteAction: 'Delete purchase',
|
||||
purchaseDeleteAction: 'Delete',
|
||||
deletingPurchase: 'Deleting purchase…',
|
||||
savingPurchase: 'Saving purchase…',
|
||||
editEntryAction: 'Edit entry',
|
||||
@@ -189,7 +189,7 @@ export const dictionary = {
|
||||
addUtilityBillAction: 'Add utility bill',
|
||||
savingUtilityBill: 'Saving utility bill…',
|
||||
saveUtilityBillAction: 'Save utility bill',
|
||||
deleteUtilityBillAction: 'Delete utility bill',
|
||||
deleteUtilityBillAction: 'Delete',
|
||||
deletingUtilityBill: 'Deleting utility bill…',
|
||||
utilityBillsEmpty: 'No utility bills recorded for this cycle yet.',
|
||||
rentAmount: 'Rent amount',
|
||||
@@ -270,7 +270,7 @@ export const dictionary = {
|
||||
loadingBody: 'Проверяем Telegram-сессию и членство…',
|
||||
loadingBadge: 'Защищённая сессия',
|
||||
demoBadge: 'Демо режим',
|
||||
liveBadge: 'Живой household',
|
||||
liveBadge: 'Подключённый дом',
|
||||
joinTitle: 'Добро пожаловать домой',
|
||||
joinBody:
|
||||
'Ты пока не участник {household}. Отправь заявку на вступление и дождись подтверждения админа.',
|
||||
@@ -397,11 +397,11 @@ export const dictionary = {
|
||||
paymentAmount: 'Сумма оплаты',
|
||||
paymentMember: 'Участник',
|
||||
paymentSaveAction: 'Сохранить оплату',
|
||||
paymentDeleteAction: 'Удалить оплату',
|
||||
paymentDeleteAction: 'Удалить',
|
||||
paymentEditorBody: 'Проверь оплату в отдельном редакторе.',
|
||||
deletingPayment: 'Удаляем оплату…',
|
||||
purchaseSaveAction: 'Сохранить покупку',
|
||||
purchaseDeleteAction: 'Удалить покупку',
|
||||
purchaseDeleteAction: 'Удалить',
|
||||
deletingPurchase: 'Удаляем покупку…',
|
||||
savingPurchase: 'Сохраняем покупку…',
|
||||
editEntryAction: 'Редактировать запись',
|
||||
@@ -450,7 +450,7 @@ export const dictionary = {
|
||||
addUtilityBillAction: 'Добавить коммунальный счёт',
|
||||
savingUtilityBill: 'Сохраняем счёт…',
|
||||
saveUtilityBillAction: 'Сохранить счёт',
|
||||
deleteUtilityBillAction: 'Удалить счёт',
|
||||
deleteUtilityBillAction: 'Удалить',
|
||||
deletingUtilityBill: 'Удаляем счёт…',
|
||||
utilityBillsEmpty: 'Для этого цикла пока нет коммунальных счетов.',
|
||||
rentAmount: 'Сумма аренды',
|
||||
|
||||
38
apps/miniapp/src/lib/dates.ts
Normal file
38
apps/miniapp/src/lib/dates.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import type { Locale } from '../i18n'
|
||||
|
||||
function localeTag(locale: Locale): string {
|
||||
return locale === 'ru' ? 'ru-RU' : 'en-US'
|
||||
}
|
||||
|
||||
export function formatFriendlyDate(value: string, locale: Locale): string {
|
||||
const date = new Date(value)
|
||||
if (Number.isNaN(date.getTime())) {
|
||||
return value
|
||||
}
|
||||
|
||||
const includeYear = date.getUTCFullYear() !== new Date().getUTCFullYear()
|
||||
|
||||
return new Intl.DateTimeFormat(localeTag(locale), {
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
...(includeYear ? { year: 'numeric' } : {})
|
||||
}).format(date)
|
||||
}
|
||||
|
||||
export function formatCyclePeriod(period: string, locale: Locale): string {
|
||||
const [yearValue, monthValue] = period.split('-')
|
||||
const year = Number.parseInt(yearValue ?? '', 10)
|
||||
const month = Number.parseInt(monthValue ?? '', 10)
|
||||
|
||||
if (!Number.isInteger(year) || !Number.isInteger(month) || month < 1 || month > 12) {
|
||||
return period
|
||||
}
|
||||
|
||||
const date = new Date(Date.UTC(year, month - 1, 1))
|
||||
const includeYear = year !== new Date().getUTCFullYear()
|
||||
|
||||
return new Intl.DateTimeFormat(localeTag(locale), {
|
||||
month: 'long',
|
||||
...(includeYear ? { year: 'numeric' } : {})
|
||||
}).format(date)
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
import { Show } from 'solid-js'
|
||||
|
||||
import { MemberBalanceCard } from '../components/finance/member-balance-card'
|
||||
import { formatCyclePeriod } from '../lib/dates'
|
||||
import type { MiniAppDashboard } from '../miniapp-api'
|
||||
|
||||
type Props = {
|
||||
copy: Record<string, string | undefined>
|
||||
locale: 'en' | 'ru'
|
||||
dashboard: MiniAppDashboard | null
|
||||
currentMemberLine: MiniAppDashboard['members'][number] | null
|
||||
}
|
||||
@@ -25,6 +27,7 @@ export function BalancesScreen(props: Props) {
|
||||
{(member) => (
|
||||
<MemberBalanceCard
|
||||
copy={props.copy}
|
||||
locale={props.locale}
|
||||
dashboard={dashboard()}
|
||||
member={member()}
|
||||
detail
|
||||
@@ -34,7 +37,7 @@ export function BalancesScreen(props: Props) {
|
||||
<article class="balance-item balance-item--muted">
|
||||
<header>
|
||||
<strong>{props.copy.balanceScreenScopeTitle ?? ''}</strong>
|
||||
<span>{dashboard().period}</span>
|
||||
<span>{formatCyclePeriod(dashboard().period, props.locale)}</span>
|
||||
</header>
|
||||
<p>{props.copy.balanceScreenScopeBody ?? ''}</p>
|
||||
</article>
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { Show } from 'solid-js'
|
||||
|
||||
import { FinanceSummaryCards } from '../components/finance/finance-summary-cards'
|
||||
import { formatCyclePeriod } from '../lib/dates'
|
||||
import { sumMajorStrings } from '../lib/money'
|
||||
import type { MiniAppDashboard } from '../miniapp-api'
|
||||
|
||||
type Props = {
|
||||
copy: Record<string, string | undefined>
|
||||
locale: 'en' | 'ru'
|
||||
dashboard: MiniAppDashboard | null
|
||||
currentMemberLine: MiniAppDashboard['members'][number] | null
|
||||
utilityTotalMajor: string
|
||||
@@ -85,7 +87,7 @@ export function HomeScreen(props: Props) {
|
||||
</article>
|
||||
<article class="stat-card balance-spotlight__stat">
|
||||
<span>{props.copy.currentCycleLabel ?? ''}</span>
|
||||
<strong>{dashboard().period}</strong>
|
||||
<strong>{formatCyclePeriod(dashboard().period, props.locale)}</strong>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
@@ -122,7 +124,7 @@ export function HomeScreen(props: Props) {
|
||||
<article class="balance-item balance-item--wide balance-item--muted">
|
||||
<header>
|
||||
<strong>{props.copy.houseSnapshotTitle ?? ''}</strong>
|
||||
<span>{dashboard().period}</span>
|
||||
<span>{formatCyclePeriod(dashboard().period, props.locale)}</span>
|
||||
</header>
|
||||
<p>{props.copy.houseSnapshotBody ?? ''}</p>
|
||||
<div class="summary-card-grid summary-card-grid--secondary">
|
||||
|
||||
@@ -9,8 +9,10 @@ import {
|
||||
Modal,
|
||||
PencilIcon,
|
||||
PlusIcon,
|
||||
SettingsIcon
|
||||
SettingsIcon,
|
||||
TrashIcon
|
||||
} from '../components/ui'
|
||||
import { formatCyclePeriod, formatFriendlyDate } from '../lib/dates'
|
||||
import type {
|
||||
MiniAppAdminCycleState,
|
||||
MiniAppAdminSettingsPayload,
|
||||
@@ -50,6 +52,7 @@ type CycleForm = {
|
||||
|
||||
type Props = {
|
||||
copy: Record<string, string | undefined>
|
||||
locale: 'en' | 'ru'
|
||||
readyIsAdmin: boolean
|
||||
householdDefaultLocale: 'en' | 'ru'
|
||||
dashboard: MiniAppDashboard | null
|
||||
@@ -226,7 +229,9 @@ export function HouseScreen(props: Props) {
|
||||
<header>
|
||||
<strong>{props.copy.billingCycleTitle ?? ''}</strong>
|
||||
<span>
|
||||
{props.cycleState?.cycle?.period ?? props.copy.billingCycleEmpty ?? ''}
|
||||
{props.cycleState?.cycle?.period
|
||||
? formatCyclePeriod(props.cycleState.cycle.period, props.locale)
|
||||
: (props.copy.billingCycleEmpty ?? '')}
|
||||
</span>
|
||||
</header>
|
||||
<p>
|
||||
@@ -565,7 +570,7 @@ export function HouseScreen(props: Props) {
|
||||
<div class="ledger-compact-card__main">
|
||||
<header>
|
||||
<strong>{bill.billName}</strong>
|
||||
<span>{bill.createdAt.slice(0, 10)}</span>
|
||||
<span>{formatFriendlyDate(bill.createdAt, props.locale)}</span>
|
||||
</header>
|
||||
<p>{props.copy.utilityCategoryName ?? ''}</p>
|
||||
<div class="ledger-compact-card__meta">
|
||||
@@ -714,6 +719,7 @@ export function HouseScreen(props: Props) {
|
||||
variant="danger"
|
||||
onClick={() => void props.onDeleteUtilityBill(bill.id)}
|
||||
>
|
||||
<TrashIcon />
|
||||
{props.deletingUtilityBillId === bill.id
|
||||
? props.copy.deletingUtilityBill
|
||||
: props.copy.deleteUtilityBillAction}
|
||||
@@ -1085,7 +1091,7 @@ export function HouseScreen(props: Props) {
|
||||
resolvedPolicy.effectiveFromPeriod
|
||||
? (props.copy.absencePolicyEffectiveFrom ?? '').replace(
|
||||
'{period}',
|
||||
resolvedPolicy.effectiveFromPeriod
|
||||
formatCyclePeriod(resolvedPolicy.effectiveFromPeriod, props.locale)
|
||||
)
|
||||
: (props.copy.absencePolicyHint ?? '')
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { For, Show } from 'solid-js'
|
||||
|
||||
import { Button, Field, IconButton, Modal, PencilIcon } from '../components/ui'
|
||||
import { Button, Field, IconButton, Modal, PencilIcon, PlusIcon, TrashIcon } from '../components/ui'
|
||||
import { formatFriendlyDate } from '../lib/dates'
|
||||
import type { MiniAppAdminSettingsPayload, MiniAppDashboard } from '../miniapp-api'
|
||||
|
||||
type PurchaseDraft = {
|
||||
@@ -23,6 +24,7 @@ type PaymentDraft = {
|
||||
|
||||
type Props = {
|
||||
copy: Record<string, string | undefined>
|
||||
locale: 'en' | 'ru'
|
||||
dashboard: MiniAppDashboard | null
|
||||
readyIsAdmin: boolean
|
||||
adminMembers: readonly MiniAppAdminSettingsPayload['members'][number][]
|
||||
@@ -147,7 +149,11 @@ export function LedgerScreen(props: Props) {
|
||||
<div class="ledger-compact-card__main">
|
||||
<header>
|
||||
<strong>{entry.title}</strong>
|
||||
<span>{entry.occurredAt?.slice(0, 10) ?? '—'}</span>
|
||||
<span>
|
||||
{entry.occurredAt
|
||||
? formatFriendlyDate(entry.occurredAt, props.locale)
|
||||
: '—'}
|
||||
</span>
|
||||
</header>
|
||||
<p>{entry.actorDisplayName ?? props.copy.ledgerActorFallback ?? ''}</p>
|
||||
<div class="ledger-compact-card__meta">
|
||||
@@ -194,6 +200,7 @@ export function LedgerScreen(props: Props) {
|
||||
return (
|
||||
<div class="modal-action-row">
|
||||
<Button variant="danger" onClick={() => void props.onDeletePurchase(entry.id)}>
|
||||
<TrashIcon />
|
||||
{props.deletingPurchaseId === entry.id
|
||||
? props.copy.deletingPurchase
|
||||
: props.copy.purchaseDeleteAction}
|
||||
@@ -394,6 +401,7 @@ export function LedgerScreen(props: Props) {
|
||||
<p>{props.copy.paymentsAdminBody ?? ''}</p>
|
||||
<div class="panel-toolbar">
|
||||
<Button variant="secondary" onClick={props.onOpenAddPayment}>
|
||||
<PlusIcon />
|
||||
{props.copy.paymentsAddAction ?? ''}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -408,7 +416,11 @@ export function LedgerScreen(props: Props) {
|
||||
<div class="ledger-compact-card__main">
|
||||
<header>
|
||||
<strong>{props.paymentMemberName(entry)}</strong>
|
||||
<span>{entry.occurredAt?.slice(0, 10) ?? '—'}</span>
|
||||
<span>
|
||||
{entry.occurredAt
|
||||
? formatFriendlyDate(entry.occurredAt, props.locale)
|
||||
: '—'}
|
||||
</span>
|
||||
</header>
|
||||
<p>{props.ledgerTitle(entry)}</p>
|
||||
<div class="ledger-compact-card__meta">
|
||||
@@ -449,7 +461,11 @@ export function LedgerScreen(props: Props) {
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
disabled={props.addingPayment || props.paymentForm.amountMajor.trim().length === 0}
|
||||
disabled={
|
||||
props.addingPayment ||
|
||||
props.paymentForm.memberId.trim().length === 0 ||
|
||||
props.paymentForm.amountMajor.trim().length === 0
|
||||
}
|
||||
onClick={() => void props.onAddPayment()}
|
||||
>
|
||||
{props.addingPayment ? props.copy.addingPayment : props.copy.paymentsAddAction}
|
||||
@@ -514,6 +530,7 @@ export function LedgerScreen(props: Props) {
|
||||
return (
|
||||
<div class="modal-action-row">
|
||||
<Button variant="danger" onClick={() => void props.onDeletePayment(entry.id)}>
|
||||
<TrashIcon />
|
||||
{props.deletingPaymentId === entry.id
|
||||
? props.copy.deletingPayment
|
||||
: props.copy.paymentDeleteAction}
|
||||
|
||||
Reference in New Issue
Block a user