mirror of
https://github.com/whekin/household-bot.git
synced 2026-03-31 13:54:02 +00:00
refactor(miniapp): remove placeholder shell cards
This commit is contained in:
@@ -107,6 +107,14 @@ function joinDeepLink(): string | null {
|
|||||||
return `https://t.me/${context.botUsername}?start=join_${encodeURIComponent(context.joinToken)}`
|
return `https://t.me/${context.botUsername}?start=join_${encodeURIComponent(context.joinToken)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function dashboardMemberCount(dashboard: MiniAppDashboard | null): string {
|
||||||
|
return dashboard ? String(dashboard.members.length) : '—'
|
||||||
|
}
|
||||||
|
|
||||||
|
function dashboardLedgerCount(dashboard: MiniAppDashboard | null): string {
|
||||||
|
return dashboard ? String(dashboard.ledger.length) : '—'
|
||||||
|
}
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [locale, setLocale] = createSignal<Locale>('en')
|
const [locale, setLocale] = createSignal<Locale>('en')
|
||||||
const [session, setSession] = createSignal<SessionState>({
|
const [session, setSession] = createSignal<SessionState>({
|
||||||
@@ -479,6 +487,12 @@ function App() {
|
|||||||
case 'house':
|
case 'house':
|
||||||
return readySession()?.member.isAdmin ? (
|
return readySession()?.member.isAdmin ? (
|
||||||
<div class="balance-list">
|
<div class="balance-list">
|
||||||
|
<article class="balance-item">
|
||||||
|
<header>
|
||||||
|
<strong>{copy().householdSettingsTitle}</strong>
|
||||||
|
</header>
|
||||||
|
<p>{copy().householdSettingsBody}</p>
|
||||||
|
</article>
|
||||||
<article class="balance-item">
|
<article class="balance-item">
|
||||||
<header>
|
<header>
|
||||||
<strong>{copy().householdLanguage}</strong>
|
<strong>{copy().householdLanguage}</strong>
|
||||||
@@ -544,22 +558,75 @@ function App() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
copy().houseEmpty
|
<div class="balance-list">
|
||||||
|
<article class="balance-item">
|
||||||
|
<header>
|
||||||
|
<strong>{copy().residentHouseTitle}</strong>
|
||||||
|
</header>
|
||||||
|
<p>{copy().residentHouseBody}</p>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<ShowDashboard
|
<div class="home-grid">
|
||||||
dashboard={dashboard()}
|
<article class="stat-card">
|
||||||
fallback={<p>{copy().summaryBody}</p>}
|
<span>{copy().totalDue}</span>
|
||||||
render={(data) => (
|
<strong>
|
||||||
<>
|
{dashboard() ? `${dashboard()!.totalDueMajor} ${dashboard()!.currency}` : '—'}
|
||||||
<p>
|
</strong>
|
||||||
{copy().totalDue}: {data.totalDueMajor} {data.currency}
|
</article>
|
||||||
</p>
|
<article class="stat-card">
|
||||||
<p>{copy().summaryBody}</p>
|
<span>{copy().membersCount}</span>
|
||||||
</>
|
<strong>{dashboardMemberCount(dashboard())}</strong>
|
||||||
)}
|
</article>
|
||||||
/>
|
<article class="stat-card">
|
||||||
|
<span>{copy().ledgerEntries}</span>
|
||||||
|
<strong>{dashboardLedgerCount(dashboard())}</strong>
|
||||||
|
</article>
|
||||||
|
{readySession()?.member.isAdmin ? (
|
||||||
|
<article class="stat-card">
|
||||||
|
<span>{copy().pendingRequests}</span>
|
||||||
|
<strong>{String(pendingMembers().length)}</strong>
|
||||||
|
</article>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
<article class="balance-item">
|
||||||
|
<header>
|
||||||
|
<strong>{copy().overviewTitle}</strong>
|
||||||
|
</header>
|
||||||
|
<p>{copy().overviewBody}</p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="balance-item">
|
||||||
|
<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="ledger-list">
|
||||||
|
{data.ledger.slice(0, 3).map((entry) => (
|
||||||
|
<article class="ledger-item">
|
||||||
|
<header>
|
||||||
|
<strong>{entry.title}</strong>
|
||||||
|
<span>
|
||||||
|
{entry.amountMajor} {data.currency}
|
||||||
|
</span>
|
||||||
|
</header>
|
||||||
|
<p>{entry.actorDisplayName ?? 'Household'}</p>
|
||||||
|
</article>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -601,7 +668,7 @@ function App() {
|
|||||||
<Switch>
|
<Switch>
|
||||||
<Match when={session().status === 'loading'}>
|
<Match when={session().status === 'loading'}>
|
||||||
<section class="hero-card">
|
<section class="hero-card">
|
||||||
<span class="pill">{copy().navHint}</span>
|
<span class="pill">{copy().loadingBadge}</span>
|
||||||
<h2>{copy().loadingTitle}</h2>
|
<h2>{copy().loadingTitle}</h2>
|
||||||
<p>{copy().loadingBody}</p>
|
<p>{copy().loadingBody}</p>
|
||||||
</section>
|
</section>
|
||||||
@@ -609,7 +676,7 @@ function App() {
|
|||||||
|
|
||||||
<Match when={session().status === 'blocked'}>
|
<Match when={session().status === 'blocked'}>
|
||||||
<section class="hero-card">
|
<section class="hero-card">
|
||||||
<span class="pill">{copy().navHint}</span>
|
<span class="pill">{copy().loadingBadge}</span>
|
||||||
<h2>
|
<h2>
|
||||||
{blockedSession()?.reason === 'telegram_only'
|
{blockedSession()?.reason === 'telegram_only'
|
||||||
? copy().telegramOnlyTitle
|
? copy().telegramOnlyTitle
|
||||||
@@ -628,7 +695,7 @@ function App() {
|
|||||||
|
|
||||||
<Match when={session().status === 'onboarding'}>
|
<Match when={session().status === 'onboarding'}>
|
||||||
<section class="hero-card">
|
<section class="hero-card">
|
||||||
<span class="pill">{copy().navHint}</span>
|
<span class="pill">{copy().loadingBadge}</span>
|
||||||
<h2>
|
<h2>
|
||||||
{onboardingSession()?.mode === 'pending'
|
{onboardingSession()?.mode === 'pending'
|
||||||
? copy().pendingTitle
|
? copy().pendingTitle
|
||||||
@@ -681,7 +748,7 @@ function App() {
|
|||||||
<section class="hero-card">
|
<section class="hero-card">
|
||||||
<div class="hero-card__meta">
|
<div class="hero-card__meta">
|
||||||
<span class="pill">
|
<span class="pill">
|
||||||
{readySession()?.mode === 'demo' ? copy().demoBadge : copy().navHint}
|
{readySession()?.mode === 'demo' ? copy().demoBadge : copy().liveBadge}
|
||||||
</span>
|
</span>
|
||||||
<span class="pill pill--muted">
|
<span class="pill pill--muted">
|
||||||
{readySession()?.member.isAdmin ? copy().adminTag : copy().residentTag}
|
{readySession()?.member.isAdmin ? copy().adminTag : copy().residentTag}
|
||||||
@@ -692,7 +759,7 @@ function App() {
|
|||||||
{copy().welcome},{' '}
|
{copy().welcome},{' '}
|
||||||
{readySession()?.telegramUser.firstName ?? readySession()?.member.displayName}
|
{readySession()?.telegramUser.firstName ?? readySession()?.member.displayName}
|
||||||
</h2>
|
</h2>
|
||||||
<p>{copy().sectionBody}</p>
|
<p>{copy().overviewBody}</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<nav class="nav-grid">
|
<nav class="nav-grid">
|
||||||
@@ -716,25 +783,10 @@ function App() {
|
|||||||
|
|
||||||
<section class="content-grid">
|
<section class="content-grid">
|
||||||
<article class="panel panel--wide">
|
<article class="panel panel--wide">
|
||||||
<p class="eyebrow">{copy().summaryTitle}</p>
|
<p class="eyebrow">{copy().overviewTitle}</p>
|
||||||
<h3>{readySession()?.member.displayName}</h3>
|
<h3>{readySession()?.member.displayName}</h3>
|
||||||
<div>{renderPanel()}</div>
|
<div>{renderPanel()}</div>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
<article class="panel">
|
|
||||||
<p class="eyebrow">{copy().cardAccess}</p>
|
|
||||||
<p>{copy().cardAccessBody}</p>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<article class="panel">
|
|
||||||
<p class="eyebrow">{copy().cardLocale}</p>
|
|
||||||
<p>{copy().cardLocaleBody}</p>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<article class="panel">
|
|
||||||
<p class="eyebrow">{copy().cardNext}</p>
|
|
||||||
<p>{copy().cardNextBody}</p>
|
|
||||||
</article>
|
|
||||||
</section>
|
</section>
|
||||||
</Match>
|
</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ export const dictionary = {
|
|||||||
appSubtitle: 'Shared home dashboard',
|
appSubtitle: 'Shared home dashboard',
|
||||||
loadingTitle: 'Checking your household access',
|
loadingTitle: 'Checking your household access',
|
||||||
loadingBody: 'Validating Telegram session and membership…',
|
loadingBody: 'Validating Telegram session and membership…',
|
||||||
|
loadingBadge: 'Secure session',
|
||||||
demoBadge: 'Demo mode',
|
demoBadge: 'Demo mode',
|
||||||
|
liveBadge: 'Live household',
|
||||||
joinTitle: 'Welcome to your household',
|
joinTitle: 'Welcome to your household',
|
||||||
joinBody:
|
joinBody:
|
||||||
'You are not a member of {household} yet. Send a join request and wait for admin approval.',
|
'You are not a member of {household} yet. Send a join request and wait for admin approval.',
|
||||||
@@ -33,28 +35,28 @@ export const dictionary = {
|
|||||||
balances: 'Balances',
|
balances: 'Balances',
|
||||||
ledger: 'Ledger',
|
ledger: 'Ledger',
|
||||||
house: 'House',
|
house: 'House',
|
||||||
navHint: 'Shell v1',
|
|
||||||
welcome: 'Welcome back',
|
welcome: 'Welcome back',
|
||||||
adminTag: 'Admin',
|
adminTag: 'Admin',
|
||||||
residentTag: 'Resident',
|
residentTag: 'Resident',
|
||||||
summaryTitle: 'Current shell',
|
overviewTitle: 'Current cycle',
|
||||||
summaryBody:
|
overviewBody:
|
||||||
'Balances, ledger, and house wiki will land in the next tickets. This shell focuses on verified access, navigation, and mobile layout.',
|
'Use the sections below to review balances, ledger entries, and household access.',
|
||||||
totalDue: 'Total due',
|
totalDue: 'Total due',
|
||||||
|
membersCount: 'Members',
|
||||||
|
ledgerEntries: 'Ledger entries',
|
||||||
|
pendingRequests: 'Pending requests',
|
||||||
shareRent: 'Rent',
|
shareRent: 'Rent',
|
||||||
shareUtilities: 'Utilities',
|
shareUtilities: 'Utilities',
|
||||||
shareOffset: 'Shared buys',
|
shareOffset: 'Shared buys',
|
||||||
ledgerTitle: 'Included ledger',
|
ledgerTitle: 'Included ledger',
|
||||||
emptyDashboard: 'No billing cycle is ready yet.',
|
emptyDashboard: 'No billing cycle is ready yet.',
|
||||||
cardAccess: 'Access',
|
latestActivityTitle: 'Latest activity',
|
||||||
cardAccessBody: 'Telegram identity verified and matched to a household member.',
|
latestActivityEmpty: 'Recent utility and purchase entries will appear here.',
|
||||||
cardLocale: 'Locale',
|
householdSettingsTitle: 'Household settings',
|
||||||
cardLocaleBody: 'Switch RU/EN immediately without reloading the shell.',
|
householdSettingsBody: 'Control household defaults and approve roommates who requested access.',
|
||||||
cardNext: 'Next up',
|
residentHouseTitle: 'Household access',
|
||||||
cardNextBody: 'Balances, ledger, and house pages will plug into this navigation.',
|
residentHouseBody:
|
||||||
sectionTitle: 'Ready for the next features',
|
'Your admins manage household settings and approvals here. You can still switch your own language above.',
|
||||||
sectionBody:
|
|
||||||
'This layout is intentionally narrow and mobile-first so it behaves well inside the Telegram webview.',
|
|
||||||
pendingMembersTitle: 'Pending members',
|
pendingMembersTitle: 'Pending members',
|
||||||
pendingMembersBody:
|
pendingMembersBody:
|
||||||
'Approve roommates here after they request access from the group join flow.',
|
'Approve roommates here after they request access from the group join flow.',
|
||||||
@@ -64,14 +66,16 @@ export const dictionary = {
|
|||||||
pendingMemberHandle: '@{username}',
|
pendingMemberHandle: '@{username}',
|
||||||
balancesEmpty: 'Balances will appear here once the dashboard API lands.',
|
balancesEmpty: 'Balances will appear here once the dashboard API lands.',
|
||||||
ledgerEmpty: 'Ledger entries will appear here after the finance view is connected.',
|
ledgerEmpty: 'Ledger entries will appear here after the finance view is connected.',
|
||||||
houseEmpty: 'House rules, Wi-Fi info, and practical notes will live here.'
|
houseEmpty: 'Household details will appear here.'
|
||||||
},
|
},
|
||||||
ru: {
|
ru: {
|
||||||
appTitle: 'Kojori House',
|
appTitle: 'Kojori House',
|
||||||
appSubtitle: 'Панель общего дома',
|
appSubtitle: 'Панель общего дома',
|
||||||
loadingTitle: 'Проверяем доступ к дому',
|
loadingTitle: 'Проверяем доступ к дому',
|
||||||
loadingBody: 'Проверяем Telegram-сессию и членство…',
|
loadingBody: 'Проверяем Telegram-сессию и членство…',
|
||||||
|
loadingBadge: 'Защищённая сессия',
|
||||||
demoBadge: 'Демо режим',
|
demoBadge: 'Демо режим',
|
||||||
|
liveBadge: 'Живой household',
|
||||||
joinTitle: 'Добро пожаловать домой',
|
joinTitle: 'Добро пожаловать домой',
|
||||||
joinBody:
|
joinBody:
|
||||||
'Ты пока не участник {household}. Отправь заявку на вступление и дождись подтверждения админа.',
|
'Ты пока не участник {household}. Отправь заявку на вступление и дождись подтверждения админа.',
|
||||||
@@ -98,28 +102,27 @@ export const dictionary = {
|
|||||||
balances: 'Баланс',
|
balances: 'Баланс',
|
||||||
ledger: 'Леджер',
|
ledger: 'Леджер',
|
||||||
house: 'Дом',
|
house: 'Дом',
|
||||||
navHint: 'Shell v1',
|
|
||||||
welcome: 'С возвращением',
|
welcome: 'С возвращением',
|
||||||
adminTag: 'Админ',
|
adminTag: 'Админ',
|
||||||
residentTag: 'Житель',
|
residentTag: 'Житель',
|
||||||
summaryTitle: 'Текущая оболочка',
|
overviewTitle: 'Текущий цикл',
|
||||||
summaryBody:
|
overviewBody: 'Ниже можно посмотреть балансы, записи леджера и доступ к household.',
|
||||||
'Баланс, леджер и вики дома появятся в следующих тикетах. Сейчас приоритет — проверенный доступ, навигация и мобильный layout.',
|
|
||||||
totalDue: 'Итого к оплате',
|
totalDue: 'Итого к оплате',
|
||||||
|
membersCount: 'Участники',
|
||||||
|
ledgerEntries: 'Записи леджера',
|
||||||
|
pendingRequests: 'Ожидают подтверждения',
|
||||||
shareRent: 'Аренда',
|
shareRent: 'Аренда',
|
||||||
shareUtilities: 'Коммуналка',
|
shareUtilities: 'Коммуналка',
|
||||||
shareOffset: 'Общие покупки',
|
shareOffset: 'Общие покупки',
|
||||||
ledgerTitle: 'Вошедшие операции',
|
ledgerTitle: 'Вошедшие операции',
|
||||||
emptyDashboard: 'Пока нет готового billing cycle.',
|
emptyDashboard: 'Пока нет готового billing cycle.',
|
||||||
cardAccess: 'Доступ',
|
latestActivityTitle: 'Последняя активность',
|
||||||
cardAccessBody: 'Telegram-личность подтверждена и сопоставлена с участником household.',
|
latestActivityEmpty: 'Здесь появятся последние коммунальные платежи и покупки.',
|
||||||
cardLocale: 'Локаль',
|
householdSettingsTitle: 'Настройки household',
|
||||||
cardLocaleBody: 'RU/EN переключаются сразу, без перезагрузки.',
|
householdSettingsBody: 'Здесь можно менять язык household и подтверждать новых соседей.',
|
||||||
cardNext: 'Дальше',
|
residentHouseTitle: 'Доступ к household',
|
||||||
cardNextBody: 'Баланс, леджер и страницы дома подключатся к этой навигации.',
|
residentHouseBody:
|
||||||
sectionTitle: 'Основа готова для следующих функций',
|
'Настройки household и подтверждение заявок управляются админами. Свой язык можно менять переключателем выше.',
|
||||||
sectionBody:
|
|
||||||
'Этот layout специально сделан узким и mobile-first, чтобы хорошо жить внутри Telegram webview.',
|
|
||||||
pendingMembersTitle: 'Ожидающие участники',
|
pendingMembersTitle: 'Ожидающие участники',
|
||||||
pendingMembersBody:
|
pendingMembersBody:
|
||||||
'Подтверждай соседей здесь после того, как они отправят заявку через кнопку подключения.',
|
'Подтверждай соседей здесь после того, как они отправят заявку через кнопку подключения.',
|
||||||
@@ -129,6 +132,6 @@ export const dictionary = {
|
|||||||
pendingMemberHandle: '@{username}',
|
pendingMemberHandle: '@{username}',
|
||||||
balancesEmpty: 'Баланс появится здесь, когда подключим dashboard API.',
|
balancesEmpty: 'Баланс появится здесь, когда подключим dashboard API.',
|
||||||
ledgerEmpty: 'Записи леджера появятся здесь после подключения finance view.',
|
ledgerEmpty: 'Записи леджера появятся здесь после подключения finance view.',
|
||||||
houseEmpty: 'Правила дома, Wi-Fi и полезные инструкции будут здесь.'
|
houseEmpty: 'Детали household появятся здесь.'
|
||||||
}
|
}
|
||||||
} satisfies Record<Locale, Record<string, string>>
|
} satisfies Record<Locale, Record<string, string>>
|
||||||
|
|||||||
@@ -228,13 +228,15 @@ button {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.balance-list,
|
.balance-list,
|
||||||
.ledger-list {
|
.ledger-list,
|
||||||
|
.home-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.balance-item,
|
.balance-item,
|
||||||
.ledger-item {
|
.ledger-item,
|
||||||
|
.stat-card {
|
||||||
border: 1px solid rgb(255 255 255 / 0.08);
|
border: 1px solid rgb(255 255 255 / 0.08);
|
||||||
border-radius: 18px;
|
border-radius: 18px;
|
||||||
padding: 14px;
|
padding: 14px;
|
||||||
@@ -260,6 +262,27 @@ button {
|
|||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.home-grid {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
display: grid;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card span {
|
||||||
|
color: #c6c2bb;
|
||||||
|
font-size: 0.82rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card strong {
|
||||||
|
font-family: 'Space Grotesk', 'IBM Plex Sans', sans-serif;
|
||||||
|
font-size: clamp(1.2rem, 4vw, 1.7rem);
|
||||||
|
}
|
||||||
|
|
||||||
.panel--wide {
|
.panel--wide {
|
||||||
min-height: 170px;
|
min-height: 170px;
|
||||||
}
|
}
|
||||||
@@ -275,6 +298,10 @@ button {
|
|||||||
grid-template-columns: 1.3fr 1fr 1fr;
|
grid-template-columns: 1.3fr 1fr 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.home-grid {
|
||||||
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
.panel--wide {
|
.panel--wide {
|
||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user