feat(clients): add Reset Traffic, QR Code, Info actions + Online/Remaining columns
The Clients page table gains:
- Online column — green/grey tag driven by /panel/api/inbounds/onlines,
polled every 10s.
- Remaining column — bytes-remaining tag, coloured green/orange/red
against quota, purple infinity when unlimited.
- Action icons per row: QR, Info, Reset traffic, Edit, Delete.
ClientInfoModal shows the full client detail (uuid/password/auth,
traffic ↑/↓ + remaining + all-time, expiry absolute + relative,
attached inbounds chip list, online + last-online).
ClientQrModal fetches links for the client's subId via
/panel/api/inbounds/getSubLinks/:subId and renders each one through
the existing QrPanel component.
Reset Traffic confirms then calls the existing per-inbound endpoint
on the client's first attached inbound (the traffic row is keyed on
email globally, so any attached inbound resets the shared counter).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 05:49:12 +00:00
|
|
|
<script setup>
|
2026-05-17 06:53:21 +00:00
|
|
|
import { computed, ref, watch } from 'vue';
|
feat(clients): add Reset Traffic, QR Code, Info actions + Online/Remaining columns
The Clients page table gains:
- Online column — green/grey tag driven by /panel/api/inbounds/onlines,
polled every 10s.
- Remaining column — bytes-remaining tag, coloured green/orange/red
against quota, purple infinity when unlimited.
- Action icons per row: QR, Info, Reset traffic, Edit, Delete.
ClientInfoModal shows the full client detail (uuid/password/auth,
traffic ↑/↓ + remaining + all-time, expiry absolute + relative,
attached inbounds chip list, online + last-online).
ClientQrModal fetches links for the client's subId via
/panel/api/inbounds/getSubLinks/:subId and renders each one through
the existing QrPanel component.
Reset Traffic confirms then calls the existing per-inbound endpoint
on the client's first attached inbound (the traffic row is keyed on
email globally, so any attached inbound resets the shared counter).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 05:49:12 +00:00
|
|
|
import { useI18n } from 'vue-i18n';
|
|
|
|
|
import { CopyOutlined } from '@ant-design/icons-vue';
|
|
|
|
|
import { message } from 'ant-design-vue';
|
2026-05-17 06:53:21 +00:00
|
|
|
import { SizeFormatter, IntlUtil, ClipboardManager, HttpUtil } from '@/utils';
|
feat(clients): add Reset Traffic, QR Code, Info actions + Online/Remaining columns
The Clients page table gains:
- Online column — green/grey tag driven by /panel/api/inbounds/onlines,
polled every 10s.
- Remaining column — bytes-remaining tag, coloured green/orange/red
against quota, purple infinity when unlimited.
- Action icons per row: QR, Info, Reset traffic, Edit, Delete.
ClientInfoModal shows the full client detail (uuid/password/auth,
traffic ↑/↓ + remaining + all-time, expiry absolute + relative,
attached inbounds chip list, online + last-online).
ClientQrModal fetches links for the client's subId via
/panel/api/inbounds/getSubLinks/:subId and renders each one through
the existing QrPanel component.
Reset Traffic confirms then calls the existing per-inbound endpoint
on the client's first attached inbound (the traffic row is keyed on
email globally, so any attached inbound resets the shared counter).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 05:49:12 +00:00
|
|
|
|
|
|
|
|
const { t } = useI18n();
|
|
|
|
|
|
|
|
|
|
const props = defineProps({
|
|
|
|
|
open: { type: Boolean, default: false },
|
|
|
|
|
client: { type: Object, default: null },
|
|
|
|
|
inboundsById: { type: Object, default: () => ({}) },
|
|
|
|
|
isOnline: { type: Boolean, default: false },
|
2026-05-17 06:53:21 +00:00
|
|
|
subSettings: {
|
|
|
|
|
type: Object,
|
|
|
|
|
default: () => ({ enable: false, subURI: '', subJsonURI: '', subJsonEnable: false }),
|
|
|
|
|
},
|
feat(clients): add Reset Traffic, QR Code, Info actions + Online/Remaining columns
The Clients page table gains:
- Online column — green/grey tag driven by /panel/api/inbounds/onlines,
polled every 10s.
- Remaining column — bytes-remaining tag, coloured green/orange/red
against quota, purple infinity when unlimited.
- Action icons per row: QR, Info, Reset traffic, Edit, Delete.
ClientInfoModal shows the full client detail (uuid/password/auth,
traffic ↑/↓ + remaining + all-time, expiry absolute + relative,
attached inbounds chip list, online + last-online).
ClientQrModal fetches links for the client's subId via
/panel/api/inbounds/getSubLinks/:subId and renders each one through
the existing QrPanel component.
Reset Traffic confirms then calls the existing per-inbound endpoint
on the client's first attached inbound (the traffic row is keyed on
email globally, so any attached inbound resets the shared counter).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 05:49:12 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const emit = defineEmits(['update:open']);
|
|
|
|
|
|
2026-05-17 06:53:21 +00:00
|
|
|
const links = ref([]);
|
|
|
|
|
const linksLoading = ref(false);
|
|
|
|
|
|
feat(clients): add Reset Traffic, QR Code, Info actions + Online/Remaining columns
The Clients page table gains:
- Online column — green/grey tag driven by /panel/api/inbounds/onlines,
polled every 10s.
- Remaining column — bytes-remaining tag, coloured green/orange/red
against quota, purple infinity when unlimited.
- Action icons per row: QR, Info, Reset traffic, Edit, Delete.
ClientInfoModal shows the full client detail (uuid/password/auth,
traffic ↑/↓ + remaining + all-time, expiry absolute + relative,
attached inbounds chip list, online + last-online).
ClientQrModal fetches links for the client's subId via
/panel/api/inbounds/getSubLinks/:subId and renders each one through
the existing QrPanel component.
Reset Traffic confirms then calls the existing per-inbound endpoint
on the client's first attached inbound (the traffic row is keyed on
email globally, so any attached inbound resets the shared counter).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 05:49:12 +00:00
|
|
|
const traffic = computed(() => props.client?.traffic || null);
|
|
|
|
|
const totalBytes = computed(() => props.client?.totalGB || 0);
|
|
|
|
|
const used = computed(() => (traffic.value?.up || 0) + (traffic.value?.down || 0));
|
|
|
|
|
const remaining = computed(() => {
|
|
|
|
|
if (totalBytes.value <= 0) return -1;
|
|
|
|
|
const r = totalBytes.value - used.value;
|
|
|
|
|
return r > 0 ? r : 0;
|
|
|
|
|
});
|
|
|
|
|
|
2026-05-17 06:53:21 +00:00
|
|
|
const subLink = computed(() => {
|
|
|
|
|
if (!props.client?.subId || !props.subSettings?.subURI) return '';
|
|
|
|
|
return props.subSettings.subURI + props.client.subId;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const subJsonLink = computed(() => {
|
|
|
|
|
if (!props.client?.subId) return '';
|
|
|
|
|
if (!props.subSettings?.subJsonEnable || !props.subSettings?.subJsonURI) return '';
|
|
|
|
|
return props.subSettings.subJsonURI + props.client.subId;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const showSubscription = computed(
|
|
|
|
|
() => !!(props.subSettings?.enable && props.client?.subId),
|
|
|
|
|
);
|
|
|
|
|
|
feat(clients): add Reset Traffic, QR Code, Info actions + Online/Remaining columns
The Clients page table gains:
- Online column — green/grey tag driven by /panel/api/inbounds/onlines,
polled every 10s.
- Remaining column — bytes-remaining tag, coloured green/orange/red
against quota, purple infinity when unlimited.
- Action icons per row: QR, Info, Reset traffic, Edit, Delete.
ClientInfoModal shows the full client detail (uuid/password/auth,
traffic ↑/↓ + remaining + all-time, expiry absolute + relative,
attached inbounds chip list, online + last-online).
ClientQrModal fetches links for the client's subId via
/panel/api/inbounds/getSubLinks/:subId and renders each one through
the existing QrPanel component.
Reset Traffic confirms then calls the existing per-inbound endpoint
on the client's first attached inbound (the traffic row is keyed on
email globally, so any attached inbound resets the shared counter).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 05:49:12 +00:00
|
|
|
function expiryLabel(ts) {
|
|
|
|
|
if (!ts || ts <= 0) return '∞';
|
|
|
|
|
return IntlUtil.formatDate(ts);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function expiryRelative(ts) {
|
|
|
|
|
if (!ts || ts <= 0) return '';
|
|
|
|
|
return IntlUtil.formatRelativeTime(ts);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function lastOnlineLabel(ts) {
|
|
|
|
|
if (!ts || ts <= 0) return '-';
|
|
|
|
|
return IntlUtil.formatDate(ts);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-17 06:53:21 +00:00
|
|
|
function dateLabel(ts) {
|
|
|
|
|
if (!ts || ts <= 0) return '-';
|
|
|
|
|
return IntlUtil.formatDate(ts);
|
|
|
|
|
}
|
|
|
|
|
|
feat(clients): add Reset Traffic, QR Code, Info actions + Online/Remaining columns
The Clients page table gains:
- Online column — green/grey tag driven by /panel/api/inbounds/onlines,
polled every 10s.
- Remaining column — bytes-remaining tag, coloured green/orange/red
against quota, purple infinity when unlimited.
- Action icons per row: QR, Info, Reset traffic, Edit, Delete.
ClientInfoModal shows the full client detail (uuid/password/auth,
traffic ↑/↓ + remaining + all-time, expiry absolute + relative,
attached inbounds chip list, online + last-online).
ClientQrModal fetches links for the client's subId via
/panel/api/inbounds/getSubLinks/:subId and renders each one through
the existing QrPanel component.
Reset Traffic confirms then calls the existing per-inbound endpoint
on the client's first attached inbound (the traffic row is keyed on
email globally, so any attached inbound resets the shared counter).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 05:49:12 +00:00
|
|
|
async function copyValue(text) {
|
|
|
|
|
if (!text) return;
|
|
|
|
|
const ok = await ClipboardManager.copyText(String(text));
|
|
|
|
|
if (ok) message.success(t('copied'));
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-17 06:53:21 +00:00
|
|
|
async function loadLinks() {
|
|
|
|
|
if (!props.client?.subId) {
|
|
|
|
|
links.value = [];
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
linksLoading.value = true;
|
|
|
|
|
try {
|
|
|
|
|
const msg = await HttpUtil.get(
|
refactor(api): move every client-shaped endpoint off /inbounds onto /clients
After the multi-inbound client migration, client state belongs to the
client API surface, not the inbound one. Twelve routes that were
crammed under /panel/api/inbounds/* now live where they belong, under
/panel/api/clients/*.
Moved (route, handler, doc):
POST /clientIps/:email
POST /clearClientIps/:email
POST /onlines
POST /lastOnline
POST /updateClientTraffic/:email
POST /resetAllClientTraffics/:id
POST /delDepletedClients/:id
POST /:id/resetClientTraffic/:email
GET /getClientTraffics/:email
GET /getClientTrafficsById/:id
GET /getSubLinks/:subId
GET /getClientLinks/:id/:email
Their /clients/* counterparts are:
POST /clients/clientIps/:email
POST /clients/clearClientIps/:email
POST /clients/onlines
POST /clients/lastOnline
POST /clients/updateTraffic/:email
POST /clients/resetTraffic/:email (email-only, fans out)
GET /clients/traffic/:email
GET /clients/traffic/byId/:id
GET /clients/subLinks/:subId
GET /clients/links/:id/:email
per-inbound resetAllClientTraffics and delDepletedClients are dropped
entirely — the Clients page already exposes global Reset All Traffic
and Delete depleted actions, and per-inbound resets are meaningless
once a client can be attached to many inbounds.
ClientService.ResetTrafficByEmail is the new email-only reset path:
it looks up every inbound the client is attached to and pushes the
counter reset + Xray re-add through inboundService.ResetClientTraffic
for each one, so depleted users come back online instantly.
Frontend callers (ClientsPage, useClients, ClientQrModal,
ClientInfoModal, InboundInfoModal, InboundsPage, useInbounds) all
switched to the new paths. The Inbounds page drops its per-inbound
"Reset client traffic" and "Delete depleted clients" dropdown items —
users do those at the client level now. api-docs is rebuilt to match.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 08:15:01 +00:00
|
|
|
`/panel/api/clients/subLinks/${encodeURIComponent(props.client.subId)}`,
|
2026-05-17 06:53:21 +00:00
|
|
|
);
|
|
|
|
|
links.value = msg?.success && Array.isArray(msg.obj) ? msg.obj : [];
|
|
|
|
|
} finally {
|
|
|
|
|
linksLoading.value = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
watch(() => props.open, (next) => {
|
|
|
|
|
if (next) loadLinks();
|
|
|
|
|
else links.value = [];
|
|
|
|
|
});
|
|
|
|
|
|
feat(clients): add Reset Traffic, QR Code, Info actions + Online/Remaining columns
The Clients page table gains:
- Online column — green/grey tag driven by /panel/api/inbounds/onlines,
polled every 10s.
- Remaining column — bytes-remaining tag, coloured green/orange/red
against quota, purple infinity when unlimited.
- Action icons per row: QR, Info, Reset traffic, Edit, Delete.
ClientInfoModal shows the full client detail (uuid/password/auth,
traffic ↑/↓ + remaining + all-time, expiry absolute + relative,
attached inbounds chip list, online + last-online).
ClientQrModal fetches links for the client's subId via
/panel/api/inbounds/getSubLinks/:subId and renders each one through
the existing QrPanel component.
Reset Traffic confirms then calls the existing per-inbound endpoint
on the client's first attached inbound (the traffic row is keyed on
email globally, so any attached inbound resets the shared counter).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 05:49:12 +00:00
|
|
|
function close() {
|
|
|
|
|
emit('update:open', false);
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
2026-05-17 06:53:21 +00:00
|
|
|
<a-modal :open="open" :title="client ? client.email : t('info')" :footer="null" :width="640" @cancel="close">
|
|
|
|
|
<template v-if="client">
|
|
|
|
|
<table class="info-table block">
|
|
|
|
|
<tbody>
|
|
|
|
|
<tr>
|
i18n(clients): replace English fallbacks with proper translation keys
Pulls every hard-coded English label/title in the Clients page and its
four modals through the i18n layer so localized panels stop leaking
English. New keys live under pages.clients (auth, hysteriaAuth, uuid,
flow, flowNone, reverseTag, reverseTagPlaceholder, telegramId,
telegramIdPlaceholder, created, updated, ipLimit) plus refresh at the
root and toasts.bulkDeletedMixed / bulkCreatedMixed for partial-failure
toasts. Also switches the add-client modal's primary button from "Add"
to "Create" for consistency with other create flows.
The bulk-add Random/Random+Prefix/... email-method options stay
hard-coded by request - they're identifier-shaped strings.
2026-05-18 08:17:15 +00:00
|
|
|
<td>{{ t('pages.clients.online') }}</td>
|
2026-05-17 06:53:21 +00:00
|
|
|
<td>
|
i18n(clients): replace English fallbacks with proper translation keys
Pulls every hard-coded English label/title in the Clients page and its
four modals through the i18n layer so localized panels stop leaking
English. New keys live under pages.clients (auth, hysteriaAuth, uuid,
flow, flowNone, reverseTag, reverseTagPlaceholder, telegramId,
telegramIdPlaceholder, created, updated, ipLimit) plus refresh at the
root and toasts.bulkDeletedMixed / bulkCreatedMixed for partial-failure
toasts. Also switches the add-client modal's primary button from "Add"
to "Create" for consistency with other create flows.
The bulk-add Random/Random+Prefix/... email-method options stay
hard-coded by request - they're identifier-shaped strings.
2026-05-18 08:17:15 +00:00
|
|
|
<a-tag v-if="client.enable && isOnline" color="green">{{ t('pages.clients.online') }}</a-tag>
|
|
|
|
|
<a-tag v-else>{{ t('pages.clients.offline') }}</a-tag>
|
2026-05-17 06:53:21 +00:00
|
|
|
<span class="hint">{{ t('lastOnline') }}: {{ lastOnlineLabel(traffic?.lastOnline) }}</span>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr>
|
|
|
|
|
<td>{{ t('status') }}</td>
|
|
|
|
|
<td>
|
|
|
|
|
<a-tag :color="client.enable ? 'green' : 'default'">
|
|
|
|
|
{{ client.enable ? t('enabled') : t('disabled') }}
|
|
|
|
|
</a-tag>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr>
|
i18n(clients): replace English fallbacks with proper translation keys
Pulls every hard-coded English label/title in the Clients page and its
four modals through the i18n layer so localized panels stop leaking
English. New keys live under pages.clients (auth, hysteriaAuth, uuid,
flow, flowNone, reverseTag, reverseTagPlaceholder, telegramId,
telegramIdPlaceholder, created, updated, ipLimit) plus refresh at the
root and toasts.bulkDeletedMixed / bulkCreatedMixed for partial-failure
toasts. Also switches the add-client modal's primary button from "Add"
to "Create" for consistency with other create flows.
The bulk-add Random/Random+Prefix/... email-method options stay
hard-coded by request - they're identifier-shaped strings.
2026-05-18 08:17:15 +00:00
|
|
|
<td>{{ t('pages.clients.email') }}</td>
|
2026-05-17 06:53:21 +00:00
|
|
|
<td>
|
|
|
|
|
<a-tag v-if="client.email" color="green">{{ client.email }}</a-tag>
|
|
|
|
|
<a-tag v-else color="red">{{ t('none') }}</a-tag>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr>
|
i18n(clients): replace English fallbacks with proper translation keys
Pulls every hard-coded English label/title in the Clients page and its
four modals through the i18n layer so localized panels stop leaking
English. New keys live under pages.clients (auth, hysteriaAuth, uuid,
flow, flowNone, reverseTag, reverseTagPlaceholder, telegramId,
telegramIdPlaceholder, created, updated, ipLimit) plus refresh at the
root and toasts.bulkDeletedMixed / bulkCreatedMixed for partial-failure
toasts. Also switches the add-client modal's primary button from "Add"
to "Create" for consistency with other create flows.
The bulk-add Random/Random+Prefix/... email-method options stay
hard-coded by request - they're identifier-shaped strings.
2026-05-18 08:17:15 +00:00
|
|
|
<td>{{ t('pages.clients.subId') }}</td>
|
2026-05-17 06:53:21 +00:00
|
|
|
<td>
|
|
|
|
|
<a-tag class="info-large-tag">{{ client.subId || '-' }}</a-tag>
|
|
|
|
|
<a-button v-if="client.subId" size="small" type="text" @click="copyValue(client.subId)">
|
|
|
|
|
<CopyOutlined />
|
|
|
|
|
</a-button>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr v-if="client.uuid">
|
i18n(clients): replace English fallbacks with proper translation keys
Pulls every hard-coded English label/title in the Clients page and its
four modals through the i18n layer so localized panels stop leaking
English. New keys live under pages.clients (auth, hysteriaAuth, uuid,
flow, flowNone, reverseTag, reverseTagPlaceholder, telegramId,
telegramIdPlaceholder, created, updated, ipLimit) plus refresh at the
root and toasts.bulkDeletedMixed / bulkCreatedMixed for partial-failure
toasts. Also switches the add-client modal's primary button from "Add"
to "Create" for consistency with other create flows.
The bulk-add Random/Random+Prefix/... email-method options stay
hard-coded by request - they're identifier-shaped strings.
2026-05-18 08:17:15 +00:00
|
|
|
<td>{{ t('pages.clients.uuid') }}</td>
|
2026-05-17 06:53:21 +00:00
|
|
|
<td>
|
|
|
|
|
<a-tag class="info-large-tag">{{ client.uuid }}</a-tag>
|
|
|
|
|
<a-button size="small" type="text" @click="copyValue(client.uuid)">
|
|
|
|
|
<CopyOutlined />
|
|
|
|
|
</a-button>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr v-if="client.password">
|
|
|
|
|
<td>{{ t('password') }}</td>
|
|
|
|
|
<td>
|
|
|
|
|
<a-tag class="info-large-tag">{{ client.password }}</a-tag>
|
|
|
|
|
<a-button size="small" type="text" @click="copyValue(client.password)">
|
|
|
|
|
<CopyOutlined />
|
|
|
|
|
</a-button>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr v-if="client.auth">
|
i18n(clients): replace English fallbacks with proper translation keys
Pulls every hard-coded English label/title in the Clients page and its
four modals through the i18n layer so localized panels stop leaking
English. New keys live under pages.clients (auth, hysteriaAuth, uuid,
flow, flowNone, reverseTag, reverseTagPlaceholder, telegramId,
telegramIdPlaceholder, created, updated, ipLimit) plus refresh at the
root and toasts.bulkDeletedMixed / bulkCreatedMixed for partial-failure
toasts. Also switches the add-client modal's primary button from "Add"
to "Create" for consistency with other create flows.
The bulk-add Random/Random+Prefix/... email-method options stay
hard-coded by request - they're identifier-shaped strings.
2026-05-18 08:17:15 +00:00
|
|
|
<td>{{ t('pages.clients.auth') }}</td>
|
2026-05-17 06:53:21 +00:00
|
|
|
<td>
|
|
|
|
|
<a-tag class="info-large-tag">{{ client.auth }}</a-tag>
|
|
|
|
|
<a-button size="small" type="text" @click="copyValue(client.auth)">
|
|
|
|
|
<CopyOutlined />
|
|
|
|
|
</a-button>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr>
|
i18n(clients): replace English fallbacks with proper translation keys
Pulls every hard-coded English label/title in the Clients page and its
four modals through the i18n layer so localized panels stop leaking
English. New keys live under pages.clients (auth, hysteriaAuth, uuid,
flow, flowNone, reverseTag, reverseTagPlaceholder, telegramId,
telegramIdPlaceholder, created, updated, ipLimit) plus refresh at the
root and toasts.bulkDeletedMixed / bulkCreatedMixed for partial-failure
toasts. Also switches the add-client modal's primary button from "Add"
to "Create" for consistency with other create flows.
The bulk-add Random/Random+Prefix/... email-method options stay
hard-coded by request - they're identifier-shaped strings.
2026-05-18 08:17:15 +00:00
|
|
|
<td>{{ t('pages.clients.flow') }}</td>
|
2026-05-17 06:53:21 +00:00
|
|
|
<td>
|
|
|
|
|
<a-tag v-if="client.flow">{{ client.flow }}</a-tag>
|
|
|
|
|
<a-tag v-else color="orange">{{ t('none') }}</a-tag>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr>
|
|
|
|
|
<td>{{ t('pages.inbounds.traffic') }}</td>
|
|
|
|
|
<td>
|
|
|
|
|
<a-tag>
|
|
|
|
|
↑ {{ SizeFormatter.sizeFormat(traffic?.up || 0) }}
|
|
|
|
|
/ ↓ {{ SizeFormatter.sizeFormat(traffic?.down || 0) }}
|
|
|
|
|
</a-tag>
|
|
|
|
|
<span class="hint">
|
|
|
|
|
{{ SizeFormatter.sizeFormat(used) }}
|
|
|
|
|
/
|
|
|
|
|
{{ totalBytes > 0 ? SizeFormatter.sizeFormat(totalBytes) : '∞' }}
|
|
|
|
|
</span>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr>
|
i18n(clients): replace English fallbacks with proper translation keys
Pulls every hard-coded English label/title in the Clients page and its
four modals through the i18n layer so localized panels stop leaking
English. New keys live under pages.clients (auth, hysteriaAuth, uuid,
flow, flowNone, reverseTag, reverseTagPlaceholder, telegramId,
telegramIdPlaceholder, created, updated, ipLimit) plus refresh at the
root and toasts.bulkDeletedMixed / bulkCreatedMixed for partial-failure
toasts. Also switches the add-client modal's primary button from "Add"
to "Create" for consistency with other create flows.
The bulk-add Random/Random+Prefix/... email-method options stay
hard-coded by request - they're identifier-shaped strings.
2026-05-18 08:17:15 +00:00
|
|
|
<td>{{ t('remained') }}</td>
|
2026-05-17 06:53:21 +00:00
|
|
|
<td>
|
|
|
|
|
<a-tag v-if="remaining < 0" color="purple">∞</a-tag>
|
|
|
|
|
<a-tag v-else :color="remaining > 0 ? '' : 'red'">
|
|
|
|
|
{{ SizeFormatter.sizeFormat(remaining) }}
|
|
|
|
|
</a-tag>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr>
|
i18n(clients): replace English fallbacks with proper translation keys
Pulls every hard-coded English label/title in the Clients page and its
four modals through the i18n layer so localized panels stop leaking
English. New keys live under pages.clients (auth, hysteriaAuth, uuid,
flow, flowNone, reverseTag, reverseTagPlaceholder, telegramId,
telegramIdPlaceholder, created, updated, ipLimit) plus refresh at the
root and toasts.bulkDeletedMixed / bulkCreatedMixed for partial-failure
toasts. Also switches the add-client modal's primary button from "Add"
to "Create" for consistency with other create flows.
The bulk-add Random/Random+Prefix/... email-method options stay
hard-coded by request - they're identifier-shaped strings.
2026-05-18 08:17:15 +00:00
|
|
|
<td>{{ t('pages.inbounds.expireDate') }}</td>
|
2026-05-17 06:53:21 +00:00
|
|
|
<td>
|
|
|
|
|
<a-tag v-if="!client.expiryTime || client.expiryTime <= 0" color="purple">∞</a-tag>
|
|
|
|
|
<a-tag v-else>{{ expiryLabel(client.expiryTime) }}</a-tag>
|
|
|
|
|
<span v-if="client.expiryTime > 0" class="hint">{{ expiryRelative(client.expiryTime) }}</span>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr>
|
i18n(clients): replace English fallbacks with proper translation keys
Pulls every hard-coded English label/title in the Clients page and its
four modals through the i18n layer so localized panels stop leaking
English. New keys live under pages.clients (auth, hysteriaAuth, uuid,
flow, flowNone, reverseTag, reverseTagPlaceholder, telegramId,
telegramIdPlaceholder, created, updated, ipLimit) plus refresh at the
root and toasts.bulkDeletedMixed / bulkCreatedMixed for partial-failure
toasts. Also switches the add-client modal's primary button from "Add"
to "Create" for consistency with other create flows.
The bulk-add Random/Random+Prefix/... email-method options stay
hard-coded by request - they're identifier-shaped strings.
2026-05-18 08:17:15 +00:00
|
|
|
<td>{{ t('pages.clients.ipLimit') }}</td>
|
2026-05-17 06:53:21 +00:00
|
|
|
<td>
|
|
|
|
|
<a-tag v-if="!client.limitIp">∞</a-tag>
|
|
|
|
|
<a-tag v-else>{{ client.limitIp }}</a-tag>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr>
|
i18n(clients): replace English fallbacks with proper translation keys
Pulls every hard-coded English label/title in the Clients page and its
four modals through the i18n layer so localized panels stop leaking
English. New keys live under pages.clients (auth, hysteriaAuth, uuid,
flow, flowNone, reverseTag, reverseTagPlaceholder, telegramId,
telegramIdPlaceholder, created, updated, ipLimit) plus refresh at the
root and toasts.bulkDeletedMixed / bulkCreatedMixed for partial-failure
toasts. Also switches the add-client modal's primary button from "Add"
to "Create" for consistency with other create flows.
The bulk-add Random/Random+Prefix/... email-method options stay
hard-coded by request - they're identifier-shaped strings.
2026-05-18 08:17:15 +00:00
|
|
|
<td>{{ t('pages.inbounds.createdAt') }}</td>
|
2026-05-17 06:53:21 +00:00
|
|
|
<td>
|
|
|
|
|
<a-tag>{{ dateLabel(client.createdAt) }}</a-tag>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr>
|
i18n(clients): replace English fallbacks with proper translation keys
Pulls every hard-coded English label/title in the Clients page and its
four modals through the i18n layer so localized panels stop leaking
English. New keys live under pages.clients (auth, hysteriaAuth, uuid,
flow, flowNone, reverseTag, reverseTagPlaceholder, telegramId,
telegramIdPlaceholder, created, updated, ipLimit) plus refresh at the
root and toasts.bulkDeletedMixed / bulkCreatedMixed for partial-failure
toasts. Also switches the add-client modal's primary button from "Add"
to "Create" for consistency with other create flows.
The bulk-add Random/Random+Prefix/... email-method options stay
hard-coded by request - they're identifier-shaped strings.
2026-05-18 08:17:15 +00:00
|
|
|
<td>{{ t('pages.inbounds.updatedAt') }}</td>
|
2026-05-17 06:53:21 +00:00
|
|
|
<td>
|
|
|
|
|
<a-tag>{{ dateLabel(client.updatedAt) }}</a-tag>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr v-if="client.comment">
|
i18n(clients): replace English fallbacks with proper translation keys
Pulls every hard-coded English label/title in the Clients page and its
four modals through the i18n layer so localized panels stop leaking
English. New keys live under pages.clients (auth, hysteriaAuth, uuid,
flow, flowNone, reverseTag, reverseTagPlaceholder, telegramId,
telegramIdPlaceholder, created, updated, ipLimit) plus refresh at the
root and toasts.bulkDeletedMixed / bulkCreatedMixed for partial-failure
toasts. Also switches the add-client modal's primary button from "Add"
to "Create" for consistency with other create flows.
The bulk-add Random/Random+Prefix/... email-method options stay
hard-coded by request - they're identifier-shaped strings.
2026-05-18 08:17:15 +00:00
|
|
|
<td>{{ t('pages.clients.comment') }}</td>
|
2026-05-17 06:53:21 +00:00
|
|
|
<td>
|
|
|
|
|
<a-tag class="info-large-tag">{{ client.comment }}</a-tag>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr>
|
i18n(clients): replace English fallbacks with proper translation keys
Pulls every hard-coded English label/title in the Clients page and its
four modals through the i18n layer so localized panels stop leaking
English. New keys live under pages.clients (auth, hysteriaAuth, uuid,
flow, flowNone, reverseTag, reverseTagPlaceholder, telegramId,
telegramIdPlaceholder, created, updated, ipLimit) plus refresh at the
root and toasts.bulkDeletedMixed / bulkCreatedMixed for partial-failure
toasts. Also switches the add-client modal's primary button from "Add"
to "Create" for consistency with other create flows.
The bulk-add Random/Random+Prefix/... email-method options stay
hard-coded by request - they're identifier-shaped strings.
2026-05-18 08:17:15 +00:00
|
|
|
<td>{{ t('pages.clients.attachedInbounds') }}</td>
|
2026-05-17 06:53:21 +00:00
|
|
|
<td>
|
|
|
|
|
<div class="chips">
|
|
|
|
|
<a-tag v-for="id in (client.inboundIds || [])" :key="id" color="blue">
|
|
|
|
|
<template v-if="inboundsById[id]">
|
|
|
|
|
{{ inboundsById[id].remark || `#${id}` }} ({{ inboundsById[id].protocol }}:{{ inboundsById[id].port }})
|
|
|
|
|
</template>
|
|
|
|
|
<template v-else>#{{ id }}</template>
|
|
|
|
|
</a-tag>
|
|
|
|
|
<span v-if="!client.inboundIds || client.inboundIds.length === 0" class="hint">—</span>
|
|
|
|
|
</div>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
<template v-if="links.length > 0">
|
i18n(clients): replace English fallbacks with proper translation keys
Pulls every hard-coded English label/title in the Clients page and its
four modals through the i18n layer so localized panels stop leaking
English. New keys live under pages.clients (auth, hysteriaAuth, uuid,
flow, flowNone, reverseTag, reverseTagPlaceholder, telegramId,
telegramIdPlaceholder, created, updated, ipLimit) plus refresh at the
root and toasts.bulkDeletedMixed / bulkCreatedMixed for partial-failure
toasts. Also switches the add-client modal's primary button from "Add"
to "Create" for consistency with other create flows.
The bulk-add Random/Random+Prefix/... email-method options stay
hard-coded by request - they're identifier-shaped strings.
2026-05-18 08:17:15 +00:00
|
|
|
<a-divider>{{ t('pages.inbounds.copyLink') }}</a-divider>
|
2026-05-17 06:53:21 +00:00
|
|
|
<div v-for="(link, idx) in links" :key="idx" class="link-panel">
|
|
|
|
|
<div class="link-panel-header">
|
i18n(clients): replace English fallbacks with proper translation keys
Pulls every hard-coded English label/title in the Clients page and its
four modals through the i18n layer so localized panels stop leaking
English. New keys live under pages.clients (auth, hysteriaAuth, uuid,
flow, flowNone, reverseTag, reverseTagPlaceholder, telegramId,
telegramIdPlaceholder, created, updated, ipLimit) plus refresh at the
root and toasts.bulkDeletedMixed / bulkCreatedMixed for partial-failure
toasts. Also switches the add-client modal's primary button from "Add"
to "Create" for consistency with other create flows.
The bulk-add Random/Random+Prefix/... email-method options stay
hard-coded by request - they're identifier-shaped strings.
2026-05-18 08:17:15 +00:00
|
|
|
<a-tag color="green">{{ `${t('pages.clients.link')} ${idx + 1}` }}</a-tag>
|
2026-05-17 06:53:21 +00:00
|
|
|
<a-tooltip :title="t('copy')">
|
|
|
|
|
<a-button size="small" @click="copyValue(link)">
|
|
|
|
|
<template #icon>
|
|
|
|
|
<CopyOutlined />
|
|
|
|
|
</template>
|
|
|
|
|
</a-button>
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
</div>
|
|
|
|
|
<code class="link-panel-text">{{ link }}</code>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<template v-if="showSubscription && subLink">
|
i18n(clients): replace English fallbacks with proper translation keys
Pulls every hard-coded English label/title in the Clients page and its
four modals through the i18n layer so localized panels stop leaking
English. New keys live under pages.clients (auth, hysteriaAuth, uuid,
flow, flowNone, reverseTag, reverseTagPlaceholder, telegramId,
telegramIdPlaceholder, created, updated, ipLimit) plus refresh at the
root and toasts.bulkDeletedMixed / bulkCreatedMixed for partial-failure
toasts. Also switches the add-client modal's primary button from "Add"
to "Create" for consistency with other create flows.
The bulk-add Random/Random+Prefix/... email-method options stay
hard-coded by request - they're identifier-shaped strings.
2026-05-18 08:17:15 +00:00
|
|
|
<a-divider>{{ t('subscription.title') }}</a-divider>
|
2026-05-17 06:53:21 +00:00
|
|
|
<div class="link-panel">
|
|
|
|
|
<div class="link-panel-header">
|
i18n(clients): replace English fallbacks with proper translation keys
Pulls every hard-coded English label/title in the Clients page and its
four modals through the i18n layer so localized panels stop leaking
English. New keys live under pages.clients (auth, hysteriaAuth, uuid,
flow, flowNone, reverseTag, reverseTagPlaceholder, telegramId,
telegramIdPlaceholder, created, updated, ipLimit) plus refresh at the
root and toasts.bulkDeletedMixed / bulkCreatedMixed for partial-failure
toasts. Also switches the add-client modal's primary button from "Add"
to "Create" for consistency with other create flows.
The bulk-add Random/Random+Prefix/... email-method options stay
hard-coded by request - they're identifier-shaped strings.
2026-05-18 08:17:15 +00:00
|
|
|
<a-tag color="green">{{ t('subscription.title') }}</a-tag>
|
2026-05-17 06:53:21 +00:00
|
|
|
<a-tooltip :title="t('copy')">
|
|
|
|
|
<a-button size="small" @click="copyValue(subLink)">
|
|
|
|
|
<template #icon>
|
|
|
|
|
<CopyOutlined />
|
|
|
|
|
</template>
|
|
|
|
|
</a-button>
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
</div>
|
|
|
|
|
<a :href="subLink" target="_blank" rel="noopener noreferrer" class="link-panel-anchor">{{ subLink }}</a>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div v-if="subJsonLink" class="link-panel">
|
|
|
|
|
<div class="link-panel-header">
|
|
|
|
|
<a-tag color="green">JSON</a-tag>
|
|
|
|
|
<a-tooltip :title="t('copy')">
|
|
|
|
|
<a-button size="small" @click="copyValue(subJsonLink)">
|
|
|
|
|
<template #icon>
|
|
|
|
|
<CopyOutlined />
|
|
|
|
|
</template>
|
|
|
|
|
</a-button>
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
</div>
|
|
|
|
|
<a :href="subJsonLink" target="_blank" rel="noopener noreferrer" class="link-panel-anchor">{{ subJsonLink }}</a>
|
feat(clients): add Reset Traffic, QR Code, Info actions + Online/Remaining columns
The Clients page table gains:
- Online column — green/grey tag driven by /panel/api/inbounds/onlines,
polled every 10s.
- Remaining column — bytes-remaining tag, coloured green/orange/red
against quota, purple infinity when unlimited.
- Action icons per row: QR, Info, Reset traffic, Edit, Delete.
ClientInfoModal shows the full client detail (uuid/password/auth,
traffic ↑/↓ + remaining + all-time, expiry absolute + relative,
attached inbounds chip list, online + last-online).
ClientQrModal fetches links for the client's subId via
/panel/api/inbounds/getSubLinks/:subId and renders each one through
the existing QrPanel component.
Reset Traffic confirms then calls the existing per-inbound endpoint
on the client's first attached inbound (the traffic row is keyed on
email globally, so any attached inbound resets the shared counter).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 05:49:12 +00:00
|
|
|
</div>
|
2026-05-17 06:53:21 +00:00
|
|
|
</template>
|
|
|
|
|
</template>
|
feat(clients): add Reset Traffic, QR Code, Info actions + Online/Remaining columns
The Clients page table gains:
- Online column — green/grey tag driven by /panel/api/inbounds/onlines,
polled every 10s.
- Remaining column — bytes-remaining tag, coloured green/orange/red
against quota, purple infinity when unlimited.
- Action icons per row: QR, Info, Reset traffic, Edit, Delete.
ClientInfoModal shows the full client detail (uuid/password/auth,
traffic ↑/↓ + remaining + all-time, expiry absolute + relative,
attached inbounds chip list, online + last-online).
ClientQrModal fetches links for the client's subId via
/panel/api/inbounds/getSubLinks/:subId and renders each one through
the existing QrPanel component.
Reset Traffic confirms then calls the existing per-inbound endpoint
on the client's first attached inbound (the traffic row is keyed on
email globally, so any attached inbound resets the shared counter).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 05:49:12 +00:00
|
|
|
</a-modal>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
2026-05-17 06:53:21 +00:00
|
|
|
.info-table {
|
|
|
|
|
width: 100%;
|
|
|
|
|
border-collapse: collapse;
|
feat(clients): add Reset Traffic, QR Code, Info actions + Online/Remaining columns
The Clients page table gains:
- Online column — green/grey tag driven by /panel/api/inbounds/onlines,
polled every 10s.
- Remaining column — bytes-remaining tag, coloured green/orange/red
against quota, purple infinity when unlimited.
- Action icons per row: QR, Info, Reset traffic, Edit, Delete.
ClientInfoModal shows the full client detail (uuid/password/auth,
traffic ↑/↓ + remaining + all-time, expiry absolute + relative,
attached inbounds chip list, online + last-online).
ClientQrModal fetches links for the client's subId via
/panel/api/inbounds/getSubLinks/:subId and renders each one through
the existing QrPanel component.
Reset Traffic confirms then calls the existing per-inbound endpoint
on the client's first attached inbound (the traffic row is keyed on
email globally, so any attached inbound resets the shared counter).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 05:49:12 +00:00
|
|
|
}
|
|
|
|
|
|
2026-05-17 06:53:21 +00:00
|
|
|
.info-table.block {
|
|
|
|
|
margin-bottom: 10px;
|
feat(clients): add Reset Traffic, QR Code, Info actions + Online/Remaining columns
The Clients page table gains:
- Online column — green/grey tag driven by /panel/api/inbounds/onlines,
polled every 10s.
- Remaining column — bytes-remaining tag, coloured green/orange/red
against quota, purple infinity when unlimited.
- Action icons per row: QR, Info, Reset traffic, Edit, Delete.
ClientInfoModal shows the full client detail (uuid/password/auth,
traffic ↑/↓ + remaining + all-time, expiry absolute + relative,
attached inbounds chip list, online + last-online).
ClientQrModal fetches links for the client's subId via
/panel/api/inbounds/getSubLinks/:subId and renders each one through
the existing QrPanel component.
Reset Traffic confirms then calls the existing per-inbound endpoint
on the client's first attached inbound (the traffic row is keyed on
email globally, so any attached inbound resets the shared counter).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 05:49:12 +00:00
|
|
|
}
|
|
|
|
|
|
2026-05-17 06:53:21 +00:00
|
|
|
.info-table td {
|
|
|
|
|
padding: 4px 8px;
|
|
|
|
|
vertical-align: top;
|
feat(clients): add Reset Traffic, QR Code, Info actions + Online/Remaining columns
The Clients page table gains:
- Online column — green/grey tag driven by /panel/api/inbounds/onlines,
polled every 10s.
- Remaining column — bytes-remaining tag, coloured green/orange/red
against quota, purple infinity when unlimited.
- Action icons per row: QR, Info, Reset traffic, Edit, Delete.
ClientInfoModal shows the full client detail (uuid/password/auth,
traffic ↑/↓ + remaining + all-time, expiry absolute + relative,
attached inbounds chip list, online + last-online).
ClientQrModal fetches links for the client's subId via
/panel/api/inbounds/getSubLinks/:subId and renders each one through
the existing QrPanel component.
Reset Traffic confirms then calls the existing per-inbound endpoint
on the client's first attached inbound (the traffic row is keyed on
email globally, so any attached inbound resets the shared counter).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 05:49:12 +00:00
|
|
|
}
|
|
|
|
|
|
2026-05-17 06:53:21 +00:00
|
|
|
.info-table td:first-child {
|
|
|
|
|
width: 140px;
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
opacity: 0.75;
|
|
|
|
|
white-space: nowrap;
|
feat(clients): add Reset Traffic, QR Code, Info actions + Online/Remaining columns
The Clients page table gains:
- Online column — green/grey tag driven by /panel/api/inbounds/onlines,
polled every 10s.
- Remaining column — bytes-remaining tag, coloured green/orange/red
against quota, purple infinity when unlimited.
- Action icons per row: QR, Info, Reset traffic, Edit, Delete.
ClientInfoModal shows the full client detail (uuid/password/auth,
traffic ↑/↓ + remaining + all-time, expiry absolute + relative,
attached inbounds chip list, online + last-online).
ClientQrModal fetches links for the client's subId via
/panel/api/inbounds/getSubLinks/:subId and renders each one through
the existing QrPanel component.
Reset Traffic confirms then calls the existing per-inbound endpoint
on the client's first attached inbound (the traffic row is keyed on
email globally, so any attached inbound resets the shared counter).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 05:49:12 +00:00
|
|
|
}
|
|
|
|
|
|
2026-05-17 06:53:21 +00:00
|
|
|
.info-large-tag {
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
display: inline-block;
|
feat(clients): add Reset Traffic, QR Code, Info actions + Online/Remaining columns
The Clients page table gains:
- Online column — green/grey tag driven by /panel/api/inbounds/onlines,
polled every 10s.
- Remaining column — bytes-remaining tag, coloured green/orange/red
against quota, purple infinity when unlimited.
- Action icons per row: QR, Info, Reset traffic, Edit, Delete.
ClientInfoModal shows the full client detail (uuid/password/auth,
traffic ↑/↓ + remaining + all-time, expiry absolute + relative,
attached inbounds chip list, online + last-online).
ClientQrModal fetches links for the client's subId via
/panel/api/inbounds/getSubLinks/:subId and renders each one through
the existing QrPanel component.
Reset Traffic confirms then calls the existing per-inbound endpoint
on the client's first attached inbound (the traffic row is keyed on
email globally, so any attached inbound resets the shared counter).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 05:49:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.hint {
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
opacity: 0.55;
|
2026-05-17 06:53:21 +00:00
|
|
|
margin-left: 6px;
|
feat(clients): add Reset Traffic, QR Code, Info actions + Online/Remaining columns
The Clients page table gains:
- Online column — green/grey tag driven by /panel/api/inbounds/onlines,
polled every 10s.
- Remaining column — bytes-remaining tag, coloured green/orange/red
against quota, purple infinity when unlimited.
- Action icons per row: QR, Info, Reset traffic, Edit, Delete.
ClientInfoModal shows the full client detail (uuid/password/auth,
traffic ↑/↓ + remaining + all-time, expiry absolute + relative,
attached inbounds chip list, online + last-online).
ClientQrModal fetches links for the client's subId via
/panel/api/inbounds/getSubLinks/:subId and renders each one through
the existing QrPanel component.
Reset Traffic confirms then calls the existing per-inbound endpoint
on the client's first attached inbound (the traffic row is keyed on
email globally, so any attached inbound resets the shared counter).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 05:49:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chips {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
}
|
2026-05-17 06:53:21 +00:00
|
|
|
|
|
|
|
|
.link-panel {
|
|
|
|
|
border: 1px solid rgba(128, 128, 128, 0.2);
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
padding: 10px;
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.link-panel-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.link-panel-text {
|
|
|
|
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
word-break: break-all;
|
|
|
|
|
white-space: pre-wrap;
|
|
|
|
|
padding: 6px 8px;
|
|
|
|
|
background: rgba(0, 0, 0, 0.04);
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
user-select: all;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:global(body.dark) .link-panel-text {
|
|
|
|
|
background: rgba(255, 255, 255, 0.05);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.link-panel-anchor {
|
|
|
|
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
word-break: break-all;
|
|
|
|
|
padding: 6px 8px;
|
|
|
|
|
background: rgba(0, 0, 0, 0.04);
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
color: var(--ant-color-primary, #1677ff);
|
|
|
|
|
text-decoration: underline;
|
|
|
|
|
text-decoration-color: rgba(22, 119, 255, 0.4);
|
|
|
|
|
transition: background 120ms ease, text-decoration-color 120ms ease;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.link-panel-anchor:hover {
|
|
|
|
|
background: rgba(22, 119, 255, 0.08);
|
|
|
|
|
text-decoration-color: var(--ant-color-primary, #1677ff);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:global(body.dark) .link-panel-anchor {
|
|
|
|
|
background: rgba(255, 255, 255, 0.05);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:global(body.dark) .link-panel-anchor:hover {
|
|
|
|
|
background: rgba(22, 119, 255, 0.16);
|
|
|
|
|
}
|
feat(clients): add Reset Traffic, QR Code, Info actions + Online/Remaining columns
The Clients page table gains:
- Online column — green/grey tag driven by /panel/api/inbounds/onlines,
polled every 10s.
- Remaining column — bytes-remaining tag, coloured green/orange/red
against quota, purple infinity when unlimited.
- Action icons per row: QR, Info, Reset traffic, Edit, Delete.
ClientInfoModal shows the full client detail (uuid/password/auth,
traffic ↑/↓ + remaining + all-time, expiry absolute + relative,
attached inbounds chip list, online + last-online).
ClientQrModal fetches links for the client's subId via
/panel/api/inbounds/getSubLinks/:subId and renders each one through
the existing QrPanel component.
Reset Traffic confirms then calls the existing per-inbound endpoint
on the client's first attached inbound (the traffic row is keyed on
email globally, so any attached inbound resets the shared counter).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 05:49:12 +00:00
|
|
|
</style>
|