miniapp: refresh after mutations, activity expand, squash chart palette

This commit is contained in:
2026-03-14 02:40:14 +04:00
parent 1274cefc0f
commit 771d64aa4e
6 changed files with 100 additions and 13 deletions

View File

@@ -1,4 +1,11 @@
import { createContext, createMemo, createSignal, useContext, type ParentProps } from 'solid-js'
import {
createContext,
createMemo,
createSignal,
onCleanup,
useContext,
type ParentProps
} from 'solid-js'
import { majorStringToMinor, minorToMajorString } from '../lib/money'
import {
@@ -51,7 +58,14 @@ export type CycleFormState = {
utilityAmountMajor: string
}
const chartPalette = ['#3ecf8e', '#6fd3c0', '#94a8ff', '#f06a8d', '#f3d36f', '#7dc96d'] as const
const chartPalette = [
'var(--chart-1)',
'var(--chart-2)',
'var(--chart-3)',
'var(--chart-4)',
'var(--chart-5)',
'var(--chart-6)'
] as const
type DashboardContextValue = {
dashboard: () => MiniAppDashboard | null
@@ -224,7 +238,7 @@ function computePurchaseInvestmentChart(
/* ── Provider ───────────────────────────────────────── */
export function DashboardProvider(props: ParentProps) {
const { readySession } = useSession()
const { readySession, registerRefreshListener } = useSession()
const { copy } = useI18n()
const [dashboard, setDashboard] = createSignal<MiniAppDashboard | null>(null)
@@ -275,6 +289,9 @@ export function DashboardProvider(props: ParentProps) {
computePurchaseInvestmentChart(dashboard(), purchaseLedger(), copy().ledgerActorFallback)
)
const unregisterDashboardRefreshListener = registerRefreshListener(loadDashboardData)
onCleanup(unregisterDashboardRefreshListener)
async function loadDashboardData(initData: string, isAdmin: boolean) {
// In demo mode, use demo data
if (!initData) {

View File

@@ -62,6 +62,9 @@ type SessionContextValue = {
handleMemberLocaleChange: (nextLocale: Locale) => Promise<void>
handleHouseholdLocaleChange: (nextLocale: Locale) => Promise<void>
refreshHouseholdData: (includeAdmin?: boolean, forceRefresh?: boolean) => Promise<void>
registerRefreshListener: (
listener: (initData: string, isAdmin: boolean) => Promise<void>
) => () => void
}
const SessionContext = createContext<SessionContextValue>()
@@ -114,6 +117,17 @@ export function SessionProvider(
const [displayNameDraft, setDisplayNameDraft] = createSignal('')
const [savingOwnDisplayName, setSavingOwnDisplayName] = createSignal(false)
const refreshListeners = new Set<(initData: string, isAdmin: boolean) => Promise<void>>()
function registerRefreshListener(
listener: (initData: string, isAdmin: boolean) => Promise<void>
) {
refreshListeners.add(listener)
return () => {
refreshListeners.delete(listener)
}
}
const readySession = () => {
const current = session()
return current.status === 'ready' ? current : null
@@ -310,7 +324,11 @@ export function SessionProvider(
// Delegate actual data loading to dashboard context via onReady
const current = readySession()
if (current) {
await props.onReady?.(data, includeAdmin || current.member.isAdmin)
const isAdmin = includeAdmin || current.member.isAdmin
await Promise.all([
props.onReady?.(data, isAdmin),
...Array.from(refreshListeners).map((l) => l(data, isAdmin))
])
}
}
@@ -336,7 +354,8 @@ export function SessionProvider(
handleSaveOwnDisplayName,
handleMemberLocaleChange,
handleHouseholdLocaleChange,
refreshHouseholdData
refreshHouseholdData,
registerRefreshListener
}}
>
{props.children}

View File

@@ -126,6 +126,8 @@ export const dictionary = {
emptyDashboard: 'No billing cycle is ready yet.',
latestActivityTitle: 'Latest activity',
latestActivityEmpty: 'Recent utility and purchase entries will appear here.',
showMoreAction: 'Show more',
showLessAction: 'Show less',
testingViewBadge: 'Testing view',
testingSurfaceTitle: 'Hidden QA view',
testingSurfaceBody:
@@ -427,6 +429,8 @@ export const dictionary = {
emptyDashboard: 'Пока нет готового расчётного цикла.',
latestActivityTitle: 'Последняя активность',
latestActivityEmpty: 'Здесь появятся последние коммунальные платежи и покупки.',
showMoreAction: 'Показать ещё',
showLessAction: 'Свернуть',
testingViewBadge: 'Тестовый вид',
testingSurfaceTitle: 'Скрытый QA режим',
testingSurfaceBody:

View File

@@ -1137,6 +1137,33 @@ a {
font-variant-numeric: tabular-nums;
}
.activity-card__show-more {
display: flex;
align-items: center;
justify-content: center;
gap: var(--spacing-xs);
width: 100%;
padding: var(--spacing-sm);
margin-top: var(--spacing-xs);
background: none;
border: none;
border-radius: var(--radius-md);
color: var(--accent);
font-size: var(--text-xs);
font-weight: 600;
cursor: pointer;
transition: background var(--transition-fast);
}
.activity-card__show-more:hover {
background: var(--bg-secondary);
}
.activity-card__show-more:active {
background: var(--bg-tertiary);
transform: translateY(1px);
}
/* ── Balances Route ───────────────────────────────────── */
.route--balances {

View File

@@ -1,5 +1,5 @@
import { Show, For } from 'solid-js'
import { Clock } from 'lucide-solid'
import { Show, For, createSignal } from 'solid-js'
import { Clock, ChevronDown, ChevronUp } from 'lucide-solid'
import { useSession } from '../contexts/session-context'
import { useI18n } from '../contexts/i18n-context'
@@ -13,6 +13,7 @@ export default function HomeRoute() {
const { readySession } = useSession()
const { copy } = useI18n()
const { dashboard, currentMemberLine } = useDashboard()
const [showAllActivity, setShowAllActivity] = createSignal(false)
function dueStatusBadge() {
const data = dashboard()
@@ -138,7 +139,7 @@ export default function HomeRoute() {
fallback={<p class="empty-state">{copy().latestActivityEmpty}</p>}
>
<div class="activity-card__list">
<For each={data().ledger.slice(0, 5)}>
<For each={showAllActivity() ? data().ledger : data().ledger.slice(0, 5)}>
{(entry) => (
<div class="activity-card__item">
<span class="activity-card__title">{entry.title}</span>
@@ -147,6 +148,25 @@ export default function HomeRoute() {
)}
</For>
</div>
<Show when={data().ledger.length > 5}>
<button
class="activity-card__show-more"
onClick={() => setShowAllActivity(!showAllActivity())}
>
<Show
when={showAllActivity()}
fallback={
<>
<span>{copy().showMoreAction}</span>
<ChevronDown size={14} />
</>
}
>
<span>{copy().showLessAction}</span>
<ChevronUp size={14} />
</Show>
</button>
</Show>
</Show>
</div>
</Card>

View File

@@ -76,11 +76,11 @@
/* ── Chart palette ──────────────────────────────────── */
--chart-1: #3ecf8e;
--chart-2: #6fd3c0;
--chart-3: #94a8ff;
--chart-4: #f06a8d;
--chart-5: #f3d36f;
--chart-6: #7dc96d;
--chart-2: #8a90ff;
--chart-3: #da8c00;
--chart-4: #2fb8c9;
--chart-5: #c26cff;
--chart-6: #ff5c9a;
/* ── Status colors ──────────────────────────────────── */
--status-credit: #4ade80;