759ae5e
import { useState } from 'react'
import { useT } from '@/shared/i18n'
import { Busy, Button, SaveBar, SectionTitle, fmtDate, useSave } from '@/shared/ui'
import { settingsKey, useAsync } from '@/shared/lib'
import * as api from '../../model/api'
export function SessionsSection() {
const { t } = useT()
const [tick, setTick] = useState(0)
const data = useAsync(async () => {
const [devices, sessions] = await Promise.all([api.listDevices(), api.listSessions()])
return { devices, sessions: sessions.items ?? [] }
}, [tick], { cacheKey: settingsKey('sessions') })
const { busy, notice, error, run } = useSave()
const refresh = () => setTick(t => t + 1)
return (
<section>
<SectionTitle title={t('userSettings.sessions.title')} sub={t('userSettings.sessions.sub')} />
{data.loading ? (
<Busy />
) : (
<>
<h3 className="mb-3 text-[14px] font-bold">{t('userSettings.sessions.devices')}</h3>
<div className="mb-8 space-y-2">
{(data.data?.devices ?? []).length === 0 ? <p className="text-[13px] text-shell-ink-muted">{t('userSettings.sessions.noDevices')}</p> : null}
{(data.data?.devices ?? []).map(device => (
<div key={device.id} className="settings-card flex flex-wrap items-center gap-3 px-4 py-3">
<span className="min-w-[160px] text-[13.5px] font-semibold">{device.device_name || device.device_type || t('userSettings.sessions.unknownDevice')}</span>
<span className="text-[12px] text-shell-ink-muted">{device.os ?? '—'} · {t('userSettings.sessions.recently', { when: fmtDate(device.last_seen_at) })}</span>
</div>
))}
</div>
<h3 className="mb-3 text-[14px] font-bold">{t('userSettings.sessions.sessions')}</h3>
<div className="mb-4 space-y-2">
{(data.data?.sessions ?? []).length === 0 ? <p className="text-[13px] text-shell-ink-muted">{t('userSettings.sessions.noSessions')}</p> : null}
{(data.data?.sessions ?? []).map(session => (
<div key={session.id} className="settings-card flex flex-wrap items-center gap-3 px-4 py-3">
<span className="text-[13.5px] font-semibold">{session.kind === 'WebLogin' ? t('userSettings.sessions.kindWeb') : t('userSettings.sessions.kindKey')}</span>
{session.is_current ? <span className="rounded-full bg-shell-active px-2 py-0.5 text-[11px]">{t('userSettings.sessions.current')}</span> : null}
{session.is_revoked ? <span className="rounded-full bg-shell-active px-2 py-0.5 text-[11px]">{t('userSettings.sessions.revoked')}</span> : null}
<span className="text-[12px] text-shell-ink-muted">
{t('userSettings.sessions.createdAt', { when: fmtDate(session.created_at) })} · {t('userSettings.sessions.recently', { when: fmtDate(session.last_seen_at) })}
</span>
{!session.is_current && !session.is_revoked ? (
<Button variant="secondary" className="ml-auto h-8 text-[12.5px]" disabled={busy} onClick={() => void run(async () => { await api.revokeSession(session.id); refresh() }, t('settings.sessions.revoked'))}>
{t('userSettings.sessions.revoke')}
</Button>
) : null}
</div>
))}
</div>
<SaveBar busy={busy} notice={notice} error={error} label={t('userSettings.sessions.revokeAll')} onSave={() => void run(async () => { await api.revokeAllSessions(); refresh() }, t('settings.account.revoked'))} />
</>
)}
</section>
)
}