Skip to content

Commit 6084f05

Browse files
committed
fix(proxy-pool): refine controls and runtime metrics
1 parent deb0223 commit 6084f05

11 files changed

Lines changed: 337 additions & 126 deletions

File tree

cliproxyapi-pro-management/monitoring-locales.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,16 @@
4646
"failover_attempts": "Max failover attempts",
4747
"health_interval": "Health-check interval",
4848
"health_timeout": "Health-check timeout",
49+
"unit_seconds": "seconds",
50+
"unit_minutes": "minutes",
4951
"isolation_threshold": "Failures before isolation",
5052
"isolation_duration": "Isolation duration",
5153
"probe_target": "TCP probe target",
5254
"test_url": "Exit-IP test URL",
5355
"health_checks": "Background health checks",
5456
"fail_open": "Allow direct fallback (traffic leak risk)",
5557
"nodes": "Proxy nodes",
58+
"actions": "Actions",
5659
"nodes_hint": "Supports HTTP, HTTPS, SOCKS5, and SOCKS5H proxy URLs.",
5760
"add_node": "Add node",
5861
"batch_import": "Batch import",
@@ -2406,13 +2409,16 @@
24062409
"failover_attempts": "最大故障转移次数",
24072410
"health_interval": "健康检查间隔",
24082411
"health_timeout": "健康检查超时",
2412+
"unit_seconds": "",
2413+
"unit_minutes": "分钟",
24092414
"isolation_threshold": "连续失败隔离阈值",
24102415
"isolation_duration": "隔离时长",
24112416
"probe_target": "TCP 探测目标",
24122417
"test_url": "出口 IP 测试地址",
24132418
"health_checks": "后台健康检查",
24142419
"fail_open": "允许直连回退(存在流量泄漏风险)",
24152420
"nodes": "代理节点",
2421+
"actions": "操作",
24162422
"nodes_hint": "支持 HTTP、HTTPS、SOCKS5 和 SOCKS5H 代理地址。",
24172423
"add_node": "添加节点",
24182424
"batch_import": "批量导入",
@@ -3692,13 +3698,16 @@
36923698
"failover_attempts": "最大故障轉移次數",
36933699
"health_interval": "健康檢查間隔",
36943700
"health_timeout": "健康檢查逾時",
3701+
"unit_seconds": "",
3702+
"unit_minutes": "分鐘",
36953703
"isolation_threshold": "連續失敗隔離門檻",
36963704
"isolation_duration": "隔離時間",
36973705
"probe_target": "TCP 探測目標",
36983706
"test_url": "出口 IP 測試位址",
36993707
"health_checks": "背景健康檢查",
37003708
"fail_open": "允許直接連線回退(存在流量洩漏風險)",
37013709
"nodes": "代理節點",
3710+
"actions": "操作",
37023711
"nodes_hint": "支援 HTTP、HTTPS、SOCKS5 和 SOCKS5H 代理位址。",
37033712
"add_node": "新增節點",
37043713
"batch_import": "批次匯入",

cliproxyapi-pro-management/overlay/src/features/proxyPool/ProxyPool.module.scss

Lines changed: 67 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
}
88

99
.pageWithSave {
10-
padding-bottom: 88px;
10+
padding-bottom: calc(var(--proxy-pool-action-bar-height, 46px) + 38px);
1111
}
1212

1313
.header {
@@ -202,20 +202,38 @@
202202
}
203203

