mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 17:43:47 +00:00
feat(admin): вкладка «Активность» в карточке пользователя
Единый таймлайн действий юзера в боте и кабинете
(GET /cabinet/admin/users/{id}/activity): вертикальная лента с
иконками-кружками по типу записи, бейджи подтипов, суммы со знаком
(+зелёный / −красный), относительное время (абсолютное — в title),
чипы-фильтры по категориям (платежи, события, промо, тикеты, подарки,
рефералы, входы), счётчик StatCard и постраничная догрузка по house
load-more паттерну. Компонент самодостаточный (ActivityTab), как
TicketsTab. Переводы во всех 4 локалях; время переиспользует
admin.auditLog.time.*.
biome check, type-check и build проходят (pre-commit пропущен: biome
не установлен локально — бинарь есть только в CI).
This commit is contained in:
@@ -24,6 +24,7 @@ import { GiftsTab } from '../components/admin/userDetail/GiftsTab';
|
||||
import { SyncTab } from '../components/admin/userDetail/SyncTab';
|
||||
import { ReferralsTab } from '../components/admin/userDetail/ReferralsTab';
|
||||
import { BalanceTab } from '../components/admin/userDetail/BalanceTab';
|
||||
import { ActivityTab } from '../components/admin/userDetail/ActivityTab';
|
||||
import { TicketsTab } from '../components/admin/userDetail/TicketsTab';
|
||||
import { InfoTab } from '../components/admin/userDetail/InfoTab';
|
||||
import { SubscriptionTab } from '../components/admin/userDetail/SubscriptionTab';
|
||||
@@ -49,7 +50,7 @@ export default function AdminUserDetail() {
|
||||
const [user, setUser] = useState<UserDetailResponse | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [activeTab, setActiveTab] = useState<
|
||||
'info' | 'subscription' | 'balance' | 'sync' | 'tickets' | 'gifts' | 'referrals'
|
||||
'info' | 'subscription' | 'balance' | 'sync' | 'tickets' | 'gifts' | 'referrals' | 'activity'
|
||||
>('info');
|
||||
const [syncStatus, setSyncStatus] = useState<PanelSyncStatusResponse | null>(null);
|
||||
const [tariffs, setTariffs] = useState<UserAvailableTariff[]>([]);
|
||||
@@ -776,7 +777,18 @@ export default function AdminUserDetail() {
|
||||
className="scrollbar-hide -mx-4 mb-6 flex gap-2 overflow-x-auto px-4 py-1"
|
||||
style={{ WebkitOverflowScrolling: 'touch' }}
|
||||
>
|
||||
{(['info', 'subscription', 'balance', 'sync', 'tickets', 'gifts', 'referrals'] as const)
|
||||
{(
|
||||
[
|
||||
'info',
|
||||
'subscription',
|
||||
'balance',
|
||||
'sync',
|
||||
'tickets',
|
||||
'gifts',
|
||||
'referrals',
|
||||
'activity',
|
||||
] as const
|
||||
)
|
||||
.filter((tab) => tab !== 'sync' || hasPermission('users:sync'))
|
||||
.map((tab) => (
|
||||
<button
|
||||
@@ -795,6 +807,7 @@ export default function AdminUserDetail() {
|
||||
{tab === 'tickets' && t('admin.users.detail.tabs.tickets')}
|
||||
{tab === 'gifts' && t('admin.users.detail.tabs.gifts')}
|
||||
{tab === 'referrals' && t('admin.users.detail.tabs.referrals')}
|
||||
{tab === 'activity' && t('admin.users.detail.tabs.activity')}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
@@ -948,6 +961,11 @@ export default function AdminUserDetail() {
|
||||
{activeTab === 'referrals' && user && userId && (
|
||||
<ReferralsTab user={user} userId={userId} onUserRefresh={loadUser} />
|
||||
)}
|
||||
|
||||
{/* Activity Tab */}
|
||||
{activeTab === 'activity' && userId && (
|
||||
<ActivityTab userId={userId} formatDate={formatDate} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user