From 523b5144d8ad569e8326be0d421bbe2b647e6499 Mon Sep 17 00:00:00 2001 From: whekin Date: Wed, 11 Mar 2026 20:57:44 +0400 Subject: [PATCH] refactor(miniapp): simplify dashboard layout and controls --- apps/miniapp/src/App.tsx | 68 +++---- .../miniapp/src/components/layout/top-bar.tsx | 2 +- apps/miniapp/src/components/ui/dialog.tsx | 6 +- apps/miniapp/src/components/ui/icons.tsx | 9 + apps/miniapp/src/index.css | 186 +++++++++--------- apps/miniapp/src/screens/home-screen.tsx | 56 +----- apps/miniapp/src/screens/house-screen.tsx | 88 ++------- 7 files changed, 148 insertions(+), 267 deletions(-) diff --git a/apps/miniapp/src/App.tsx b/apps/miniapp/src/App.tsx index 3cc8807..75543b8 100644 --- a/apps/miniapp/src/App.tsx +++ b/apps/miniapp/src/App.tsx @@ -1,4 +1,4 @@ -import { Match, Switch, createMemo, createSignal, onMount } from 'solid-js' +import { Match, Show, Switch, createMemo, createSignal, onMount } from 'solid-js' import { dictionary, type Locale } from './i18n' import { @@ -38,10 +38,8 @@ import { type MiniAppDashboard, type MiniAppPendingMember } from './miniapp-api' -import { Button, Field, Modal } from './components/ui' -import { HeroBanner } from './components/layout/hero-banner' +import { Button, Field, MiniChip, Modal } from './components/ui' import { NavigationTabs } from './components/layout/navigation-tabs' -import { ProfileCard } from './components/layout/profile-card' import { TopBar } from './components/layout/top-bar' import { BlockedState } from './components/session/blocked-state' import { LoadingState } from './components/session/loading-state' @@ -2337,10 +2335,7 @@ function App() { currentMemberLine={currentMemberLine()} utilityTotalMajor={utilityTotalMajor()} purchaseTotalMajor={purchaseTotalMajor()} - memberBalanceVisuals={memberBalanceVisuals()} - purchaseChart={purchaseInvestmentChart()} memberBaseDueMajor={memberBaseDueMajor} - memberRemainingClass={memberRemainingClass} ledgerTitle={ledgerTitle} ledgerPrimaryAmount={ledgerPrimaryAmount} ledgerSecondaryAmount={ledgerSecondaryAmount} @@ -2426,25 +2421,30 @@ function App() { - setProfileEditorOpen(true) - } - : undefined - } - /> +
+
+ + {readySession()?.mode === 'demo' ? copy().demoBadge : copy().liveBadge} + + + {readySession()?.member.isAdmin ? copy().adminTag : copy().residentTag} + + + {readySession()?.member.status + ? memberStatusLabel(readySession()!.member.status) + : copy().memberStatusActive} + +
+ + + +
-
- -
{renderPanel()}
-
+
{renderPanel()}
-
+

{props.subtitle}

{props.title}

diff --git a/apps/miniapp/src/components/ui/dialog.tsx b/apps/miniapp/src/components/ui/dialog.tsx index 49810f1..b054c66 100644 --- a/apps/miniapp/src/components/ui/dialog.tsx +++ b/apps/miniapp/src/components/ui/dialog.tsx @@ -1,6 +1,8 @@ import * as Dialog from '@kobalte/core/dialog' import { Show, type JSX, type ParentProps } from 'solid-js' +import { XIcon } from './icons' + export function Modal( props: ParentProps<{ open: boolean @@ -24,8 +26,8 @@ export function Modal( {(description) => {description()}}
- - + + {props.closeLabel} diff --git a/apps/miniapp/src/components/ui/icons.tsx b/apps/miniapp/src/components/ui/icons.tsx index da334e2..5081e18 100644 --- a/apps/miniapp/src/components/ui/icons.tsx +++ b/apps/miniapp/src/components/ui/icons.tsx @@ -65,3 +65,12 @@ export function GlobeIcon(props: IconProps) { ) } + +export function XIcon(props: IconProps) { + return ( + + + + + ) +} diff --git a/apps/miniapp/src/index.css b/apps/miniapp/src/index.css index 6ba9d35..39978cb 100644 --- a/apps/miniapp/src/index.css +++ b/apps/miniapp/src/index.css @@ -82,7 +82,12 @@ button { display: flex; align-items: flex-start; justify-content: space-between; - gap: 16px; + gap: 12px; + margin-bottom: 10px; +} + +.topbar__copy { + min-width: 0; } .topbar h1, @@ -94,7 +99,8 @@ button { } .topbar h1 { - font-size: clamp(2rem, 5vw, 3rem); + font-size: clamp(1.55rem, 4.4vw, 2.35rem); + line-height: 0.96; } .eyebrow { @@ -117,14 +123,15 @@ button { .locale-switch--compact { justify-items: end; min-width: 0; + align-self: flex-start; } .locale-switch__buttons { display: inline-grid; grid-auto-flow: column; align-items: center; - gap: 6px; - padding: 4px; + gap: 4px; + padding: 3px; border: 1px solid rgb(255 255 255 / 0.1); border-radius: 999px; background: rgb(255 255 255 / 0.04); @@ -135,8 +142,8 @@ button { display: inline-flex; align-items: center; justify-content: center; - width: 30px; - height: 30px; + width: 24px; + height: 24px; color: rgb(247 179 137 / 0.88); } @@ -154,13 +161,17 @@ button { .locale-switch__buttons button { border-radius: 999px; - min-width: 42px; - padding: 7px 10px; - font-size: 0.78rem; + min-width: 36px; + padding: 6px 8px; + font-size: 0.72rem; font-weight: 700; letter-spacing: 0.04em; } +.locale-switch__buttons--inline { + margin-top: 6px; +} + .locale-switch__buttons button.is-active, .nav-grid button.is-active { border-color: rgb(247 179 137 / 0.7); @@ -263,15 +274,22 @@ button { } .ui-button--primary { - border-color: rgb(247 179 137 / 0.42); - background: rgb(247 179 137 / 0.16); + border-color: rgb(247 179 137 / 0.52); + background: rgb(247 179 137 / 0.18); color: #fff4ea; } -.ui-button--secondary, +.ui-button--secondary { + background: rgb(255 255 255 / 0.04); +} + .ui-button--ghost, .ui-button--icon { - background: rgb(255 255 255 / 0.04); + background: transparent; +} + +.ui-button--ghost { + border-color: rgb(255 255 255 / 0.08); } .ui-button--danger { @@ -296,13 +314,19 @@ button { .nav-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); - gap: 10px; - margin-top: 18px; + gap: 6px; + margin-top: 12px; + padding: 4px; + border: 1px solid rgb(255 255 255 / 0.08); + border-radius: 18px; + background: rgb(255 255 255 / 0.03); } .nav-grid button { - border-radius: 18px; - padding: 14px 8px; + border: none; + border-radius: 14px; + min-height: 38px; + padding: 9px 10px; } .content-grid { @@ -314,12 +338,13 @@ button { .content-stack { display: grid; gap: 12px; + margin-top: 12px; } .summary-card-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 12px; + gap: 10px; } .panel { @@ -342,9 +367,11 @@ button { .stat-card, .activity-row, .utility-bill-row { + display: grid; + gap: 10px; border: 1px solid rgb(255 255 255 / 0.08); border-radius: 18px; - padding: 14px; + padding: 16px; background: rgb(255 255 255 / 0.03); } @@ -367,7 +394,7 @@ button { grid-template-columns: minmax(0, 1fr) auto; align-items: start; gap: 12px; - margin-bottom: 10px; + margin-bottom: 0; } .balance-item strong, @@ -382,7 +409,7 @@ button { .ledger-item p, .activity-row p, .utility-bill-row p { - margin-top: 6px; + margin: 0; } .balance-status.is-credit { @@ -409,7 +436,6 @@ button { .balance-breakdown { display: grid; gap: 10px; - margin-top: 12px; } .member-visual-list { @@ -596,32 +622,8 @@ button { margin-top: 12px; } -.section-switch { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 8px; -} - -.section-switch button { - border: 1px solid rgb(255 255 255 / 0.12); - border-radius: 16px; - min-height: 44px; - padding: 10px 12px; - background: rgb(255 255 255 / 0.04); - color: inherit; -} - -.section-switch button.is-active { - border-color: rgb(247 179 137 / 0.7); - background: rgb(247 179 137 / 0.14); -} - .admin-layout { - gap: 18px; -} - -.admin-hero { - gap: 16px; + gap: 12px; } .admin-summary-grid, @@ -636,24 +638,6 @@ button { gap: 12px; } -.admin-section__header { - display: flex; - align-items: end; - justify-content: space-between; - gap: 12px; -} - -.admin-section__header h3 { - margin: 0; - font-family: 'Space Grotesk', 'IBM Plex Sans', sans-serif; - letter-spacing: -0.04em; - font-size: 1.15rem; -} - -.admin-section__header p { - margin-top: 6px; -} - .admin-sublist { margin-top: 12px; } @@ -728,10 +712,9 @@ button { .panel-toolbar { display: flex; flex-wrap: wrap; - justify-content: flex-end; + justify-content: flex-start; gap: 10px; - margin-top: 14px; - margin-bottom: 14px; + margin-top: 8px; } .ledger-compact-card { @@ -806,7 +789,7 @@ button { overflow: auto; border: 1px solid rgb(255 255 255 / 0.1); border-radius: 24px; - padding: 18px; + padding: 16px; background: linear-gradient(180deg, rgb(255 255 255 / 0.07), rgb(255 255 255 / 0.03)), rgb(18 26 36 / 0.96); box-shadow: 0 28px 80px rgb(0 0 0 / 0.35); @@ -815,7 +798,7 @@ button { .modal-sheet__header { display: grid; grid-template-columns: minmax(0, 1fr) auto; - gap: 12px; + gap: 10px; align-items: start; } @@ -827,17 +810,25 @@ button { } .modal-sheet__header p { - margin-top: 8px; + margin-top: 6px; } .modal-sheet__body { display: grid; - gap: 16px; - margin-top: 18px; + gap: 14px; + margin-top: 14px; } .modal-sheet__footer { - margin-top: 18px; + margin-top: 14px; +} + +.modal-close-button { + align-self: start; + width: 36px; + min-width: 36px; + min-height: 36px; + border-color: rgb(255 255 255 / 0.08); } .editor-grid { @@ -952,6 +943,29 @@ button { overflow-wrap: anywhere; } +.balance-item > p + .ledger-list, +.balance-item > p + .balance-list { + margin-top: 4px; +} + +.app-context-row { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + gap: 10px; +} + +.app-context-meta { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.app-context-row__action { + margin-left: auto; +} + @media (min-width: 760px) { .shell { max-width: 920px; @@ -976,10 +990,6 @@ button { align-self: stretch; } - .admin-summary-grid { - grid-template-columns: repeat(4, minmax(0, 1fr)); - } - .admin-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } @@ -999,10 +1009,6 @@ button { .balance-item--wide { grid-column: 1 / -1; } - - .section-switch { - grid-template-columns: repeat(4, minmax(0, 1fr)); - } } @media (min-width: 980px) { @@ -1025,21 +1031,17 @@ button { } .topbar { - flex-direction: column; + flex-direction: row; + align-items: flex-start; } .locale-switch { - justify-items: stretch; - width: 100%; + width: auto; min-width: 0; } .locale-switch--compact { - justify-items: start; - } - - .locale-switch__buttons { - justify-self: start; + justify-items: end; } .nav-grid { @@ -1054,10 +1056,6 @@ button { grid-template-columns: minmax(0, 1fr); } - .admin-section__header { - align-items: start; - } - .activity-row header, .ledger-compact-card header, .ledger-item header, diff --git a/apps/miniapp/src/screens/home-screen.tsx b/apps/miniapp/src/screens/home-screen.tsx index 900ae72..c15b072 100644 --- a/apps/miniapp/src/screens/home-screen.tsx +++ b/apps/miniapp/src/screens/home-screen.tsx @@ -1,7 +1,6 @@ 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 = { @@ -12,32 +11,7 @@ type Props = { 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 @@ -91,17 +65,7 @@ export function HomeScreen(props: Props) { - -
- {props.copy.overviewTitle ?? ''} -
-

{props.copy.overviewBody ?? ''}

- - } - > + {(member) => (
@@ -110,7 +74,6 @@ export function HomeScreen(props: Props) { {member().remainingMajor} {props.dashboard!.currency}
-

{props.copy.yourBalanceBody ?? ''}

{props.copy.shareRent ?? ''}: {props.dashboard!.rentSourceAmountMajor}{' '} {props.dashboard!.rentSourceCurrency} @@ -154,23 +117,6 @@ export function HomeScreen(props: Props) { )} - -

{props.copy.latestActivityTitle ?? ''} diff --git a/apps/miniapp/src/screens/house-screen.tsx b/apps/miniapp/src/screens/house-screen.tsx index 7000da7..99c14bf 100644 --- a/apps/miniapp/src/screens/house-screen.tsx +++ b/apps/miniapp/src/screens/house-screen.tsx @@ -10,6 +10,7 @@ import { PlusIcon, SettingsIcon } from '../components/ui' +import { NavigationTabs } from '../components/layout/navigation-tabs' import type { MiniAppAdminCycleState, MiniAppAdminSettingsPayload, @@ -175,63 +176,21 @@ export function HouseScreen(props: Props) { return (
-
-
- {props.copy.householdSettingsTitle ?? ''} - {props.adminSettings?.settings.settlementCurrency ?? '—'} -
-

{props.copy.householdSettingsBody ?? ''}

-
-
- {props.copy.billingCycleTitle ?? ''} - {props.cycleState?.cycle?.period ?? props.copy.billingCycleEmpty ?? ''} -
-
- {props.copy.settlementCurrency ?? ''} - {props.adminSettings?.settings.settlementCurrency ?? '—'} -
-
- {props.copy.membersCount ?? ''} - {String(props.adminSettings?.members.length ?? 0)} -
-
- {props.copy.pendingRequests ?? ''} - {String(props.pendingMembers.length)} -
-
-
- -
- - {([key, label]) => ( - - )} - -
+
-
-
-

{props.copy.billingCycleTitle ?? ''}

-

{props.copy.billingSettingsTitle ?? ''}

-
-
@@ -310,8 +269,7 @@ export function HouseScreen(props: Props) { {props.copy.householdLanguage ?? ''} {props.householdDefaultLocale.toUpperCase()}
-

{props.copy.householdSettingsBody ?? ''}

-
+