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