diff --git a/app/src/app/styles/index.css b/app/src/app/styles/index.css index ae5153d..5641b67 100644 --- a/app/src/app/styles/index.css +++ b/app/src/app/styles/index.css @@ -38,6 +38,11 @@ --color-info: #6366f1; --color-info-soft: #eef2ff; + + /* Animations */ + --animate-modal-in: modal-in 0.2s cubic-bezier(0.16, 1, 0.3, 1); + --animate-backdrop-in: backdrop-in 0.15s ease-out; + --animate-drawer-in-right: drawer-in-right 0.25s cubic-bezier(0.16, 1, 0.3, 1); } /* Dark theme overrides */ @@ -105,3 +110,54 @@ select, textarea { font: inherit; } + +@keyframes modal-in { + from { + opacity: 0; + transform: scale(0.97) translateY(8px); + } + to { + opacity: 1; + transform: scale(1) translateY(0); + } +} + +@keyframes backdrop-in { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes drawer-in-right { + from { + opacity: 0; + transform: translateX(20px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +@keyframes shimmer { + from { + background-position: 200% center; + } + to { + background-position: -200% center; + } +} + +@utility animate-shimmer { + animation: shimmer 1.6s linear infinite; + background-image: linear-gradient( + 90deg, + var(--color-border) 0%, + var(--color-surface-hover) 50%, + var(--color-border) 100% + ); + background-size: 200% 100%; +} diff --git a/app/src/features/create-transaction/ui/CreateTransactionModal.tsx b/app/src/features/create-transaction/ui/CreateTransactionModal.tsx index 131c9ad..bcace78 100644 --- a/app/src/features/create-transaction/ui/CreateTransactionModal.tsx +++ b/app/src/features/create-transaction/ui/CreateTransactionModal.tsx @@ -11,7 +11,7 @@ import { DatePicker } from '@shared/ui/date-picker'; import { Input } from '@shared/ui/input'; import { localStorageService } from '@shared/lib/storage/local-storage.service'; import { Modal } from '@shared/ui/modal'; -import { Select } from '@shared/ui/select'; +import { cn } from '@shared/lib/classnames/cn'; import { mapFormToTransaction } from '../lib/map-form-to-transaction'; import { createTransactionFormSchema } from '../model/transaction-form.schema'; @@ -144,6 +144,16 @@ export function CreateTransactionModal({ ); const [formError, setFormError] = useState(null); + function switchKind(kind: 'income' | 'expense') { + setValues({ + ...values, + categoryId: selectedCategory?.kind === kind ? values.categoryId : '', + categoryName: selectedCategory?.kind === kind ? values.categoryName : '', + kind, + }); + setSelectedCategory((current) => (current?.kind === kind ? current : null)); + } + function selectCategory(category: Category) { setSelectedCategory(category); setValues({ @@ -222,28 +232,32 @@ export function CreateTransactionModal({ {visibleError} ) : null} - updateFilters({ container })} options={[ { label: 'All containers', value: '' }, - ...containerOptions.map((container) => ({ label: container, value: container })), + ...containerOptions.map((container) => ({ + label: container, + value: container, + })), ]} value={filters.container} /> @@ -427,6 +400,8 @@ export function TransactionsHistory({ ) : null} + + {/* List */} {isInitialLoading ? ( ) : !filteredTransactions.length ? ( @@ -438,72 +413,79 @@ export function TransactionsHistory({ title="No matching transactions" /> ) : ( -
-
- Date - Type - Category - Amount - Payment method - Comment - Container - Status - Actions -
-
- {filteredTransactions.map((transaction) => ( +
+ {filteredTransactions.map((transaction) => { + const isDeleted = Boolean(transaction.deletedAt); + const isIncome = transaction.kind === 'income'; + + const metaParts = [ + transaction.date, + transaction.paymentMethod, + transaction.comment, + containersEnabled ? transaction.containerName : undefined, + ] + .filter(Boolean) + .join(' · '); + + return (
- -
{transaction.date}
-
- -
{formatKind(transaction.kind)}
-
- -
{transaction.categoryName}
-
- -
+ {transaction.categoryName.charAt(0)} +
+ +
+

+ {transaction.categoryName} +

+ {metaParts ? ( +

{metaParts}

+ ) : null} +
+ +
+ {formatMoney(transaction.signedAmount, transaction.currency)} -
-
- -
{transaction.paymentMethod || '-'}
-
- - - - -
{transaction.containerName || '-'}
-
- -
- {transaction.deletedAt ? Deleted : null} + +
+ {isDeleted ? Deleted : null} {formatSyncStatus(transaction.syncStatus)}
- +
+ onDeleteTransaction(transaction.id)} onEdit={() => onEditTransaction(transaction)} />
- ))} -
+ ); + })}
)}