diff --git a/frontend/src/pages/groups/GroupsPage.tsx b/frontend/src/pages/groups/GroupsPage.tsx
index 45f144c9a..e8ce06957 100644
--- a/frontend/src/pages/groups/GroupsPage.tsx
+++ b/frontend/src/pages/groups/GroupsPage.tsx
@@ -22,6 +22,8 @@ import {
} from 'antd';
import type { MenuProps, TableColumnsType } from 'antd';
import {
+ ArrowDownOutlined,
+ ArrowUpOutlined,
ClockCircleOutlined,
DeleteOutlined,
EditOutlined,
@@ -165,6 +167,14 @@ export default function GroupsPage() {
() => groups.reduce((acc, g) => acc + (g.trafficUsed || 0), 0),
[groups],
);
+ const totalUpload = useMemo(
+ () => groups.reduce((acc, g) => acc + (g.up || 0), 0),
+ [groups],
+ );
+ const totalDownload = useMemo(
+ () => groups.reduce((acc, g) => acc + (g.down || 0), 0),
+ [groups],
+ );
function openCreate() {
setCreateName('');
@@ -417,6 +427,20 @@ export default function GroupsPage() {
width: 180,
render: (count: number) => {count || 0},
},
+ {
+ title: t('pages.groups.upload'),
+ dataIndex: 'up',
+ key: 'up',
+ width: 140,
+ render: (bytes: number) => {SizeFormatter.sizeFormat(bytes || 0)},
+ },
+ {
+ title: t('pages.groups.download'),
+ dataIndex: 'down',
+ key: 'down',
+ width: 140,
+ render: (bytes: number) => {SizeFormatter.sizeFormat(bytes || 0)},
+ },
{
title: t('pages.groups.trafficUsed'),
dataIndex: 'trafficUsed',
@@ -456,26 +480,30 @@ export default function GroupsPage() {
-
+
}
/>
-
+
}
/>
-
+
}
/>
+
+ {SizeFormatter.sizeFormat(totalUpload)}
+ {SizeFormatter.sizeFormat(totalDownload)}
+
diff --git a/frontend/src/schemas/client.ts b/frontend/src/schemas/client.ts
index 3dc73949c..916812280 100644
--- a/frontend/src/schemas/client.ts
+++ b/frontend/src/schemas/client.ts
@@ -129,6 +129,8 @@ export const GroupSummarySchema = z.object({
name: z.string(),
clientCount: z.number(),
trafficUsed: z.number().nullable().transform((v) => v ?? 0),
+ up: z.number().nullable().transform((v) => v ?? 0),
+ down: z.number().nullable().transform((v) => v ?? 0),
});
export const GroupSummaryListSchema = z.array(GroupSummarySchema).nullable().transform((v) => v ?? []);
diff --git a/internal/web/service/client_groups.go b/internal/web/service/client_groups.go
index 493f6e5f3..243b7d98d 100644
--- a/internal/web/service/client_groups.go
+++ b/internal/web/service/client_groups.go
@@ -14,6 +14,8 @@ type GroupSummary struct {
Name string `json:"name"`
ClientCount int `json:"clientCount"`
TrafficUsed int64 `json:"trafficUsed"`
+ Up int64 `json:"up"`
+ Down int64 `json:"down"`
}
func (s *ClientService) ListGroups() ([]GroupSummary, error) {
@@ -22,7 +24,7 @@ func (s *ClientService) ListGroups() ([]GroupSummary, error) {
// never double-counts a client's traffic.
var derived []GroupSummary
if err := db.Table("clients AS c").
- Select("c.group_name AS name, COUNT(*) AS client_count, COALESCE(SUM(ct.up + ct.down), 0) AS traffic_used").
+ Select("c.group_name AS name, COUNT(*) AS client_count, COALESCE(SUM(ct.up + ct.down), 0) AS traffic_used, COALESCE(SUM(ct.up), 0) AS up, COALESCE(SUM(ct.down), 0) AS down").
Joins("LEFT JOIN client_traffics ct ON ct.email = c.email").
Where("c.group_name <> ''").
Group("c.group_name").
@@ -36,17 +38,19 @@ func (s *ClientService) ListGroups() ([]GroupSummary, error) {
type groupAgg struct {
count int
traffic int64
+ up int64
+ down int64
}
merged := make(map[string]groupAgg, len(derived)+len(stored))
for _, g := range stored {
merged[g.Name] = groupAgg{}
}
for _, g := range derived {
- merged[g.Name] = groupAgg{count: g.ClientCount, traffic: g.TrafficUsed}
+ merged[g.Name] = groupAgg{count: g.ClientCount, traffic: g.TrafficUsed, up: g.Up, down: g.Down}
}
out := make([]GroupSummary, 0, len(merged))
for name, agg := range merged {
- out = append(out, GroupSummary{Name: name, ClientCount: agg.count, TrafficUsed: agg.traffic})
+ out = append(out, GroupSummary{Name: name, ClientCount: agg.count, TrafficUsed: agg.traffic, Up: agg.up, Down: agg.down})
}
sort.Slice(out, func(i, j int) bool {
return strings.ToLower(out[i].Name) < strings.ToLower(out[j].Name)
diff --git a/internal/web/translation/ar-EG.json b/internal/web/translation/ar-EG.json
index 0361e55c6..2956a042a 100644
--- a/internal/web/translation/ar-EG.json
+++ b/internal/web/translation/ar-EG.json
@@ -812,10 +812,12 @@
"groups": {
"title": "المجموعات",
"name": "الاسم",
- "clientCount": "عملاء في المجموعة",
+ "clientCount": "العملاء",
"totalGroups": "إجمالي المجموعات",
"totalGroupedClients": "العملاء بمجموعة",
"trafficUsed": "حركة المرور المستخدمة",
+ "upload": "رفع",
+ "download": "تنزيل",
"totalTraffic": "إجمالي حركة المرور",
"addGroup": "إضافة مجموعة",
"createSuccess": "تم إنشاء المجموعة «{name}».",
diff --git a/internal/web/translation/en-US.json b/internal/web/translation/en-US.json
index 47a59fce7..c7dc19cd9 100644
--- a/internal/web/translation/en-US.json
+++ b/internal/web/translation/en-US.json
@@ -813,10 +813,12 @@
"groups": {
"title": "Groups",
"name": "Name",
- "clientCount": "Clients in group",
+ "clientCount": "Clients",
"totalGroups": "Total groups",
"totalGroupedClients": "Clients with a group",
"trafficUsed": "Traffic used",
+ "upload": "Upload",
+ "download": "Download",
"totalTraffic": "Total traffic",
"addGroup": "Add Group",
"createSuccess": "Group \"{name}\" created.",
diff --git a/internal/web/translation/es-ES.json b/internal/web/translation/es-ES.json
index 7c788d3ee..2226aaf12 100644
--- a/internal/web/translation/es-ES.json
+++ b/internal/web/translation/es-ES.json
@@ -812,10 +812,12 @@
"groups": {
"title": "Grupos",
"name": "Nombre",
- "clientCount": "Clientes en el grupo",
+ "clientCount": "Clientes",
"totalGroups": "Total de grupos",
"totalGroupedClients": "Clientes con grupo",
"trafficUsed": "Tráfico usado",
+ "upload": "Subida",
+ "download": "Bajada",
"totalTraffic": "Tráfico total",
"addGroup": "Añadir grupo",
"createSuccess": "Grupo «{name}» creado.",
diff --git a/internal/web/translation/fa-IR.json b/internal/web/translation/fa-IR.json
index 5c67bc3b2..1980cb79c 100644
--- a/internal/web/translation/fa-IR.json
+++ b/internal/web/translation/fa-IR.json
@@ -812,10 +812,12 @@
"groups": {
"title": "گروهها",
"name": "نام",
- "clientCount": "کاربران در گروه",
+ "clientCount": "کاربران",
"totalGroups": "تعداد گروهها",
"totalGroupedClients": "کاربران دارای گروه",
"trafficUsed": "ترافیک مصرفشده",
+ "upload": "آپلود",
+ "download": "دانلود",
"totalTraffic": "مجموع ترافیک",
"addGroup": "افزودن گروه",
"createSuccess": "گروه «{name}» ایجاد شد.",
diff --git a/internal/web/translation/id-ID.json b/internal/web/translation/id-ID.json
index 30e5464b2..ad3075bb2 100644
--- a/internal/web/translation/id-ID.json
+++ b/internal/web/translation/id-ID.json
@@ -812,10 +812,12 @@
"groups": {
"title": "Grup",
"name": "Nama",
- "clientCount": "Klien di grup",
+ "clientCount": "Klien",
"totalGroups": "Total grup",
"totalGroupedClients": "Klien dengan grup",
"trafficUsed": "Trafik terpakai",
+ "upload": "Unggah",
+ "download": "Unduh",
"totalTraffic": "Total trafik",
"addGroup": "Tambah grup",
"createSuccess": "Grup «{name}» dibuat.",
diff --git a/internal/web/translation/ja-JP.json b/internal/web/translation/ja-JP.json
index 414311de3..6d02d4ff7 100644
--- a/internal/web/translation/ja-JP.json
+++ b/internal/web/translation/ja-JP.json
@@ -812,10 +812,12 @@
"groups": {
"title": "グループ",
"name": "名前",
- "clientCount": "グループ内のクライアント",
+ "clientCount": "クライアント",
"totalGroups": "グループ合計",
"totalGroupedClients": "グループのあるクライアント",
"trafficUsed": "使用済みトラフィック",
+ "upload": "アップロード",
+ "download": "ダウンロード",
"totalTraffic": "合計トラフィック",
"addGroup": "グループ追加",
"createSuccess": "グループ「{name}」を作成しました。",
diff --git a/internal/web/translation/pt-BR.json b/internal/web/translation/pt-BR.json
index b4410b4f6..3567ad0bd 100644
--- a/internal/web/translation/pt-BR.json
+++ b/internal/web/translation/pt-BR.json
@@ -812,10 +812,12 @@
"groups": {
"title": "Grupos",
"name": "Nome",
- "clientCount": "Clientes no grupo",
+ "clientCount": "Clientes",
"totalGroups": "Total de grupos",
"totalGroupedClients": "Clientes com grupo",
"trafficUsed": "Tráfego usado",
+ "upload": "Envio",
+ "download": "Recebimento",
"totalTraffic": "Tráfego total",
"addGroup": "Adicionar grupo",
"createSuccess": "Grupo «{name}» criado.",
diff --git a/internal/web/translation/ru-RU.json b/internal/web/translation/ru-RU.json
index 88912957d..58f2b1c39 100644
--- a/internal/web/translation/ru-RU.json
+++ b/internal/web/translation/ru-RU.json
@@ -812,10 +812,12 @@
"groups": {
"title": "Группы",
"name": "Имя",
- "clientCount": "Клиентов в группе",
+ "clientCount": "Клиенты",
"totalGroups": "Всего групп",
"totalGroupedClients": "Клиенты с группой",
"trafficUsed": "Использованный трафик",
+ "upload": "Отправлено",
+ "download": "Получено",
"totalTraffic": "Общий трафик",
"addGroup": "Добавить группу",
"createSuccess": "Группа «{name}» создана.",
diff --git a/internal/web/translation/tr-TR.json b/internal/web/translation/tr-TR.json
index bba16fd32..b55107647 100644
--- a/internal/web/translation/tr-TR.json
+++ b/internal/web/translation/tr-TR.json
@@ -813,10 +813,12 @@
"groups": {
"title": "Gruplar",
"name": "İsim",
- "clientCount": "Gruptaki kullanıcılar",
+ "clientCount": "Kullanıcılar",
"totalGroups": "Toplam grup",
"totalGroupedClients": "Grubu olan kullanıcılar",
"trafficUsed": "Kullanılan trafik",
+ "upload": "Yükleme",
+ "download": "İndirme",
"totalTraffic": "Toplam trafik",
"addGroup": "Grup ekle",
"createSuccess": "«{name}» grubu oluşturuldu.",
diff --git a/internal/web/translation/uk-UA.json b/internal/web/translation/uk-UA.json
index 0bb3aca26..29a7fb06e 100644
--- a/internal/web/translation/uk-UA.json
+++ b/internal/web/translation/uk-UA.json
@@ -812,10 +812,12 @@
"groups": {
"title": "Групи",
"name": "Назва",
- "clientCount": "Клієнтів у групі",
+ "clientCount": "Клієнти",
"totalGroups": "Всього груп",
"totalGroupedClients": "Клієнти з групою",
"trafficUsed": "Використаний трафік",
+ "upload": "Вивантаження",
+ "download": "Завантаження",
"totalTraffic": "Загальний трафік",
"addGroup": "Додати групу",
"createSuccess": "Групу «{name}» створено.",
diff --git a/internal/web/translation/vi-VN.json b/internal/web/translation/vi-VN.json
index 82acf8dab..aaa16155f 100644
--- a/internal/web/translation/vi-VN.json
+++ b/internal/web/translation/vi-VN.json
@@ -812,10 +812,12 @@
"groups": {
"title": "Nhóm",
"name": "Tên",
- "clientCount": "Client trong nhóm",
+ "clientCount": "Client",
"totalGroups": "Tổng số nhóm",
"totalGroupedClients": "Client có nhóm",
"trafficUsed": "Lưu lượng đã dùng",
+ "upload": "Tải lên",
+ "download": "Tải xuống",
"totalTraffic": "Tổng lưu lượng",
"addGroup": "Thêm nhóm",
"createSuccess": "Đã tạo nhóm «{name}».",
diff --git a/internal/web/translation/zh-CN.json b/internal/web/translation/zh-CN.json
index a2d4a26e2..946678a0c 100644
--- a/internal/web/translation/zh-CN.json
+++ b/internal/web/translation/zh-CN.json
@@ -812,10 +812,12 @@
"groups": {
"title": "分组",
"name": "名称",
- "clientCount": "分组中的客户端",
+ "clientCount": "客户端",
"totalGroups": "分组总数",
"totalGroupedClients": "有分组的客户端",
"trafficUsed": "已用流量",
+ "upload": "上传",
+ "download": "下载",
"totalTraffic": "总流量",
"addGroup": "添加分组",
"createSuccess": "已创建分组 “{name}”。",
diff --git a/internal/web/translation/zh-TW.json b/internal/web/translation/zh-TW.json
index 7b94fb5ca..5485693cb 100644
--- a/internal/web/translation/zh-TW.json
+++ b/internal/web/translation/zh-TW.json
@@ -812,10 +812,12 @@
"groups": {
"title": "群組",
"name": "名稱",
- "clientCount": "群組中的客戶端",
+ "clientCount": "客戶端",
"totalGroups": "群組總數",
"totalGroupedClients": "有群組的客戶端",
"trafficUsed": "已用流量",
+ "upload": "上傳",
+ "download": "下載",
"totalTraffic": "總流量",
"addGroup": "新增群組",
"createSuccess": "已建立群組「{name}」。",