diff --git a/apps/miniapp/src/contexts/dashboard-context.tsx b/apps/miniapp/src/contexts/dashboard-context.tsx index caa274a..0f7e54a 100644 --- a/apps/miniapp/src/contexts/dashboard-context.tsx +++ b/apps/miniapp/src/contexts/dashboard-context.tsx @@ -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(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) { diff --git a/apps/miniapp/src/contexts/session-context.tsx b/apps/miniapp/src/contexts/session-context.tsx index 42e65ae..bf7793b 100644 --- a/apps/miniapp/src/contexts/session-context.tsx +++ b/apps/miniapp/src/contexts/session-context.tsx @@ -62,6 +62,9 @@ type SessionContextValue = { handleMemberLocaleChange: (nextLocale: Locale) => Promise handleHouseholdLocaleChange: (nextLocale: Locale) => Promise refreshHouseholdData: (includeAdmin?: boolean, forceRefresh?: boolean) => Promise + registerRefreshListener: ( + listener: (initData: string, isAdmin: boolean) => Promise + ) => () => void } const SessionContext = createContext() @@ -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>() + + function registerRefreshListener( + listener: (initData: string, isAdmin: boolean) => Promise + ) { + 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} diff --git a/apps/miniapp/src/i18n.ts b/apps/miniapp/src/i18n.ts index d0fbf80..6e56a1b 100644 --- a/apps/miniapp/src/i18n.ts +++ b/apps/miniapp/src/i18n.ts @@ -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: diff --git a/apps/miniapp/src/index.css b/apps/miniapp/src/index.css index bcc88d7..e785ac2 100644 --- a/apps/miniapp/src/index.css +++ b/apps/miniapp/src/index.css @@ -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 { diff --git a/apps/miniapp/src/routes/home.tsx b/apps/miniapp/src/routes/home.tsx index 748b326..120eaef 100644 --- a/apps/miniapp/src/routes/home.tsx +++ b/apps/miniapp/src/routes/home.tsx @@ -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={

{copy().latestActivityEmpty}

} >
- + {(entry) => (
{entry.title} @@ -147,6 +148,25 @@ export default function HomeRoute() { )}
+ 5}> + +
diff --git a/apps/miniapp/src/theme.css b/apps/miniapp/src/theme.css index 525c807..bbb3874 100644 --- a/apps/miniapp/src/theme.css +++ b/apps/miniapp/src/theme.css @@ -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;