fix: refine mobile financial workflows
This commit is contained in:
@@ -146,8 +146,8 @@
|
||||
"archiveConfirmBody": "Account “{{name}}” will leave working lists but remain in history."
|
||||
},
|
||||
"operations": {
|
||||
"title": "Operations",
|
||||
"subtitle": "Expenses, income, transfers and refunds.",
|
||||
"title": "Ledger",
|
||||
"subtitle": "Every expense, income, transfer and refund in one traceable sequence.",
|
||||
"new": "New operation",
|
||||
"empty": "No operations match the selected filters.",
|
||||
"search": "Search",
|
||||
@@ -187,8 +187,8 @@
|
||||
"nextPage": "Next"
|
||||
},
|
||||
"budget": {
|
||||
"title": "Budget",
|
||||
"subtitle": "Month plan, funding and the general pool.",
|
||||
"title": "Monthly plan",
|
||||
"subtitle": "Plan every ruble, then fund the month from the real pool.",
|
||||
"monthPicker": "Month picker",
|
||||
"month": "Month",
|
||||
"prevMonth": "Previous month",
|
||||
@@ -329,8 +329,8 @@
|
||||
"revertBody": "Operations created by the “{{fileName}}” import will be removed atomically; affected months will require reopening."
|
||||
},
|
||||
"analytics": {
|
||||
"title": "Analytics",
|
||||
"subtitle": "Four fixed reports for your space.",
|
||||
"title": "Financial picture",
|
||||
"subtitle": "Spending, plan accuracy, cash flow and balances without noise.",
|
||||
"range": "Period:",
|
||||
"months_one": "{{count}} month",
|
||||
"months_other": "{{count}} months",
|
||||
@@ -352,8 +352,8 @@
|
||||
"unallocated": "Unallocated"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Settings & data",
|
||||
"subtitle": "Profile, export and space data controls.",
|
||||
"title": "Space & data",
|
||||
"subtitle": "Identity, language, export and lifecycle controls for this financial space.",
|
||||
"profile": "Profile",
|
||||
"timezone": "Time zone",
|
||||
"dataTitle": "Data",
|
||||
|
||||
@@ -146,8 +146,8 @@
|
||||
"archiveConfirmBody": "Счёт «{{name}}» скроется из рабочих списков, но останется в истории."
|
||||
},
|
||||
"operations": {
|
||||
"title": "Операции",
|
||||
"subtitle": "Расходы, доходы, переводы и возвраты.",
|
||||
"title": "Гроссбух",
|
||||
"subtitle": "Расходы, доходы, переводы и возвраты в одной проверяемой последовательности.",
|
||||
"new": "Новая операция",
|
||||
"empty": "Операций по выбранным условиям не найдено.",
|
||||
"search": "Поиск",
|
||||
@@ -187,8 +187,8 @@
|
||||
"nextPage": "Дальше"
|
||||
},
|
||||
"budget": {
|
||||
"title": "Бюджет",
|
||||
"subtitle": "План месяца, обеспечение и общий пул.",
|
||||
"title": "План месяца",
|
||||
"subtitle": "Спланируйте каждый рубль и обеспечьте месяц из реального пула.",
|
||||
"monthPicker": "Выбор месяца",
|
||||
"month": "Месяц",
|
||||
"prevMonth": "Предыдущий месяц",
|
||||
@@ -329,8 +329,8 @@
|
||||
"revertBody": "Операции, созданные импортом «{{fileName}}», будут удалены атомарно; затронутые месяцы потребуют переоткрытия."
|
||||
},
|
||||
"analytics": {
|
||||
"title": "Аналитика",
|
||||
"subtitle": "Четыре фиксированных отчёта по вашему пространству.",
|
||||
"title": "Финансовая картина",
|
||||
"subtitle": "Расходы, точность плана, денежный поток и балансы без шума.",
|
||||
"range": "Период:",
|
||||
"months_one": "{{count}} месяц",
|
||||
"months_few": "{{count}} месяца",
|
||||
@@ -354,8 +354,8 @@
|
||||
"unallocated": "Без распределения"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Настройки и данные",
|
||||
"subtitle": "Профиль, экспорт и управление данными пространства.",
|
||||
"title": "Пространство и данные",
|
||||
"subtitle": "Профиль, язык, экспорт и жизненный цикл финансового пространства.",
|
||||
"profile": "Профиль",
|
||||
"timezone": "Часовой пояс",
|
||||
"dataTitle": "Данные",
|
||||
|
||||
@@ -246,22 +246,22 @@ export function ImportDetailPage() {
|
||||
</thead>
|
||||
<tbody>
|
||||
{data.rows.map((row) => (
|
||||
<tr key={row.id}>
|
||||
<th scope="row" data-label={t('common.date')}>
|
||||
<tr key={row.id} className="statement-row">
|
||||
<th scope="row" data-label={t('common.date')} className="statement-date">
|
||||
{formatDate(row.occurredOn, locale)}
|
||||
</th>
|
||||
<td data-label={t('operations.description')}>
|
||||
<td data-label={t('operations.description')} className="statement-description">
|
||||
{row.description ?? '—'}
|
||||
{row.payeeHint ? <span className="muted"> · {row.payeeHint}</span> : null}
|
||||
</td>
|
||||
<td
|
||||
data-label={t('common.amount')}
|
||||
className={`num ${row.direction === 'in' ? 'pos' : 'neg'}`}
|
||||
className={`statement-amount num ${row.direction === 'in' ? 'pos' : 'neg'}`}
|
||||
>
|
||||
{row.direction === 'in' ? '+' : '−'}
|
||||
{formatMoney(row.amountKopecks, locale)}
|
||||
</td>
|
||||
<td data-label={t('imports.rowWarnings')}>
|
||||
<td data-label={t('imports.rowWarnings')} className="statement-warnings">
|
||||
{row.warnings.length === 0 ? (
|
||||
'—'
|
||||
) : (
|
||||
@@ -274,7 +274,7 @@ export function ImportDetailPage() {
|
||||
</ul>
|
||||
)}
|
||||
</td>
|
||||
<td data-label={t('imports.decision')}>
|
||||
<td data-label={t('imports.decision')} className="statement-decision">
|
||||
<select
|
||||
className="input select"
|
||||
aria-label={t('imports.decision')}
|
||||
|
||||
@@ -623,10 +623,10 @@ export function OperationsPage() {
|
||||
key={operation.id}
|
||||
data-voided={operation.status === 'voided' ? 'true' : undefined}
|
||||
>
|
||||
<th scope="row" data-label={t('common.date')}>
|
||||
<th scope="row" data-label={t('common.date')} className="operation-date">
|
||||
{formatDate(operation.occurredOn, locale)}
|
||||
</th>
|
||||
<td data-label={t('operations.kindLabel')}>
|
||||
<td data-label={t('operations.kindLabel')} className="operation-kind">
|
||||
<Badge tone={OPERATION_KIND_TONES[operation.kind]}>
|
||||
{t(`operations.kind.${operation.kind}`)}
|
||||
</Badge>
|
||||
@@ -634,22 +634,22 @@ export function OperationsPage() {
|
||||
<Badge tone="neutral">{t('operations.status.voided')}</Badge>
|
||||
) : null}
|
||||
</td>
|
||||
<td data-label={t('operations.description')}>
|
||||
<td data-label={t('operations.description')} className="operation-description">
|
||||
{operation.payeeName ?? operation.description ?? '—'}
|
||||
</td>
|
||||
<td data-label={t('operations.account')}>
|
||||
<td data-label={t('operations.account')} className="operation-account">
|
||||
{accountName(operation.accountId)}
|
||||
{operation.kind === 'transfer'
|
||||
? ` → ${accountName(operation.counterpartyAccountId)}`
|
||||
: ''}
|
||||
</td>
|
||||
<td data-label={t('operations.category')}>
|
||||
<td data-label={t('operations.category')} className="operation-category">
|
||||
{operation.categoryName ?? t('operations.noCategory')}
|
||||
</td>
|
||||
<td data-label={t('common.amount')} className={`num ${amount.tone}`}>
|
||||
<td data-label={t('common.amount')} className={`operation-amount num ${amount.tone}`}>
|
||||
{amount.text}
|
||||
</td>
|
||||
<td data-label={t('common.actions')}>
|
||||
<td data-label={t('common.actions')} className="operation-actions">
|
||||
{operation.status === 'confirmed' ? (
|
||||
<Button variant="subtle" onClick={() => setVoidTarget(operation)}>
|
||||
{t('operations.void')}
|
||||
|
||||
@@ -792,6 +792,40 @@ input[type='file']::file-selector-button { min-height: 30px; margin-right: 10px;
|
||||
.data-table tbody td:nth-child(odd) { justify-content: flex-end; text-align: right; }
|
||||
.data-table tbody td:last-child { grid-column: 1 / -1; justify-content: flex-end; }
|
||||
.data-table tbody td .inline-actions { width: 100%; }
|
||||
.view-operations .data-table tbody tr {
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
grid-template-areas:
|
||||
'date kind'
|
||||
'description description'
|
||||
'account category'
|
||||
'amount actions';
|
||||
column-gap: 14px;
|
||||
}
|
||||
.view-operations .operation-date { grid-area: date; grid-column: auto; }
|
||||
.view-operations .operation-kind { grid-area: kind; justify-content: flex-end; }
|
||||
.view-operations .operation-description { grid-area: description; display: block; padding-block: 8px; font-size: 12px; line-height: 1.4; text-align: left; }
|
||||
.view-operations .operation-account { grid-area: account; justify-content: flex-start; }
|
||||
.view-operations .operation-category { grid-area: category; justify-content: flex-end; text-align: right; }
|
||||
.view-operations .operation-amount { grid-area: amount; justify-content: flex-start; text-align: left; }
|
||||
.view-operations .operation-actions { grid-area: actions; grid-column: auto; justify-content: flex-end; }
|
||||
.view-operations .operation-description::before { display: block; margin-bottom: 3px; }
|
||||
.view-imports .statement-date,
|
||||
.view-imports .statement-amount { grid-column: auto; }
|
||||
.view-imports .statement-date { grid-area: statement-date; }
|
||||
.view-imports .statement-amount { grid-area: statement-amount; justify-content: flex-end; }
|
||||
.view-imports .statement-description { grid-area: statement-description; display: block; padding-block: 8px; font-size: 12px; line-height: 1.4; text-align: left; }
|
||||
.view-imports .statement-warnings { grid-area: statement-warnings; }
|
||||
.view-imports .statement-decision { grid-area: statement-decision; grid-column: 1 / -1; }
|
||||
.view-imports .statement-description::before { display: block; margin-bottom: 3px; }
|
||||
.view-imports .statement-row {
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
grid-template-areas:
|
||||
'statement-date statement-amount'
|
||||
'statement-description statement-description'
|
||||
'statement-warnings statement-warnings'
|
||||
'statement-decision statement-decision';
|
||||
column-gap: 14px;
|
||||
}
|
||||
.analytics-grid { display: block; border: 0; }
|
||||
.analytics-grid .card { padding: 20px 0; border-width: 0 0 1px; background: transparent; }
|
||||
.hbar-row { grid-template-columns: minmax(70px, 34%) 1fr minmax(64px, auto); }
|
||||
|
||||
Reference in New Issue
Block a user