feat: add loading overlay and spinner to PositionGroupTable

This commit is contained in:
Sergey Krylov 2026-06-18 06:57:53 +03:00
parent c9dec60d4b
commit 1fc50c018a

View File

@ -100,7 +100,7 @@ function PositionGroupTable({
const [cursor, setCursor] = useState<string | undefined>(undefined);
const query = group.type ? { type: group.type, limit: 10, cursor } : { limit: 100, cursor };
const { data: page, isLoading } = useBrokerPositions(accountId, query);
const { data: page, isLoading, isFetching } = useBrokerPositions(accountId, query);
const rawPositions = page?.items ?? [];
const positions = group.type
@ -148,10 +148,17 @@ function PositionGroupTable({
<button
type="button"
onClick={handlePrevious}
disabled={!canGoBack}
style={canGoBack ? pagButtonStyle : pagButtonDisabledStyle}
disabled={!canGoBack || isFetching}
style={canGoBack && !isFetching ? pagButtonStyle : pagButtonDisabledStyle}
>
{isFetching ? (
<span
className="loading-spinner"
style={{ width: 14, height: 14, display: 'block' }}
/>
) : (
'←'
)}
</button>
<span
style={{
@ -167,10 +174,17 @@ function PositionGroupTable({
<button
type="button"
onClick={handleNext}
disabled={!canGoForward}
style={canGoForward ? pagButtonStyle : pagButtonDisabledStyle}
disabled={!canGoForward || isFetching}
style={canGoForward && !isFetching ? pagButtonStyle : pagButtonDisabledStyle}
>
{isFetching ? (
<span
className="loading-spinner"
style={{ width: 14, height: 14, display: 'block' }}
/>
) : (
'→'
)}
</button>
</div>
)}
@ -213,58 +227,68 @@ function PositionGroupTable({
)}
{!isLoading && positions.length > 0 && (
<div style={{ overflowX: 'auto', background: 'var(--color-surface)' }}>
<table aria-label={`Брокерские позиции: ${group.title}`} style={tableStyle}>
<thead>
<tr>
<th align="left" style={thStyle}>
Тикер
</th>
<th align="left" style={thStyle}>
Название
</th>
<th align="right" style={thStyle}>
Количество
</th>
<th align="right" style={thStyle}>
Цена
</th>
<th align="right" style={thStyle}>
Стоимость
</th>
</tr>
</thead>
<tbody>
{positions.map((position) => (
<tr
key={
position.positionUid ||
position.instrumentUid ||
position.ticker ||
position.figi
}
>
<td style={tdStyle}>
<PositionTicker position={position} />
</td>
<td style={tdStyle}>
<span style={{ color: 'var(--color-text-secondary)' }}>
{position.name || '-'}
</span>
</td>
<td align="right" style={tdStyle}>
{formatQuantity(position.quantity)}
</td>
<td align="right" style={tdStyle}>
{formatMoney(position.currentPrice)}
</td>
<td align="right" style={tdStyle}>
{formatMoney(position.currentValue)}
</td>
<div className="table-container">
<div style={{ overflowX: 'auto', background: 'var(--color-surface)' }}>
<table aria-label={`Брокерские позиции: ${group.title}`} style={tableStyle}>
<thead>
<tr>
<th align="left" style={thStyle}>
Тикер
</th>
<th align="left" style={thStyle}>
Название
</th>
<th align="right" style={thStyle}>
Количество
</th>
<th align="right" style={thStyle}>
Цена
</th>
<th align="right" style={thStyle}>
Стоимость
</th>
</tr>
))}
</tbody>
</table>
</thead>
<tbody>
{positions.map((position) => (
<tr
key={
position.positionUid ||
position.instrumentUid ||
position.ticker ||
position.figi
}
>
<td style={tdStyle}>
<PositionTicker position={position} />
</td>
<td style={tdStyle}>
<span style={{ color: 'var(--color-text-secondary)' }}>
{position.name || '-'}
</span>
</td>
<td align="right" style={tdStyle}>
{formatQuantity(position.quantity)}
</td>
<td align="right" style={tdStyle}>
{formatMoney(position.currentPrice)}
</td>
<td align="right" style={tdStyle}>
{formatMoney(position.currentValue)}
</td>
</tr>
))}
</tbody>
</table>
</div>
{isFetching && (
<div className="table-loading-overlay">
<div className="loading-spinner" />
<span style={{ fontSize: 13, color: 'var(--color-text-secondary)' }}>
Загрузка страницы {pageNumber}
</span>
</div>
)}
</div>
)}
</section>