204204
.quickSettings {
205-
display: flex;
206-
align-items: center;
207-
gap: 18px;
208-
padding: 11px 14px;
205+
display: grid;
206+
grid-template-columns: minmax(220px, 320px) minmax(190px, 240px) minmax(0, 1fr);
207+
align-items: end;
208+
gap: 16px;
209+
padding: 13px 14px;
209210
border: 1px solid var(--border-color);
210211
border-radius: 12px;
211212
background: var(--bg-secondary);
212213
}
213-
.quickSettings > :first-child {
214-
width: 210px;
215-
margin: 0;
214+
.quickSettingField {
215+
display: grid;
216+
min-width: 0;
217+
gap: 6px;
216218
}
217-
.quickSettings label {
219+
.quickSettingLabel {
220+
color: var(--text-primary);
218221
font-size: 11px;
222+
font-weight: 600;
223+
line-height: 1.2;
224+
}
225+
.quickSelectTrigger {
226+
height: 34px !important;
227+
}
228+
.quickToggleControl {
229+
display: flex;
230+
height: 34px;
231+
align-items: center;
232+
gap: 8px;
233+
}
234+
.quickToggleControl > span {
235+
color: var(--text-secondary);
236+
font-size: 12px;
219237
}
220238
.settingsLink,
221239
.textButton {
@@ -229,6 +247,13 @@
229247
font-size: 12px;
230248
font-weight: 600;
231249
}
250+
.quickSettings .settingsLink {
251+
display: inline-flex;
252+
height: 34px;
253+
align-items: center;
254+
align-self: end;
255+
justify-self: end;
256+
}
232257

233258
.nodePanel,
234259
.settingsPanel,
@@ -329,6 +354,9 @@
329354
.desktopTable table {
330355
table-layout: fixed;
331356
}
357+
.desktopTable th {
358+
white-space: nowrap;
359+
}
332360
.desktopTable th:nth-child(1),
333361
.desktopTable td:nth-child(1) {
334362
width: 38px;
@@ -355,7 +383,7 @@
355383
}
356384
.desktopTable th:nth-child(7),
357385
.desktopTable td:nth-child(7) {
358-
width: 58px;
386+
width: 76px;
359387
}
360388
.desktopTable th:nth-child(8),
361389
.desktopTable td:nth-child(8) {
@@ -836,6 +864,31 @@
836864
grid-template-columns: repeat(3, minmax(0, 1fr));
837865
gap: 13px;
838866
}
867+
.durationControl {
868+
display: grid;
869+
grid-template-columns: minmax(0, 1fr) auto;
870+
}
871+
.durationControl input {
872+
min-width: 0;
873+
border-top-right-radius: 0;
874+
border-bottom-right-radius: 0;
875+
}
876+
.durationControl > span {
877+
display: inline-flex;
878+
min-width: 58px;
879+
align-items: center;
880+
justify-content: center;
881+
box-sizing: border-box;
882+
padding: 0 11px;
883+
border: 1px solid var(--border-color);
884+
border-left: 0;
885+
border-radius: 0 8px 8px 0;
886+
color: var(--text-secondary);
887+
background: var(--muted-bg, rgba(148, 163, 184, 0.1));
888+
font-size: 11px;
889+
font-weight: 600;
890+
white-space: nowrap;
891+
}
839892
.dangerZone {
840893
display: flex;
841894
align-items: center;
@@ -966,47 +1019,6 @@
9661019
margin-top: 12px;
9671020
}
9681021

969-
.saveBar {
970-
position: fixed;
971-
z-index: 50;
972-
right: max(24px, env(safe-area-inset-right));
973-
bottom: max(16px, env(safe-area-inset-bottom));
974-
left: calc(var(--sidebar-active-width, 216px) + 24px);
975-
display: flex;
976-
min-height: 58px;
977-
box-sizing: border-box;
978-
align-items: center;
979-
justify-content: space-between;
980-
gap: 14px;
981-
padding: 8px 11px 8px 14px;
982-
border: 1px solid color-mix(in srgb, var(--primary-color) 25%, var(--border-color));
983-
border-radius: 13px;
984-
background: color-mix(in srgb, var(--bg-secondary) 94%, transparent);
985-
box-shadow: 0 10px 32px rgba(15, 23, 42, 0.17);
986-
backdrop-filter: blur(16px);
987-
}
988-
.saveBar > div:first-child {
989-
min-width: 0;
990-
}
991-
.saveBar strong,
992-
.saveBar span {
993-
display: block;
994-
}
995-
.saveBar strong {
996-
font-size: 13px;
997-
}
998-
.saveBar span {
999-
overflow: hidden;
1000-
color: var(--text-secondary);
1001-
font-size: 11px;
1002-
text-overflow: ellipsis;
1003-
white-space: nowrap;
1004-
}
1005-
.saveBar > div:last-child {
1006-
display: flex;
1007-
gap: 7px;
1008-
}
1009-
10101022
.noticeCard {
10111023
padding: 24px;
10121024
}
@@ -1056,7 +1068,7 @@
10561068
padding: 0 0 18px;
10571069
}
10581070
.pageWithSave {
1059-
padding-bottom: 78px;
1071+
padding-bottom: calc(var(--proxy-pool-action-bar-height, 46px) + 28px);
10601072
}
10611073
.header {
10621074
align-items: flex-start;
@@ -1107,13 +1119,9 @@
11071119
white-space: nowrap;
11081120
}
11091121
.quickSettings {
1110-
display: grid;
1111-
grid-template-columns: minmax(0, 1fr) auto;
1122+
grid-template-columns: repeat(2, minmax(0, 1fr));
11121123
gap: 11px;
11131124
}
1114-
.quickSettings > :first-child {
1115-
width: auto;
1116-
}
11171125
.settingsLink {
11181126
grid-column: 1 / -1;
11191127
margin-left: 0;
@@ -1220,19 +1228,6 @@
12201228
.bypassPanel > div > div {
12211229
grid-template-columns: 1fr;
12221230
}
1223-
.saveBar {
1224-
right: 10px;
1225-
bottom: max(8px, env(safe-area-inset-bottom));
1226-
left: 10px;
1227-
min-height: 58px;
1228-
padding: 7px 8px 7px 11px;
1229-
}
1230-
.saveBar > div:first-child span {
1231-
display: none;
1232-
}
1233-
.saveBar > div:last-child > :first-child {
1234-
display: none;
1235-
}
12361231
}
12371232

12381233
@media (max-width: 480px) {
@@ -1267,7 +1262,8 @@
12671262
.quickSettings {
12681263
grid-template-columns: 1fr;
12691264
}
1270-
.quickSettings > :nth-child(2) {
1265+
.quickSettings .settingsLink {
1266+
grid-column: auto;
12711267
justify-self: start;
12721268
}
12731269
.nodeFilters {

cliproxyapi-pro-management/overlay/src/features/proxyPool/ProxyPoolNodeManager.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ export function ProxyPoolNodeManager({
269269
{t('proxy_pool.last_check', { defaultValue: 'Last check' })}
270270
</TableHead>
271271
<TableHead alignRight>
272-
{t('common.actions', { defaultValue: 'Actions' })}
272+
{t('proxy_pool.actions', { defaultValue: 'Actions' })}
273273
</TableHead>
274274
</TableRow>
275275
</TableHeader>
Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
import { useRef } from 'react';
12
import { createPortal } from 'react-dom';
23
import { useTranslation } from 'react-i18next';
3-
import { Button } from '@/components/ui/Button';
4-
import styles from './ProxyPool.module.scss';
4+
import { IconCheck, IconRefreshCw } from '@/components/ui/icons';
5+
import { useActionBarHeightVar } from '@/hooks/useActionBarHeightVar';
6+
import configStyles from '@/pages/ConfigPage.module.scss';
57

68
interface ProxyPoolSaveBarProps {
79
visible: boolean;
@@ -12,27 +14,41 @@ interface ProxyPoolSaveBarProps {
1214

1315
export function ProxyPoolSaveBar({ visible, saving, onDiscard, onSave }: ProxyPoolSaveBarProps) {
1416
const { t } = useTranslation();
17+
const actionBarRef = useRef<HTMLDivElement>(null);
18+
useActionBarHeightVar(actionBarRef, '--proxy-pool-action-bar-height', visible);
19+
1520
if (!visible) return null;
1621
const content = (
17-
<footer className={styles.saveBar}>
18-
<div>
19-
<strong>{t('proxy_pool.unsaved_changes', { defaultValue: 'Unsaved changes' })}</strong>
20-
<span>
21-
{t('proxy_pool.save_bar_hint', {
22-
defaultValue: 'Review and save to apply them to the running pool.',
23-
})}
24-
</span>
25-
</div>
26-
<div>
27-
<Button variant="ghost" size="sm" onClick={onDiscard} disabled={saving}>
28-
{t('proxy_pool.discard_changes', { defaultValue: 'Discard' })}
29-
</Button>
30-
<Button size="sm" onClick={onSave} loading={saving}>
31-
{t('common.save')}
32-
</Button>
22+
<div className={configStyles.floatingActionContainer} ref={actionBarRef}>
23+
<div className={configStyles.floatingActionList}>
24+
<div className={`${configStyles.floatingStatus} ${configStyles.modified}`}>
25+
{saving
26+
? t('config_management.status_saving_short', { defaultValue: 'Saving' })
27+
: t('config_management.status_dirty_short', { defaultValue: 'Unsaved' })}
28+
</div>
29+
<button
30+
type="button"
31+
className={configStyles.floatingActionButton}
32+
onClick={onDiscard}
33+
disabled={saving}
34+
title={t('proxy_pool.discard_changes', { defaultValue: 'Discard changes' })}
35+
aria-label={t('proxy_pool.discard_changes', { defaultValue: 'Discard changes' })}
36+
>
37+
<IconRefreshCw size={16} />
38+
</button>
39+
<button
40+
type="button"
41+
className={configStyles.floatingActionButton}
42+
onClick={onSave}
43+
disabled={saving}
44+
title={t('common.save')}
45+
aria-label={t('common.save')}
46+
>
47+
<IconCheck size={16} />
48+
{!saving && <span className={configStyles.dirtyDot} aria-hidden="true" />}
49+
</button>
3350
</div>
34-
</footer>
51+
</div>
3552
);
36-
const target = typeof document !== 'undefined' ? document.querySelector('.main-body') : null;
37-
return target ? createPortal(content, target) : content;
53+
return typeof document === 'undefined' ? content : createPortal(content, document.body);
3854
}

0 commit comments

Comments
 (0)