mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-05 20:54:14 +00:00
feat(clients): live online dot + last-online tooltip on offline
Two small UX cues on the clients table online column: - a pulsing green dot next to the Online tag so an active client reads as live at a glance (honors prefers-reduced-motion). - hovering the Offline tag shows the client's last-online timestamp from record.traffic.lastOnline, formatted with the panel's calendar setting (or "-" when the client has never connected).
This commit is contained in:
parent
b67c4c2f81
commit
c8df1b19ff
2 changed files with 30 additions and 3 deletions
|
|
@ -62,6 +62,26 @@
|
||||||
.dot-orange { background: var(--ant-color-warning); }
|
.dot-orange { background: var(--ant-color-warning); }
|
||||||
.dot-gray { background: var(--ant-color-text-quaternary); }
|
.dot-gray { background: var(--ant-color-text-quaternary); }
|
||||||
|
|
||||||
|
.online-dot {
|
||||||
|
display: inline-block;
|
||||||
|
width: 7px;
|
||||||
|
height: 7px;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-inline-end: 5px;
|
||||||
|
vertical-align: middle;
|
||||||
|
background: var(--ant-color-success);
|
||||||
|
animation: online-blink 1.1s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes online-blink {
|
||||||
|
0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(82, 196, 26, 0.55); }
|
||||||
|
50% { opacity: 0.35; box-shadow: 0 0 0 4px rgba(82, 196, 26, 0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.online-dot { animation: none; }
|
||||||
|
}
|
||||||
|
|
||||||
.status-tag {
|
.status-tag {
|
||||||
margin: 0 0 0 4px;
|
margin: 0 0 0 4px;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
|
|
|
||||||
|
|
@ -626,10 +626,17 @@ export default function ClientsPage() {
|
||||||
render: (_v, record) => {
|
render: (_v, record) => {
|
||||||
const bucket = clientBucket(record);
|
const bucket = clientBucket(record);
|
||||||
if (bucket === 'depleted') return <Tag color="red">{t('depleted')}</Tag>;
|
if (bucket === 'depleted') return <Tag color="red">{t('depleted')}</Tag>;
|
||||||
if (record.enable && isOnline(record.email)) return <Tag color="green">{t('pages.clients.online')}</Tag>;
|
if (record.enable && isOnline(record.email)) return (
|
||||||
|
<Tag color="green"><span className="online-dot" />{t('pages.clients.online')}</Tag>
|
||||||
|
);
|
||||||
if (!record.enable) return <Tag>{t('disabled')}</Tag>;
|
if (!record.enable) return <Tag>{t('disabled')}</Tag>;
|
||||||
if (bucket === 'expiring') return <Tag color="orange">{t('depletingSoon')}</Tag>;
|
if (bucket === 'expiring') return <Tag color="orange">{t('depletingSoon')}</Tag>;
|
||||||
return <Tag>{t('pages.clients.offline')}</Tag>;
|
const lastOnline = record.traffic?.lastOnline ?? 0;
|
||||||
|
return (
|
||||||
|
<Tooltip title={`${t('lastOnline')}: ${lastOnline > 0 ? IntlUtil.formatDate(lastOnline, datepicker) : '-'}`}>
|
||||||
|
<Tag>{t('pages.clients.offline')}</Tag>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -732,7 +739,7 @@ export default function ClientsPage() {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
], [t, togglingEmail, clientBucket, isOnline, inboundsById, filters, allGroups]);
|
], [t, togglingEmail, clientBucket, isOnline, inboundsById, filters, allGroups, datepicker]);
|
||||||
|
|
||||||
const tablePagination = {
|
const tablePagination = {
|
||||||
current: currentPage,
|
current: currentPage,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue